«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() в раздел repositories файла build.gradle:
compile 'org.apache.commons:commons-collections4:4.1'
3. Predicated Set
Метод predicatedSet() библиотеки SetUtils позволяет определять условия, которым должны удовлетворять все элементы, которые должны быть вставлены в набор. Он принимает исходный объект Set и предикат.
Мы можем использовать это, чтобы легко проверить, что все элементы набора удовлетворяют определенному условию, что может быть удобно при разработке сторонней библиотеки/API.
Если проверка не пройдена для любого элемента, будет выдано исключение IllegalArgumentException. Фрагмент ниже предотвращает добавление строк, которые не начинаются с «L», в исходный набор или возвращаемый набор проверки:
Set<String> validatingSet
= SetUtils.predicatedSet(sourceSet, s -> s.startsWith("L"));
В библиотеке также есть функции predicatedSortedSet() и predicatedNavigableSet() для работы с SortedSet и NavigableSet соответственно. .
4. Объединение, разность и пересечение множества
В библиотеке есть методы, которые могут вычислять объединение, разность и пересечение элементов множества.
Метод разница() принимает два объекта 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.intersection(). Этот метод также возвращает объект 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.transformedSet(). Этот метод принимает объект 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, мы можем использовать преобразованный SortedSet() или преобразованныйNavigableSet() соответственно.
Обратите внимание, что новый экземпляр 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 вызовет исключение UnsupportedOperationException. Существует также метод SetUtils.emptySet(), который возвращает типобезопасный неизменяемый пустой набор. Метод SetUtils.emptyIfNull() принимает объект Set, допускающий значение NULL. Он возвращает пустой набор, доступный только для чтения, если предоставленный набор имеет значение null; в противном случае он возвращает предоставленный набор Set. SetUtils.orderedSet() возвращает объект Set, поддерживающий порядок добавления элементов. SetUtils.hashCodeForSet() может генерировать хэш-код для набора — таким образом, что два набора одни и те же элементы будут иметь одинаковый хэш-код. SetUtils.newIdentityHashSet() вернет HashSet, который использует == для сопоставления элемента вместо метода equals(). Пожалуйста, прочтите о ее предостережениях здесь
8. Заключение
В этой статье мы подробно рассмотрели библиотеку SetUtils. Вспомогательный класс предлагает статические методы, которые делают работу с установленной структурой данных простой и увлекательной. Это также повышает производительность.
Как всегда, фрагменты кода доступны на GitHub. Официальный документ по SetUtils API можно найти здесь.