Java'da iki dizi kümesini birleştirmenin daha iyi bir yolu var mı?


91

Gereksiz bilgileri filtrelerken iki dizi kümesini birleştirmem gerekiyor, bulduğum çözüm bu, herkesin önerebileceği daha iyi bir yol var mı? Belki de gözden kaçırdığım yerleşik bir şey? Google ile hiç şansım olmadı.

Set<String> oldStringSet = getOldStringSet();
Set<String> newStringSet = getNewStringSet();

for(String currentString : oldStringSet)
{
    if (!newStringSet.contains(currentString))
    {
        newStringSet.add(currentString);
    }
}

Yanıtlar:


117

A Setyinelenen girişler içermediğinden, ikisini şu şekilde birleştirebilirsiniz:

newStringSet.addAll(oldStringSet);

Bir şeyleri iki kez eklemenizin bir önemi yoktur, küme yalnızca bir kez öğeyi içerecektir ... örneğin, containsyöntemi kullanarak kontrol etmeye gerek yoktur .


92

Bu tek astarı kullanarak yapabilirsiniz

Set<String> combined = Stream.concat(newStringSet.stream(), oldStringSet.stream())
        .collect(Collectors.toSet());

Statik ithalatla daha da güzel görünüyor

Set<String> combined = concat(newStringSet.stream(), oldStringSet.stream())
        .collect(toSet());

Başka bir yol da flatMap yöntemini kullanmaktır :

Set<String> combined = Stream.of(newStringSet, oldStringSet).flatMap(Set::stream)
        .collect(toSet());

Ayrıca herhangi bir koleksiyon tek bir elementle kolayca birleştirilebilir

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());

bu nasıl addAll'dan daha iyi?
KKlalala

7
@KKlalala, hangisinin daha iyi olduğunu gereksinimleriniz belirleyecek. addAllAkışları kullanma ve kullanma arasındaki temel fark şudur: • kullanmak set1.addAll(set2), içeriklerini fiziksel olarak değiştirmenin yan etkisine sahiptir set1. • Ancak, Akışların kullanılması her zaman Setorijinal Küme örneklerinden herhangi birini değiştirmeden her iki kümenin içeriğini içeren yeni bir örnekle sonuçlanır . IMHO bu cevap daha iyidir, çünkü orijinal içeriği beklerken başka bir yerde kullanılacaksa, orijinal sette yan etkilerden ve beklenmedik değişiklikler olasılığından kaçınır. HTH
edwardsmatt

1
Bu aynı zamanda Değişmez Setleri destekleme avantajına da sahiptir. Bakınız: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt

34

Guava ile aynı :

Set<String> combinedSet = Sets.union(oldStringSet, newStringSet)

2
Sets :: union, Collectors.reducing () ile kullanmak için harika bir BinaryOperator'dur.
mskfisher

12

Set tanımına göre yalnızca benzersiz öğeler içerir.

Set<String> distinct = new HashSet<String>(); 
 distinct.addAll(oldStringSet);
 distinct.addAll(newStringSet);

Kodunuzu geliştirmek için bunun için genel bir yöntem oluşturabilirsiniz

public static <T> Set<T> distinct(Collection<T>... lists) {
    Set<T> distinct = new HashSet<T>();

    for(Collection<T> list : lists) {
        distinct.addAll(list);
    }
    return distinct;
}

7

Guava kullanıyorsanız, daha fazla esneklik elde etmek için bir oluşturucu da kullanabilirsiniz:

ImmutableSet.<String>builder().addAll(someSet)
                              .addAll(anotherSet)
                              .add("A single string")
                              .build();

4

Sadece kullan newStringSet.addAll(oldStringSet). SetUygulama zaten bunu yaptığı için kopyaları kontrol etmeye gerek yoktur.



3
 newStringSet.addAll(oldStringSet);

Bu s1 ve s2 Birliği üretecek


2

Kullan boolean addAll(Collection<? extends E> c)
Belirtilen koleksiyondaki tüm öğeleri, mevcut değilse bu kümeye ekler (isteğe bağlı işlem). Belirtilen koleksiyon da bir küme ise, addAll işlemi bu kümeyi, değeri iki kümenin birleşimi olacak şekilde etkili bir şekilde değiştirir. İşlem devam ederken belirtilen koleksiyon değiştirilirse, bu işlemin davranışı tanımsızdır.

newStringSet.addAll(oldStringSet)

2

Performansı önemsiyorsanız ve iki setinizi saklamanız gerekmiyorsa ve bunlardan biri çok büyükse, hangi setin en büyük olduğunu kontrol etmenizi ve en küçükten öğeleri eklemenizi öneririm.

Set<String> newStringSet = getNewStringSet();
Set<String> oldStringSet = getOldStringSet();

Set<String> myResult;
if(oldStringSet.size() > newStringSet.size()){
    oldStringSet.addAll(newStringSet);
    myResult = oldStringSet;
} else{
    newStringSet.addAll(oldStringSet);
    myResult = newStringSet;
}

Bu şekilde yeni setiniz 10 elemente sahipse ve eski setiniz 100000'e sahipse, 100000 yerine sadece 10 işlem yaparsınız.


Bu çok iyi bir mantık, bunun neden ana addAll metod parametresinde olmadığını hayal edemiyorum, meselapublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar

Sanırım spesifikasyonun kendisinden dolayı: Belirtilen koleksiyondaki tüm öğeleri bu koleksiyona ekler . Gerçekten başka bir yönteme sahip olabilirsiniz, ancak aşırı yüklediği yöntemlerle aynı spesifikasyona uymazsa oldukça kafa karıştırıcı olur.
Ricola

Evet, onu aşırı yükleyen başka bir yöntemi söylüyordum
Gaspar

2

Apache Common kullanıyorsanız, şu SetUtilssınıftan kullanın :org.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

Bunun SetViewdeğişmez olan a döndürdüğüne dikkat edin .
jaco0646

Ayrıca a'dan boyut () elde etmek SetViewher zaman doğrusal bir işlemdir.
Jugbot

2
Set.addAll()

Önceden mevcut değilse, belirtilen koleksiyondaki tüm öğeleri bu kümeye ekler (isteğe bağlı işlem). Belirtilen koleksiyon da bir küme ise, addAll işlemi bu kümeyi etkili bir şekilde değiştirir, böylece değeri iki kümenin birleşimi olur

newStringSet.addAll(oldStringSet)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.