¿Hay una mejor manera de combinar dos conjuntos de cadenas en java?


90

Necesito combinar dos conjuntos de cadenas mientras filtro información redundante, esta es la solución que se me ocurrió, ¿hay una manera mejor que alguien pueda sugerir? ¿Quizás algo incorporado que pasé por alto? No tuve suerte con Google.

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

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

Respuestas:


116

Dado Setque a no contiene entradas duplicadas, puede combinar las dos mediante:

newStringSet.addAll(oldStringSet);

No importa si agrega cosas dos veces, el conjunto solo contendrá el elemento una vez ... por ejemplo, no es necesario verificar usando el containsmétodo.


88

Puedes hacerlo usando este one-liner

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

Con una importación estática, se ve aún mejor

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

Otra forma es usar el método flatMap :

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

Además, cualquier colección se puede combinar fácilmente con un solo elemento.

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

¿cómo es esto mejor que addAll?
KKlalala

7
@KKlalala, sus requisitos determinarán cuál es mejor. La principal diferencia entre addAlly usar Streams es: • usar set1.addAll(set2)tendrá el efecto secundario de cambiar físicamente el contenido de set1. • Sin embargo, el uso de Streams siempre resultará en una nueva instancia de Setcontener el contenido de ambos conjuntos sin modificar ninguna de las instancias de Set originales. En mi humilde opinión, esta respuesta es mejor porque evita los efectos secundarios y la posibilidad de cambios inesperados en el conjunto original si se usara en otro lugar mientras se espera el contenido original. HTH
edwardsmatt

1
Esto también tiene la ventaja de admitir conjuntos inmutables. Ver: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt

34

Lo mismo con Guayaba :

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

2
Sets :: union es un gran BinaryOperator para usar con Collectors.reducing ().
mskfisher

12

De la definición, el conjunto contiene solo elementos únicos.

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

Para mejorar su código, puede crear un método genérico para eso

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

6

Si está usando Guava, también puede usar un constructor para obtener más flexibilidad:

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

4

Solo usa newStringSet.addAll(oldStringSet). No es necesario verificar si hay duplicados, ya que la Setimplementación ya lo hace.



3
 newStringSet.addAll(oldStringSet);

Esto producirá unión de s1 y s2


2

Usar boolean addAll(Collection<? extends E> c)
Agrega todos los elementos de la colección especificada a este conjunto si aún no están presentes (operación opcional). Si la colección especificada también es un conjunto, la operación addAll modifica efectivamente este conjunto para que su valor sea la unión de los dos conjuntos. El comportamiento de esta operación no está definido si la colección especificada se modifica mientras la operación está en curso.

newStringSet.addAll(oldStringSet)

2

Si le importa el rendimiento, y si no necesita conservar sus dos conjuntos y uno de ellos puede ser enorme, le sugiero que compruebe cuál es el conjunto más grande y agregue los elementos del más pequeño.

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

De esta manera, si su nuevo conjunto tiene 10 elementos y su conjunto anterior tiene 100 000, solo realiza 10 operaciones en lugar de 100 000.


Esta es una muy buena lógica que no puedo imaginar por qué esto no está en el parámetro principal del método addAll, comopublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar

Supongo que debido a la especificación en sí: agrega todos los elementos de la colección especificada a esta colección . De hecho, podría tener otro método, pero sería bastante confuso si no sigue la misma especificación que los métodos que sobrecarga.
Ricola

Sí, estaba diciendo que otro método sobrecarga ese
Gaspar

2

Si está utilizando Apache Common, utilice la SetUtilsclase deorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

Tenga en cuenta que esto devuelve a SetView, que es inmutable.
jaco0646

2
Set.addAll()

Agrega todos los elementos de la colección especificada a este conjunto si aún no están presentes (operación opcional). Si la colección especificada también es un conjunto, la operación addAll modifica efectivamente este conjunto para que su valor sea la unión de los dos conjuntos

newStringSet.addAll(oldStringSet)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.