Перейти к основному содержимому

Коллекции Apache Commons SetUtils

· 5 мин. чтения

1. Обзор

В этой статье мы рассмотрим API SetUtils библиотеки Apache Commons Collections. Проще говоря, эти утилиты можно использовать для выполнения определенных операций над структурами данных Set в Java.

2. Установка зависимостей

Чтобы мы могли использовать библиотеку SetUtils в нашем проекте, нам нужно добавить следующую зависимость в файл pom.xml нашего проекта :

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>

В качестве альтернативы, если наш проект основан на Gradle, мы должны добавить зависимость в файл build.gradle нашего проекта. Также нам нужно добавить mavenCentral() в раздел репозиториев файла build.gradle :

compile 'org.apache.commons:commons-collections4:4.1'

3. Предикативный набор

Метод predicatedSet() библиотеки SetUtils позволяет определить условия, которым должны удовлетворять все элементы, вставляемые в набор. Он принимает исходный объект Set и предикат.

Мы можем использовать это, чтобы легко проверить, что все элементы набора удовлетворяют определенному условию, что может быть удобно при разработке сторонней библиотеки/API.

Если проверка не пройдена для любого элемента, будет выдано исключение IllegalArgumentException . Фрагмент ниже предотвращает добавление строк, которые не начинаются с «L», в исходный набор или возвращаемый набор проверки :

Set<String> validatingSet
= SetUtils.predicatedSet(sourceSet, s -> s.startsWith("L"));

В библиотеке также есть функции predicatedSortedSet() и predicatedNavigableSet() для работы с SortedSet и NavigableSet соответственно.

4. Объединение, разность и пересечение множества.

В библиотеке есть методы, которые могут вычислять объединение, разность и пересечение элементов Set .

Метод разница() принимает два объекта Set и возвращает неизменяемый SetUtils. Объект SetView . Возвращенный SetUtils. SetView содержит элементы, которые есть в наборе a, но не в наборе b :

Set<Integer> a = new HashSet<>(Arrays.asList(1, 2, 5));
Set<Integer> b = new HashSet<>(Arrays.asList(1, 2));
SetUtils.SetView<Integer> result = SetUtils.difference(a, b);

assertTrue(result.size() == 1 && result.contains(5));

Обратите внимание, что при попытке выполнить операции записи, такие как add() или addAll() , для возвращенных SetUtils. SetView вызовет исключение UnsupportedOperationException .

Чтобы изменить возвращенный результат, нам нужно вызвать метод toSet() возвращенного SetUtils. SetView для получения доступного для записи объекта Set :

Set<Integer> mutableSet = result.toSet();

Метод union библиотеки SetUtils делает именно то, на что он похож — он возвращает все элементы набора a и b . Метод union также возвращает неизменяемый объект SetUtil.SetView :

Set<Integer> expected = new HashSet<>(Arrays.asList(1, 2, 5));
SetUtils.SetView<Integer> union = SetUtils.union(a, b);

assertTrue(SetUtils.isEqualSet(expected, union));

Обратите внимание на метод isEqualSet () , используемый в операторе assert. Это удобный статический метод библиотеки SetUtils , эффективно проверяющий, равны ли два множества.

Чтобы получить пересечение набора, т. е. элементы, которые присутствуют как в наборе a, так и в наборе b , мы будем использовать SetUtils. Метод пересечения() . Этот метод также возвращает объект SetUtil.SetView :

Set<Integer> expected = new HashSet<>(Arrays.asList(1, 2));
SetUtils.SetView<Integer> intersect = SetUtils.intersection(a, b);

assertTrue(SetUtils.isEqualSet(expected, intersect));

5. Преобразование элементов набора

Давайте рассмотрим еще один интересный метод — SetUtils. преобразованный набор() . Этот метод принимает объект Set и интерфейс Transformer . Опираясь на исходный набор, он использует метод transform () интерфейса Transformer для преобразования каждого элемента набора.

Логика преобразования определяется в методе transform() интерфейса Transformer , который применяется к каждому элементу, добавляемому в набор. Фрагмент кода ниже умножает каждый элемент, добавленный в набор, на 2:

Set<Integer> a = SetUtils.transformedSet(new HashSet<>(), e -> e * 2  );
a.add(2);

assertEquals(a.toArray()[0], 4);

Метод transformSet() очень удобен — его можно даже использовать для приведения элементов набора — скажем, из String в Integer. Просто убедитесь, что тип вывода является подтипом ввода.

Предположим, что мы работаем с SortedSet или NavigableSet вместо HashSet, мы можем использовать transformedSortedSet() или convertNavigableSet () соответственно.

Обратите внимание, что новый экземпляр HashSet передается в метод transformSet() . В ситуациях, когда существующий непустой набор передается методу, ранее существовавшие элементы не будут преобразованы.

Если мы хотим преобразовать уже существующие элементы (и добавленные впоследствии), нам нужно использовать метод transformSet() из org.apache.commons.collections4.set.TransformedSet :

Set<Integer> source = new HashSet<>(Arrays.asList(1));
Set<Integer> newSet = TransformedSet.transformedSet(source, e -> e * 2);

assertEquals(newSet.toArray()[0], 2);
assertEquals(source.toArray()[0], 2);

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

6. Установите дизъюнкцию

Библиотека SetUtils предоставляет статический метод, который можно использовать для поиска дизъюнкций множеств. Дизъюнкция множества a и множества b — это все элементы, которые уникальны для множества a и множества b.

Давайте посмотрим, как использовать метод disjunction () библиотеки SetUtils :

Set<Integer> a = new HashSet<>(Arrays.asList(1, 2, 5));
Set<Integer> b = new HashSet<>(Arrays.asList(1, 2, 3));
SetUtils.SetView<Integer> result = SetUtils.disjunction(a, b);

assertTrue(
result.toSet().contains(5) && result.toSet().contains(3));

7. Другие методы в библиотеке SetUtils

В библиотеке SetUtils есть и другие методы, упрощающие обработку заданных данных:

  • Мы можем использовать synchronizedSet() или synchronizedSortedSet() , чтобы получить потокобезопасный Set . Однако, как указано в документах, мы должны вручную синхронизировать итератор возвращаемого набора, чтобы избежать недетерминированного поведения.
  • Мы можем использовать SetUtils.unmodifiedSet() , чтобы получить набор только для чтения. Обратите внимание, что попытка добавить элементы в возвращаемый объект Set вызовет исключение UnsupportedOperationException .
  • Существует также метод SetUtils.emptySet() , который возвращает типобезопасный неизменяемый пустой набор.
  • Метод SetUtils.emptyIfNull() принимает объект Set , допускающий значение NULL . Он возвращает пустой набор, доступный только для чтения, если предоставленный набор имеет значение null; в противном случае он возвращает предоставленный Set
  • SetUtils.orderedSet() вернет объект Set , который поддерживает порядок добавления элементов.
  • SetUtils.hashCodeForSet() может генерировать хэш-код для набора таким образом, что два набора одинаковых элементов будут иметь одинаковый хэш-код.
  • SetUtils.newIdentityHashSet() вернет HashSet , который использует == для сопоставления элемента вместо метода equals() . Пожалуйста, прочитайте о его предостережениях здесь

8. Заключение

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

Как всегда, фрагменты кода доступны на GitHub . Официальный документ по SetUtils API можно найти здесь .