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

Введение в коллекции Eclipse

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

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 .