Чи є кращий спосіб поєднати два набори рядків у Java?


90

Мені потрібно поєднати два набори рядків, фільтруючи надлишкову інформацію, це рішення, яке я придумав, чи є кращий спосіб, який хтось може запропонувати? Можливо, щось вбудоване, що я пропустив? Не пощастило з Google.

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

7
@KKlalala, ваші вимоги визначать, що краще. Основна відмінність між addAllпотоками та їх використанням є: • використання 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, наприкладpublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Гаспар

Я думаю, через саму специфікацію: додає всі елементи до вказаної колекції до цієї колекції . Ви могли б мати інший метод, але було б досить заплутано, якщо він не відповідає тій же специфікації, ніж методи, які він перевантажує.
Ricola

Так, я говорив, що інший метод перевантажує той
Гаспар,

2

Якщо ви використовуєте Apache Common, використовуйте SetUtilsклас зorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

Зверніть увагу, що це повертає a SetView, яке є незмінним.
jaco0646

2
Set.addAll()

Додає до цього набору всі елементи у вказаній колекції, якщо вони ще не присутні (необов’язкова операція). Якщо вказана колекція також є набором, операція addAll ефективно модифікує цей набір так, що його значення є об'єднанням двох наборів

newStringSet.addAll(oldStringSet)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.