Есть ли лучший способ объединить два набора строк в Java?

90

Мне нужно объединить два набора строк при фильтрации избыточной информации, это решение, которое я придумал, есть ли лучший способ, который можно предложить? Возможно, что-то встроенное в то, что я не заметил? Не повезло с гуглом.

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

for(String currentString : oldStringSet)
{
    if (!newStringSet.contains(currentString))
    {
        newStringSet.add(currentString);
    }
}
FooBar
источник

Ответы:

116

Поскольку a Setне содержит повторяющихся записей, вы можете объединить их:

newStringSet.addAll(oldStringSet);

Не имеет значения, если вы добавите элементы дважды, набор будет содержать элемент только один раз ... например, нет необходимости проверять с помощью containsметода.

Dacwe
источник
88

Вы можете сделать это с помощью этого однострочного

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

Со статическим импортом выглядит еще лучше

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

Другой способ - использовать метод flatMap :

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

Также любую коллекцию можно легко объединить с одним элементом

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());
ytterrr
источник
чем это лучше чем addAll?
KKlalala 01
7
@KKlalala, ваши требования определят, что лучше. Основное различие между addAllиспользованием Streams заключается в следующем: • использование set1.addAll(set2)будет иметь побочный эффект физического изменения содержимого set1. • Однако использование Streams всегда будет приводить к созданию нового экземпляра, Setсодержащего содержимое обоих наборов, без изменения любого из исходных экземпляров Set. IMHO этот ответ лучше, потому что он позволяет избежать побочных эффектов и возможности неожиданных изменений в исходном наборе, если он будет использоваться в другом месте, ожидая исходного содержимого. HTH
edwardsmatt
1
Это также имеет преимущество поддержки неизменяемых наборов. См .: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt
34

То же и с Гуавой :

Set<String> combinedSet = Sets.union(oldStringSet, newStringSet)
проактивный-е
источник
2
Sets :: union - отличный BinaryOperator для использования с Collectors.reeding ().
mskfisher 06
12

Из определения Set содержат только уникальные элементы.

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

Чтобы улучшить свой код, вы можете создать общий метод для этого

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

Если вы используете Guava, вы также можете использовать конструктор для большей гибкости:

ImmutableSet.<String>builder().addAll(someSet)
                              .addAll(anotherSet)
                              .add("A single string")
                              .build();
Леонардо Бернардес
источник
4

Просто используйте newStringSet.addAll(oldStringSet). Нет необходимости проверять дубликаты, поскольку Setреализация это уже делает.

Tobiasbayer
источник
3
 newStringSet.addAll(oldStringSet);

Это произведет объединение s1 и s2

Кушан
источник
2

Использовать boolean addAll(Collection<? extends E> c)
Добавляет все элементы в указанной коллекции в этот набор, если они еще не присутствуют (необязательная операция). Если указанная коллекция также является набором, операция addAll эффективно изменяет этот набор, так что его значение является объединением двух наборов. Поведение этой операции не определено, если указанная коллекция изменяется во время выполнения операции.

newStringSet.addAll(oldStringSet)
Сумит Сингх
источник
2

Если вы заботитесь о производительности и вам не нужно хранить два набора, и один из них может быть огромным, я бы посоветовал проверить, какой набор самый большой, и добавить элементы из самого маленького.

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

Таким образом, если в вашем новом наборе 10 элементов, а в старом - 100 000, вы выполняете только 10 операций вместо 100 000.

Ricola
источник
Это очень хорошая логика , что я не могу понять , почему это не в основном метод addAll parametter, какpublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar
Я предполагаю, что из-за самой спецификации: добавляет все элементы из указанной коллекции в эту коллекцию . У вас действительно может быть другой метод, но было бы довольно запутанно, если бы он не соответствовал той же спецификации, что и методы, которые он перегружает.
Ricola
Да, я говорил, что другой метод перегрузил этот
Гаспар
2

Если вы используете Apache Common, используйте SetUtilsкласс изorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);
Винит Соланки
источник
Обратите внимание, что это возвращает SetViewнеизменяемый.
jaco0646,
2
Set.addAll()

Добавляет все элементы в указанной коллекции в этот набор, если они еще не присутствуют (необязательная операция). Если указанная коллекция также является набором, операция addAll эффективно изменяет этот набор, так что его значение является объединением двух наборов

newStringSet.addAll(oldStringSet)
UmNyobe
источник