Existe uma maneira melhor de combinar dois conjuntos de strings em java?

91

Preciso combinar dois conjuntos de strings ao filtrar as informações redundantes. Essa é a solução que encontrei. Existe uma maneira melhor que alguém possa sugerir? Talvez algo embutido que eu esqueci? Não tive sorte com o google.

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

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

Respostas:

117

Como a Setnão contém entradas duplicadas, você pode combinar os dois:

newStringSet.addAll(oldStringSet);

Não importa se você adiciona coisas duas vezes, o conjunto conterá o elemento apenas uma vez ... por exemplo, não é necessário verificar usando o containsmétodo.

Dacwe
fonte
92

Você pode fazer isso usando este one-liner

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

Com uma importação estática, fica ainda melhor

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

Outra maneira é usar o método flatMap :

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

Além disso, qualquer coleção pode ser facilmente combinada com um único elemento

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());
ytterrr
fonte
como isso é melhor do que addAll?
KKlalala 01 de
7
@KKlalala, seus requisitos determinarão qual é o melhor. A principal diferença entre addAlle usando Streams é: • o uso set1.addAll(set2)terá o efeito colateral de alterar fisicamente o conteúdo de set1. • No entanto, o uso de Streams sempre resultará em uma nova instância Setcontendo o conteúdo de ambos os conjuntos sem modificar nenhuma das instâncias Set originais. IMHO esta resposta é melhor porque evita efeitos colaterais e potencial para mudanças inesperadas no conjunto original se fosse usado em outro lugar enquanto esperava o conteúdo original. HTH
edwardsmatt
1
Isso também tem a vantagem de oferecer suporte a conjuntos imutáveis. Veja: docs.oracle.com/javase/8/docs/api/java/util/...
edwardsmatt
34

O mesmo com a goiaba :

Set<String> combinedSet = Sets.union(oldStringSet, newStringSet)
proativo-e
fonte
2
Sets :: union é um ótimo BinaryOperator para usar com Collectors.reducing ().
mskfisher
12

Da definição, o conjunto contém apenas elementos únicos.

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

Para aprimorar seu código, você pode criar um método genérico para que

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;
}
Damian Leszczyński - Vash
fonte
7

Se você estiver usando Guava, também pode usar um construtor para obter mais flexibilidade:

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

Basta usar newStringSet.addAll(oldStringSet). Não há necessidade de verificar se há duplicatas, pois a Setimplementação já faz isso.

Tobiasbayer
fonte
3
 newStringSet.addAll(oldStringSet);

Isso produzirá União de s1 e s2

Kushan
fonte
2

Use boolean addAll(Collection<? extends E> c)
Adiciona todos os elementos da coleção especificada a este conjunto se eles ainda não estiverem presentes (operação opcional). Se a coleção especificada também for um conjunto, a operação addAll modifica efetivamente esse conjunto para que seu valor seja a união dos dois conjuntos. O comportamento desta operação é indefinido se a coleção especificada for modificada enquanto a operação estiver em andamento.

newStringSet.addAll(oldStringSet)
Sumit Singh
fonte
2

Se você se preocupa com o desempenho e não precisa manter seus dois conjuntos e um deles pode ser enorme, sugiro verificar qual conjunto é o maior e adicionar os elementos dos menores.

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;
}

Dessa forma, se o seu novo conjunto tiver 10 elementos e o antigo tiver 100.000, você só fará 10 operações em vez de 100.000.

Ricola
fonte
Essa é uma lógica muito boa que não consigo imaginar por que não está no parâmetro principal do método addAll, comopublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar
Acho que por causa da própria especificação: Adiciona todos os elementos da coleção especificada a esta coleção . Você poderia realmente ter outro método, mas seria bastante confuso se ele não seguisse a mesma especificação dos métodos que sobrecarrega.
Ricola
Sim, eu estava dizendo outro método sobrecarregando aquele
Gaspar
2

Se você estiver usando o Apache Common, use a SetUtilsclasse deorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);
Vinit Solanki
fonte
Observe que isso retorna um SetView, que é imutável.
jaco0646
Além disso, obter size () de a SetViewé sempre uma operação linear.
Jugbot
2
Set.addAll()

Adiciona todos os elementos da coleção especificada a este conjunto, se ainda não estiverem presentes (operação opcional). Se a coleção especificada também for um conjunto, a operação addAll modifica efetivamente esse conjunto para que seu valor seja a união dos dois conjuntos

newStringSet.addAll(oldStringSet)
UmNyobe
fonte