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


90

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

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

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

Ответы:


116

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

newStringSet.addAll(oldStringSet);

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


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

чем это лучше чем 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


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реализация это уже делает.




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.


Это очень хорошая логика , что я не могу понять , почему это не в основном метод 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.