Contents

Eclipse Collections 简介

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 Central Repository 中找到该库的最新版本。

3. 基础

3.1. 基本集合类型

Eclipse Collections 中的基本集合类型是:

  • ListIterable - 一个有序集合,维护插入顺序并允许重复元素。子接口包括: MutableListFixedSizeListImmutableList。最常见的 ListIterable 实现是 FastList,它是 MutableList 的子类
  • SetIterable – 一个不允许重复元素的集合。它可以排序或未排序。子接口包括: SortedSetIterableUnsortedSetIterable。最常见的未排序SetIterable实现是UnifiedSet
  • MapIterable – 键/值对的集合。子接口包括MutableMapFixedSizeMapImmutableMap。两个常见的实现是UnifiedMapMutableSortedMap。虽然UnifiedMap不维护任何顺序,但 MutableSortedMap维护元素的自然顺序
  • BiMap - 可以在任一方向迭代的键/值对的集合。BiMap扩展了MapIterable接口
  • Bag – 允许重复的无序集合。子接口包括MutableBag 和 FixedSizeBag。最常见的实现是HashBag
  • StackIterable – 一个维护“后进先出”顺序的集合,以反向插入顺序遍历元素。子接口包括MutableStackImmutableStack
  • MultiMap – 键/值对的集合,允许每个键有多个值

3.2. 原始集合

该框架还提供了大量的原始集合;它们的实现以它们持有的类型命名。每种类型都有可变、不可变、同步和不可修改的形式:

  • 原始Lists
  • 原始Sets
  • 原始Stacks
  • 原始Bags
  • 原始Maps
  • IntInterval

有大量原始映射形式涵盖原始或对象键以及原始或对象值的所有可能组合。

快速说明 - IntInterval是可以使用步长值迭代的整数范围。

4. 实例化一个集合

要将元素添加到ArrayListHashSet,我们通过调用无参数构造函数实例化一个集合,然后一个一个地添加每个元素。

虽然我们仍然可以在 Eclipse Collections 中执行此操作,但我们也可以实例化一个集合并在一行中同时提供所有初始元素。

让我们看看如何实例化一个FastList

MutableList<String> list = FastList.newListWith(
  "Porsche", "Volkswagen", "Toyota", "Mercedes", "Toyota");

同样,我们可以通过将元素传递给newSetWith()静态方法来实例化UnifiedSet并向其添加元素:

Set<String> comparison = UnifiedSet.newSetWith(
  "Porsche", "Volkswagen", "Toyota", "Mercedes");

下面是我们如何实例化一个HashBag

MutableBag<String> bag = HashBag.newBagWith(
  "Porsche", "Volkswagen", "Toyota", "Porsche", "Mercedes");

实例化映射并向它们添加键值对是类似的。唯一的区别是我们将键值对作为Pair接口的实现传递给*newMapWith()*方法。

我们以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. 从集合中检索元素

就像使用标准Lists一样,Eclipse Collections Lists的元素可以通过它们的索引来检索:

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);
}

同样的事情可以使用一个简单的 lambda 表达式来完成:

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()*方法

collect方法返回一个新集合,其元素是提供的 lambda 表达式返回的结果——本质上它是 Stream API 中的*map()collect()*的组合。

让我们看看它的实际效果:

@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包含从Student列表中收集的姓氏。

但是,如果返回的集合是集合的集合并且我们不想维护嵌套结构怎么办?

例如,如果每个学生有多个地址,并且我们需要一个包含地址作为string的集合而不是集合的集合,我们可以使用*flatCollect()*方法。

这是一个例子:

@Test
public void whenFlatCollect_thenCorrect() {
    MutableList<String> addresses = students
      .flatCollect(Student::getAddresses);
    
    Assertions.assertThat(addresses)
      .containsExactlyElementsOf(this.expectedAddresses);
}

6.3. 元素检测

detect方法查找并返回满足逻辑条件的第一个元素。

让我们看一个简单的例子:

@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()*方法

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()方法之前不会检索Student列表的元素。

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"));
}