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

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.


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());

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)

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

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();

4

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



3
 newStringSet.addAll(oldStringSet);

Isso produzirá União de s1 e s2


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)

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.


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

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.