1 概览
本文是关于Apache Commons Collections库的SetUtils API。此工具接口将操作Java的Set数据结构。
2 安装依赖
基于maven:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.1</version>
</dependency>
基于gradle:
compile 'org.apache.commons:commons-collections4:4.1'
3 predicatedSet()
SetUtils的predicatedSet()方法允许定义要插入Set的元素的条件,接受Set对象和predicate。
通过此方法,可以很轻松地验证一个集合是否满足某个特定条件。比如在使用某个第三方库或接口之前。
任何一个元素的验证失败,都会抛出IllegalArgumentException。下面的代码示例中,要求sourceSset中的字符串必须是L开头,然后返回一个验证通过的结果给validateingSet。
Set<String> validatingSet
= SetUtils.predicatedSet(sourceSet, s -> s.startsWith("L"));
此外,SetUtils还有predicatedSortedSet()和predicatedNavigableSet()来处理SortedSet和NavigableSet 。
4 Set的合并、交集、插入
SetUtils支持对Set进行合并、交集、插入等操作。
diffence()方法,对两个Set返回不可变的SetUtils.SetView对象。此对象里包含Set a存在,但set 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()方法将返回结果从SetUtils.SetView转换为可写的Set对象:
Set<Integer> mutableSet = result.toSet();
SetUtils的合并方法会返回set a和b中所有的元素。同样地,返回结果也是一个SetUtils.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()是用来检查两个set是否相等的。
为了获取set的交集(同时在set a和b中存在的元素),使用intersection(),返回SetView。
Set<Integer> expected = new HashSet<>(Arrays.asList(1, 2));
SetUtils.SetView<Integer> intersect = SetUtils.intersection(a, b);
assertTrue(SetUtils.isEqualSet(expected, intersect));
5 转换Set元素
SetUtils.transformedSet()接受Set对象和一个Transformer接口,Transformer接口的实现将对set里面的每个元素按定义的规则进行转换。
转换逻辑会对每个加入set的元素生效。下面这段代码,会对每个加入set的元素做乘以2的处理:
Set<Integer> a = SetUtils.transformedSet(new HashSet<>(), e -> e * 2 );
a.add(2);
assertEquals(a.toArray()[0], 4);
transformedSet()很有用,还可以用来转换元素类型,比如从String转换为Integer。只要保证输出是输入的子类型。
如果我们使用的是SortedSet或NavigableSet,相应地我们可以使用transformedSortedSet()和transformedNavigableSet()。
注意:上面的例子中,我们是实例化了一个HashSet对象传给transformedSet()方法的。如果传递是一个已存在的非空的Set,那么已存在的元素是不会被转换的。如果我们现在转换已存在的元素,那么需要使用org.apache.commons.collections4.set.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);
如上,source里面的元素也被转换了的,结果被复制到新的集合newSet中。
6 集合差异
SetUtils提供一个静态方法来获取集合的差异。集合a和集合b的差异,就是说只存在于a或b中的元素。
下面看看代码:
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():
获取线程安全的Sest。但是,官方文档指出,必须手动同步返回set的迭代器,以避免一些不确定的行为。 - unmodifiableSet():
获取一个只读的set。对这个set做增加元素的操作,会抛出UnsupportedOperationException异常。 - emptySet():
返回类型安全的,不可变的空set。 - emptyIfNull():
接受一个null的集合对象。返回一个空的、只读的集合。如果提供空集合,则返回空集合,否则返回提供的集合。 - orderedSet():
保持元素加入顺序,返回集合。 - hashCodeForSet():
为集合创建一个hashcode。这样,拥有同样元素的集合可以具有一样hashcode。 - newIdentityHashSet():
返回一个HashSet,这个集合使用==来判断元素是否相等,而不是用equals()方法。更多说明在这里
8 结论
本文我们讨论了SetUtils库,这个库提供了一些静态方法来更高效简单地操作集合类型的数据结构。
本文相关代码在github,SetUtils的官方文档在这里。