Статьи

Общие и уникальные элементы в нескольких коллекциях

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

Итак, давайте приступим к этому. Иногда вы обнаружите, что вам необходимо определить, какие элементы в одной коллекции существуют в другой, какие являются общими и / или которые не существуют в другой коллекции. В коллекциях Apache Commons есть несколько полезных методов в CollectionUtils , которые полезны, в частности, intersection (), но этот пост выходит за рамки вычисления уникальных элементов в коллекции коллекций, и всегда приятно перейти к деталям. Мы также сделаем решение более общим, поддерживая любое количество коллекций, а не только две коллекции, как это делает CollectionUtils . Кроме того, есть факт, что не все из нас выбирают или могут включать библиотеки только для того, чтобы получить пару полезных служебных методов.

Когда речь идет только о двух коллекциях, это не сложная проблема, но не все разработчики знакомы со всеми методами, которые определяет java.util.Collection , поэтому вот пример кода. Они используют вместе методы retainAll и removeAll для создания трех наборов — общего, присутствующего только в коллекции A, и присутствующего только в B.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
Set<String> a = new HashSet<String>();
a.add("a");
a.add("a2");
a.add("common");
 
Set<String> b = new HashSet<String>();
b.add("b");
b.add("b2");
b.add("common");
 
Set<String> inAOnly = new HashSet<String>(a);
inAOnly.removeAll(b);
System.out.println("A Only: " + inAOnly );
 
Set<String> inBOnly = new HashSet<String>(b);
inBOnly .removeAll(a);
System.out.println("B Only: " + inBOnly );
 
Set<String> common = new HashSet<String>(a);
common.retainAll(b);
System.out.println("Common: " + common);

Выход:

1
2
3
A Only: [a, a2]
B Only: [b, b2]
Common: [common1]

Обработка трех или более коллекций
Эта проблема немного сложнее при работе с более чем двумя коллекциями, но она может быть решена довольно просто, как показано ниже:

Вычисление общих элементов
Вычислить общие элементы легко, и этот код будет работать согласованно даже с большим количеством коллекций.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static void main(String[] args) {
   List<String> a = Arrays.asList("a", "b", "c");
   List<String> b = Arrays.asList("a", "b", "c", "d");
   List<String> c = Arrays.asList("d", "e", "f", "g");
 
   List<List<String>> lists = new ArrayList<List<String>>();
   lists.add(a);
   System.out.println("Common in A: " + getCommonElements(lists));
 
   lists.add(b);
   System.out.println("Common in A & B: " + getCommonElements(lists));
 
   lists.add(c);
   System.out.println("Common in A & B & C: " + getCommonElements(lists));
 
   lists.remove(a);
   System.out.println("Common in B & C: " + getCommonElements(lists));
}
 
public static <T> Set<T> getCommonElements(Collection<? extends Collection<T>> collections) {
 
    Set<T> common = new LinkedHashSet<T>();
    if (!collections.isEmpty()) {
       Iterator<? extends Collection<T>> iterator = collections.iterator();
       common.addAll(iterator.next());
       while (iterator.hasNext()) {
          common.retainAll(iterator.next());
       }
    }
    return common;
}

Выход:

1
2
3
4
Common in A: [a, b, c]
Common in A & B: [a, b, c]
Common in A & B & C: []
Common in B & C: [d]

Вычисление уникальных элементов
Вычисление уникальных элементов примерно так же просто, как вычисление общих элементов. Обратите внимание, что производительность этого кода будет ухудшаться при добавлении большого количества коллекций, хотя в большинстве случаев это не имеет значения. Я предполагаю, что есть способы, которыми это можно было бы оптимизировать, но, поскольку у меня не было проблемы, я не потрудился попробовать. Как сказал Кнут, как известно, «мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация — корень всех зол».

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static void main(String[] args) {
   List<String> a = Arrays.asList("a", "b", "c");
   List<String> b = Arrays.asList("a", "b", "c", "d");
   List<String> c = Arrays.asList("d", "e", "f", "g");
 
   List<List<String>> lists = new ArrayList<List<String>>();
   lists.add(a);
   System.out.println("Unique in A: " + getUniqueElements(lists));
 
   lists.add(b);
   System.out.println("Unique in A & B: " + getUniqueElements(lists));
 
   lists.add(c);
   System.out.println("Unique in A & B & C: " + getUniqueElements(lists));
 
   lists.remove(a);
   System.out.println("Unique in B & C: " + getUniqueElements(lists));
}
 
public static <T> List<Set<T>> getUniqueElements(Collection<? extends Collection<T>> collections) {
 
    List<Set<T>> allUniqueSets = new ArrayList<Set<T>>();
    for (Collection<T> collection : collections) {
       Set<T> unique = new LinkedHashSet<T>(collection);
       allUniqueSets.add(unique);
       for (Collection<T> otherCollection : collections) {
          if (collection != otherCollection) {
             unique.removeAll(otherCollection);
          }
       }
   }
 
    return allUniqueSets;
}

Выход:

1
2
3
4
Unique in A: [[a, b, c]]
Unique in A & B: [[], [d]]
Unique in A & B & C: [[], [], [e, f, g]]
Unique in B & C: [[a, b, c], [e, f, g]]

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

Справка: Вычисление общих и уникальных элементов в нескольких коллекциях — Java & от нашего партнера JCG