1. Обзор
Eclipse Collections — это еще одна улучшенная структура коллекций для Java.
Проще говоря, он предоставляет оптимизированные реализации, а также некоторые дополнительные структуры данных и функции, которых нет в ядре Java.
Библиотека предоставляет как изменяемые, так и неизменяемые реализации всех структур данных.
2. Зависимость от Maven
Давайте начнем с добавления следующей зависимости Maven в наш pom.xml
:
<dependency
<groupId>org.eclipse.collections</groupId>
<artifactId>eclipse-collections</artifactId>
<version>8.2.0</version>
</dependency>
Последнюю версию библиотеки мы можем найти в центральном репозитории Maven .
3. Общая картина
3.1. Основные типы коллекций
Основные типы коллекций в Eclipse Collections:
ListIterable
— упорядоченная коллекция, сохраняющая порядок вставки и допускающая дублирование элементов. Подинтерфейсы включают:MutableList
,FixedSizeList
иImmutableList
. Наиболее распространеннойреализацией ListIterable является FastList, который является подклассом MutableList.
SetIterable
— коллекция, не допускающая дублирования элементов. Он может быть сортированным или несортированным. Подинтерфейсы включают:SortedSetIterable
иUnsortedSetIterable.
Наиболее распространенной несортированнойреализацией SetIterable
являетсяUnifiedSet.
MapIterable
— набор пар ключ/значение. Подинтерфейсы включаютMutableMap
,FixedSizeMap
иImmutableMap
. Двумя распространенными реализациями являютсяUnifiedMap
иMutableSortedMap
. В то время какUnifiedMap
не поддерживает никакого порядка,MutableSortedMap
поддерживает естественный порядок элементов.BiMap
— набор пар ключ/значение, которые можно повторять в любом направлении.BiMap
расширяет интерфейсMapIterable
Сумка
— неупорядоченная коллекция, допускающая дублирование. Подинтерфейсы включаютMutableBag и
FixedSizeBag
. Наиболее распространенной реализацией являетсяHashBag.
StackIterable
— коллекция, которая поддерживает порядок «последний пришел — первый ушел», перебирая элементы в обратном порядке вставки. Подинтерфейсы включаютMutableStack
иImmutableStack .
MultiMap
— набор пар ключ/значение, допускающий несколько значений для каждого ключа.
3.2. Коллекции примитивов
Фреймворк также предоставляет огромный набор примитивных коллекций ; их реализации названы в честь типа, который они содержат. Для каждого из них существуют изменяемые, неизменяемые, синхронизируемые и немодифицируемые формы:
- Примитивные
списки
- Примитивные
наборы
- Примитивные
стеки
- Первобытные
сумки
- Примитивные
карты
Интервал
Существует огромное количество примитивных карт, охватывающих все возможные комбинации либо примитивных, либо объектных ключей, либо примитивных, либо объектных значений.
Небольшое примечание: IntInterval
— это диапазон целых чисел, которые можно повторять с использованием значения шага.
4. Создание коллекции
Чтобы добавить элементы в ArrayList
или HashSet
, мы создаем экземпляр коллекции, вызывая конструктор без аргументов, а затем добавляя каждый элемент один за другим.
Хотя мы по-прежнему можем делать это в Eclipse Collections, мы также можем создать экземпляр коллекции и предоставить все исходные элементы одновременно в одной строке.
Давайте посмотрим, как мы можем создать экземпляр FastList
:
MutableList<String> list = FastList.newListWith(
"Porsche", "Volkswagen", "Toyota", "Mercedes", "Toyota");
Точно так же мы можем создать экземпляр UnifiedSet
и добавить к нему элементы, передав элементы статическому методу newSetWith()
:
Set<String> comparison = UnifiedSet.newSetWith(
"Porsche", "Volkswagen", "Toyota", "Mercedes");
Вот как мы можем создать экземпляр HashBag
:
MutableBag<String> bag = HashBag.newBagWith(
"Porsche", "Volkswagen", "Toyota", "Porsche", "Mercedes");
Создание экземпляров карт и добавление к ним пар ключ-значение аналогично. Единственное отличие состоит в том, что мы передаем пары ключей и значений методу newMapWith()
как реализации интерфейса Pair .
Возьмем в качестве примера UnifiedMap :
Pair<Integer, String> pair1 = Tuples.pair(1, "One");
Pair<Integer, String> pair2 = Tuples.pair(2, "Two");
Pair<Integer, String> pair3 = Tuples.pair(3, "Three");
UnifiedMap<Integer, String> map = new UnifiedMap<>(pair1, pair2, pair3);
Мы по-прежнему можем использовать подход Java Collections API:
UnifiedMap<Integer, String> map = new UnifiedMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
Поскольку неизменяемые коллекции не могут быть изменены, у них нет реализаций методов, изменяющих коллекции , таких как add()
и remove()
.
Однако неизменяемые коллекции позволяют нам вызывать эти методы, но в этом случае будет выброшено исключение UnsupportedOperationException
.
5. Извлечение элементов из коллекций
Как и при использовании стандартных списков , элементы
списков
коллекций Eclipse могут быть получены по их индексу:
list.get(0);
А значения карт Eclipse Collections можно получить по их ключу:
map.get(0);
Методы getFirst()
и getLast()
можно использовать для получения соответственно первого и последнего элементов списка. В случае других коллекций они возвращают первый и последний элементы, которые были бы возвращены итератором.
map.getFirst();
map.getLast();
Методы max()
и min()
можно использовать для получения максимального и минимального значений коллекции на основе естественного порядка.
map.max();
map.min();
6. Перебор коллекции
Eclipse Collections предоставляет множество способов перебора коллекций. Давайте посмотрим, что они из себя представляют и как они работают на практике.
6.1. Фильтрация коллекций
Шаблон выбора возвращает новую коллекцию, содержащую элементы коллекции, удовлетворяющие логическому условию. По сути, это операция фильтрации.
Вот пример:
@Test
public void givenListwhenSelect_thenCorrect() {
MutableList<Integer> greaterThanThirty = list
.select(Predicates.greaterThan(30))
.sortThis();
Assertions.assertThat(greaterThanThirty)
.containsExactly(31, 38, 41);
}
То же самое можно сделать с помощью простого лямбда-выражения:
return list.select(i -> i > 30)
.sortThis();
Схема отказа противоположна. Он возвращает коллекцию всех элементов, которые не удовлетворяют логическому условию.
Давайте посмотрим пример:
@Test
public void whenReject_thenCorrect() {
MutableList<Integer> notGreaterThanThirty = list
.reject(Predicates.greaterThan(30))
.sortThis();
Assertions.assertThat(notGreaterThanThirty)
.containsExactlyElementsOf(this.expectedList);
}
Здесь мы отклоняем все элементы, которые больше 30.
6.2. Метод сбора(
)
Метод collect
возвращает новую коллекцию, элементами которой являются результаты, возвращаемые предоставленным лямбда-выражением, — по сути, это комбинация методов map()
и collect()
из Stream API.
Давайте посмотрим на это в действии:
@Test
public void whenCollect_thenCorrect() {
Student student1 = new Student("John", "Hopkins");
Student student2 = new Student("George", "Adams");
MutableList<Student> students = FastList
.newListWith(student1, student2);
MutableList<String> lastNames = students
.collect(Student::getLastName);
Assertions.assertThat(lastNames)
.containsExactly("Hopkins", "Adams");
}
Созданная коллекция lastNames
содержит фамилии, собранные из списка студентов .
Но что, если возвращаемая коллекция является коллекцией коллекций, и мы не хотим поддерживать вложенную структуру?
Например, если у каждого учащегося несколько адресов и нам нужна коллекция, содержащая адреса в виде строк
, а не коллекция коллекций, мы можем использовать метод flatCollect()
.
Вот пример:
@Test
public void whenFlatCollect_thenCorrect() {
MutableList<String> addresses = students
.flatCollect(Student::getAddresses);
Assertions.assertThat(addresses)
.containsExactlyElementsOf(this.expectedAddresses);
}
6.3. Обнаружение элемента
Метод обнаружения
находит и возвращает первый элемент, удовлетворяющий логическому условию.
Давайте рассмотрим быстрый пример:
@Test
public void whenDetect_thenCorrect() {
Integer result = list.detect(Predicates.greaterThan(30));
Assertions.assertThat(result)
.isEqualTo(41);
}
Метод anySatisfy
определяет, удовлетворяет ли какой-либо элемент коллекции логическому условию.
Вот пример:
@Test
public void whenAnySatisfiesCondition_thenCorrect() {
boolean result = list.anySatisfy(Predicates.greaterThan(30));
assertTrue(result);
}
Точно так же метод allSatisfy
определяет, удовлетворяют ли все элементы коллекции логическому условию.
Давайте посмотрим на быстрый пример:
@Test
public void whenAnySatisfiesCondition_thenCorrect() {
boolean result = list.allSatisfy(Predicates.greaterThan(0));
assertTrue(result);
}
6.4. Метод раздела(
)
Метод partition
выделяет каждый элемент коллекции в одну из двух коллекций в зависимости от того, удовлетворяет ли элемент логическому условию.
Давайте посмотрим пример:
@Test
public void whenAnySatisfiesCondition_thenCorrect() {
MutableList<Integer> numbers = list;
PartitionMutableList<Integer> partitionedFolks = numbers
.partition(i -> i > 30);
MutableList<Integer> greaterThanThirty = partitionedFolks
.getSelected()
.sortThis();
MutableList<Integer> smallerThanThirty = partitionedFolks
.getRejected()
.sortThis();
Assertions.assertThat(smallerThanThirty)
.containsExactly(1, 5, 8, 17, 23);
Assertions.assertThat(greaterThanThirty)
.containsExactly(31, 38, 41);
}
6.5. Ленивая итерация
Ленивая итерация — это шаблон оптимизации, в котором метод итерации вызывается, но его фактическое выполнение откладывается до тех пор, пока его действие или возвращаемые значения не потребуются другим последующим методом.
@Test
public void whenLazyIteration_thenCorrect() {
Student student1 = new Student("John", "Hopkins");
Student student2 = new Student("George", "Adams");
Student student3 = new Student("Jennifer", "Rodriguez");
MutableList<Student> students = Lists.mutable
.with(student1, student2, student3);
LazyIterable<Student> lazyStudents = students.asLazy();
LazyIterable<String> lastNames = lazyStudents
.collect(Student::getLastName);
Assertions.assertThat(lastNames)
.containsAll(Lists.mutable.with("Hopkins", "Adams", "Rodriguez"));
}
Здесь объект lazyStudents
не извлекает элементы списка студентов
до тех пор, пока не будет вызван метод collect() .
7. Сопряжение элементов коллекции
Метод zip()
возвращает новую коллекцию, объединяя элементы двух коллекций в пары. Если какая-либо из двух коллекций длиннее, остальные элементы будут усечены.
Давайте посмотрим, как мы можем его использовать:
@Test
public void whenZip_thenCorrect() {
MutableList<String> numbers = Lists.mutable
.with("1", "2", "3", "Ignored");
MutableList<String> cars = Lists.mutable
.with("Porsche", "Volvo", "Toyota");
MutableList<Pair<String, String>> pairs = numbers.zip(cars);
Assertions.assertThat(pairs)
.containsExactlyElementsOf(this.expectedPairs);
}
Мы также можем соединить элементы коллекции с их индексами, используя метод zipWithIndex()
:
@Test
public void whenZip_thenCorrect() {
MutableList<String> cars = FastList
.newListWith("Porsche", "Volvo", "Toyota");
MutableList<Pair<String, Integer>> pairs = cars.zipWithIndex();
Assertions.assertThat(pairs)
.containsExactlyElementsOf(this.expectedPairs);
}
8. Преобразование коллекций
Eclipse Collections предоставляет простые методы преобразования одного типа контейнера в другой. Это методы toList()
, toSet()
, toBag()
и toMap().
Давайте посмотрим, как мы можем их использовать:
public static List convertToList() {
UnifiedSet<String> cars = new UnifiedSet<>();
cars.add("Toyota");
cars.add("Mercedes");
cars.add("Volkswagen");
return cars.toList();
}
Запустим наш тест:
@Test
public void whenConvertContainerToAnother_thenCorrect() {
MutableList<String> cars = (MutableList) ConvertContainerToAnother
.convertToList();
Assertions.assertThat(cars)
.containsExactlyElementsOf(
FastList.newListWith("Volkswagen", "Toyota", "Mercedes"));
}
9. Заключение
В этом руководстве мы рассмотрели краткий обзор коллекций Eclipse и функций, которые они предоставляют.
Полная реализация этого руководства доступна на GitHub .