1. 概述

本文将深入探讨 Apache Commons Collections 库中的 SetUtils 工具类。简单来说,这些工具类能帮我们高效处理 Java 中的 Set 数据结构操作。

2. 依赖安装

要在项目中使用 SetUtils,需在 pom.xml 添加以下依赖:

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

如果是 Gradle 项目,在 build.gradle 中添加(确保配置了 mavenCentral() 仓库):

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

3. 断言集合

predicatedSet() 方法允许定义集合元素的准入条件。它接收一个源 Set 和一个 Predicate 断言。

这个特性在开发第三方库/API 时特别实用,能确保所有元素满足特定条件。⚠️ 注意:当元素验证失败时会抛出 IllegalArgumentException

以下代码禁止添加不以 'L' 开头的字符串

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

针对 SortedSetNavigableSet,库还提供了 predicatedSortedSet()predicatedNavigableSet() 方法。

4. 集合的并集、差集与交集

SetUtils 提供了计算集合并集、差集和交集的便捷方法:

4.1 差集计算

difference() 方法接收两个 Set,返回不可变的 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));

⚠️ 踩坑提示:**对返回的 SetView 执行写操作(如 add()addAll())会抛出 UnsupportedOperationException**。需调用 toSet() 获取可写集合:

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

4.2 并集计算

union() 方法返回两个集合的所有元素(同样返回不可变 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()SetUtils 提供的静态方法,用于高效判断两个集合是否相等。

4.3 交集计算

intersection() 返回两个集合共有的元素:

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

5. 集合元素转换

transformedSet() 方法接收一个 SetTransformer 接口。它基于源集合,使用 Transformertransform() 方法转换每个元素。

转换逻辑在 transform() 中定义,会作用于所有新增元素。以下代码将每个元素乘以 2:

Set<Integer> a = SetUtils.transformedSet(new HashSet<>(), e -> e * 2  );
a.add(2);
 
assertEquals(a.toArray()[0], 4);

这个方法非常实用,甚至可用于类型转换(如 String → Integer),但需确保输出类型是输入类型的子类。

对于 SortedSetNavigableSet,可使用 transformedSortedSet()transformedNavigableSet()

⚠️ 重要:向 transformedSet() 传递非空集合时,已有元素不会被转换。若需转换已有元素及后续新增元素,应使用 TransformedSet.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. 集合对称差

disjunction() 方法计算两个集合的对称差(仅存在于其中一个集合的元素):

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 还提供了一系列简化集合操作的实用方法:

线程安全集合

  • synchronizedSet() / synchronizedSortedSet():获取线程安全集合
  • ⚠️ 注意:必须手动同步返回集合的迭代器,否则可能导致非确定性行为

不可变集合

  • unmodifiableSet():获取只读集合(修改操作抛出 UnsupportedOperationException
  • emptySet():返回类型安全的不可变空集合

空值处理

  • emptyIfNull():接收可空 Set,若为 null 返回空只读集合,否则返回原集合

有序集合

  • orderedSet():返回保持插入顺序的 Set

哈希工具

  • hashCodeForSet():为集合生成哈希码(相同元素的集合哈希码相同)
  • newIdentityHashSet():返回使用 == 而非 equals() 比较元素的 HashSet(⚠️ 使用前需了解其陷阱)

8. 总结

本文深入剖析了 SetUtils 工具类。其提供的静态方法让集合操作变得简单高效,显著提升开发效率。所有示例代码可在 GitHub 获取,官方文档见 此处


原始标题:Apache Commons Collections SetUtils