最近使用了一下Eclipse Collections,感觉比Java 8自带的集合操作方便了很多,但是感觉EC并不像Apache Commons Collections或者Google的Guava那么流行,所以写一篇博客,给没用过的人简介一下这个框架。
1、历史
Eclipse Collections最早是由高盛(Goldman Sachs)公司开发的,所以一开始叫GS Collections,在2012年开源,后来捐赠给Eclipse基金会,在2015年改名为Eclipse Collections。
2、简介
Eclipse Collections的设计目标是提供一整套集合管理工具,因此,除了对Java集合框架(Java Collections Framework)的List, Map, Set三大数据结构进行了增强,还引入了Bag, BiMap, MultiMap等数据结构。即使没有Bag, MultiMap这些,光是Eclipse Collections引入的那些操作集合的方法就值得一用,下面我们就用一些Java集合框架和Eclipse Collections对比的例子来看一看。
2.1、去除样板代码(Boilerplate Code)
在Java中,你不能直接对集合做map,filter之类的操作,必须先把集合转成stream,然后对stream做操作,最后再把结果转成你需要的形式,而Eclipse Collections就不需要这样,不管你的数据来源是java的List/Set/Map,还是Eclipse Collections自己的MutableList/MutableSet/MutableMap,都可以只聚焦在业务操作上,而不用关心集合和stream之间的转换1
2
3
4
5
6
7
8
9
10
11
12List<Person> list1 = Arrays.asList(new Person("Mary", 20), new Person("Jack", 15), new Person("Tom", 25));
List<Person> filter1 = list1.stream(). //boilerplate code
filter(person -> person.getAge() > 18).
collect(Collectors.toList());//boilerplate code
Assert.assertEquals(2, filter1.size());
MutableList<Person> list2 = Lists.mutable.of(new Person("Mary", 20), new Person("Jack", 15), new Person("Tom", 25));
MutableList<Person> filter2 = list2.select(person -> person.getAge() > 18);
Assert.assertEquals(2, filter2.size());
MutableList<Person> filter3 = ListIterate.select(list1, person -> person.getAge() > 18);
Assert.assertEquals(2, filter3.size());
可以看到,使用了Eclipse Collections之后,stream和collect方法都可以省略了。
还有一种常见的样板代码是存在于业务逻辑中的,那就是做统计的时候,比如统计一段文本中,每个单词出现的个数,生成一个Map1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21String str = "This is a test, Blah Blah Blah, it is so crazy";
String[] arr = str.split("[ ,]+");
//old style
Map<String, Integer> countMap1 = new HashMap<>();
for (String word : arr) {
int count = countMap1.getOrDefault(word, 0);
count++;
countMap1.put(word, count);
}
Assert.assertEquals(3, countMap1.get("Blah").intValue());
Assert.assertEquals(2, countMap1.get("is").intValue());
//java8 stream & Collectors
Map<String, Long> countMap2 = Arrays.stream(arr).collect(Collectors.groupingBy(w -> w, Collectors.counting()));
Assert.assertEquals(3, countMap2.get("Blah").intValue());
Assert.assertEquals(2, countMap2.get("is").intValue());
//Bag
final MutableBag<String> countBag = Lists.mutable.of(arr).toBag();
Assert.assertEquals(3, countBag.occurrencesOf("Blah"));
Assert.assertEquals(2, countBag.occurrencesOf("is"));
这里Eclipse Collections引入了Bag这种数据结构,Bag相当于一个带有统计每个元素出现次数功能的List,它不仅可以查询元素出现的次数,还可以查询出现次数最多的n个元素(topOccurrences方法)。
2.2、更好的API
Eclipse Collections提供了比Java集合框架更多的API,比如Java的stream有filter方法,如果你想选择年龄大于18的人和年龄小于等于18的人,那就要写两个不同的条件,但是Eclipse Collections就有select/reject两个方法,让你可以用一个条件来筛选,这样可以复用函数表达式,对性能也是有好处的。下面是一些对比代码1
2
3
4
5
6
7
8
9
10
11
12
13
14//java style
List<Person> filter1 = people.stream().filter(person -> person.getAge() > 18).collect(Collectors.toList());
List<Person> filter2 = people.stream().filter(person -> person.getAge() <= 18).collect(Collectors.toList());
Map<Boolean, List<Person>> partition1 = people.stream()
.collect(Collectors.partitioningBy(person -> person.getAge() >=18));
List<Person> adults1 = partition1.get(Boolean.TRUE);
List<Person> children1 = partition1.get(Boolean.FALSE);
//EC style
MutableList<Person> filter3 = people.select(person -> person.getAge() > 18);
MutableList<Person> filter4 = people.reject(person -> person.getAge() > 18);
PartitionMutableList<Person> partition2 = people.partition(person -> person.getAge() > 18);
MutableList<Person> adults2 = partition2.getSelected();
MutableList<Person> children2 = partition2.getRejected();
可以看到,用了Eclipse Collections的代码不仅代码量更少,而且函数名含义也更明确了。
2.3、更好的性能
这部分我并没有实测,可以参考infoQ上这篇文章 中的”内存使用比较”和”JMH 基准测试结果”部分