Guava的使用指南之Collections
Guava Collections可以帮助我们写出更简短精炼、可读性强的代码。
看看Guava Collections为我们做了哪些很酷的事情:
- Immutable Collections:还在使用Collections.unmodifiableXXX()? Immutable Collections这才是真正的不可修改的集合
- Multiset:看看如何把重复的元素放入一个集合
- Multimaps:需要在一个key对应多个value的时候,自己写一个实现比较繁琐,让Multimaps来帮帮
- BiMap:java.util.Map只能保证key的不重复,BiMap保证value也不重复
- MapMaker:超级强大的Map构造类
- Ordering class:大家知道用Comparator作为比较器来对集合排序,但是对于多关键字排序Ordering class可以简化很多的代码
- 其他特性
当然,如果没有Guava Collections你也可以用Java Collections Framework完成上面的功能。但是Guava Collections提供的这些API经过精心设计,而且还有2500个单元测试来保障它的质量。所以我们没必要重新发明轮子。接下来我们来详细看看Guava Collections的一些具体功能。
Immutable Collections:真正的不可修改的集合
大家都用过Collections.unmodifiableXXX()来做一个不可修改的集合。例如你要构造存储常量的Set,你可以这样做:
1 2 |
|
这看上去似乎不错,因为每次调unmodifiableSet.add()都会抛出一个UnsupportedOperationException。感觉安全了?慢!如果有人在原来的set上add或者remove元素会怎么样?结果unmodifiableSet也是被add或者remove元素了。而且构造这样一个简单的set写了两句长的代码。下面看看ImmutableSet是怎么来做地更安全和简洁:
1
|
|
就这样一句就够了,而且试图调add方法的时候,它一样会抛出UnsupportedOperationException。重要的是代码的可读性增强了不少,非常直观的展现了代码的用意。如果像之前这个代码保护一个set怎么做呢?你可以:
1
|
|
从构造的方式来说,ImmutableSet集合还提供了Builder模式来构造一个集合:
1 2 |
|
在这个例子里面Builder不但能加入单个元素还能加入既有的集合。
除此之外,Guava Collections还提供了各种Immutable集合的实现:ImmutableList, ImmutableMap, ImmutableSortedSet, ImmutableSortedMap。
Multiset:把重复的元素放入集合
你可能会说这和Set接口的契约冲突,因为Set接口的javaDoc里面规定不能放入重复元素。事实上,Multiset并没有实现java.util.Set接口,它更像是一个Bag。普通的Set就像这样:[car,ship,bike],而Multiset会是这样:[carx2,shipx6,bikex3]。
譬如一个List里面有各种字符,然后你要统计每个字符串在List里面出现的次数:
1 2 3 4 5 6 7 |
|
如果用Multiset就可以这样:
1 2 3 4 |
|
这样连循环都不用了,而且Multiset用的方法叫count,显然比在Map里面调用get有更好的可读性。Multiset还提供了setCount这样设定元素重复次数的方法,虽然你可以通过使用Map来实现类似的功能,但是程序的可读性比Multiset差了很多。
常用实现Multiset接口的类有: * HashMultiset:元素存放于HashMap * LinkedHashMultiset:元素存放于LinkedHashMap,即元素的排列顺序由第一次放入的顺序决定 * TreeMultiset:元素被排序存放于TreeMap * EnumMultiset:元素必须是enum类型 * ImmutableMultiset:不可修改的Mutiset
看到这里你可能已经发现Guava Collections都是以create或是of这样的静态方法来构造对象。这是因为这些集合类大多有多个参数的私有构造方法,由于参数数目很多,客户代码程序员使用起来就很不方便。而且以这种方式可以返回原类型的子类型对象。另外,对于创建范型对象来讲,这种方式更加简洁。
Muitimap:在Map的value里面放多个元素
Muitimap就是一个key对应多个value的数据结构。看上去它很像java.util.Map的结构,但是Muitimap不是Map,没有实现Map的接口。设想你对Map调了2次参数key一样的put方法,结果就是第2次的value覆盖了第一次的value。但是对Muitimap来说这个key同时对应了2个value。所以Map看上去是:{k1=v1,k2=v2,…},而Muitimap是:{k1=[v1,v2,v3],k2=[v7,v8],…}。
举个记名投票的例子。所有选票都放在一个List
1 2 3 4 5 6 7 8 9 10 |
|
我们再来看看Muitimap能做些什么:
1 2 3 4 |
|
就这么简单!
Muitimap接口的主要实现类有: * HashMultimap:key放在HashMap, 而value放在HashSet, 即一个key对应的value不可重复 * ArrayListMultimap:key放在HashMap,而value放在ArrayList,即一个key对应的value有顺序可重复 * LinkedHashMultimap:key放在LinkedHashMap,而value放在LinkedHashSet,即一个key对应的value有顺序不可重复 * TreeMultimap:key放在TreeMap,而value放在TreeSet,即一个key对应的value有排列顺序 * ImmutableMultimap:不可修改的Multimap
BiMap:双向Map
BiMap实现了java.util.Map接口。它的特点是它的value和它key一样也是不可重复的,换句话说它的key和value是等价的。如果你往BiMap的value里面放了重复的元素,就会得到IllegalArgumentException.
举个例子,你可能经常会碰到在Map里面根据value值来反推它的key值的逻辑:
1 2 3 4 5 |
|
如果把User和Address都放在BiMap,那么一句代码就得到结果了:
1
|
|
这里的inverse方法就是把BiMap的key集合value集合对调,因此biMap==biMap.inverse().inverse()。
BiMap的常用实现有: * HashBiMap:key集合与value集合都有HashMap实现 * EnumBiMap:key与value都必须是enum类型 * ImmutableBiMap:不可修改的BiMap
MapMaker:超级强大的Map构造工具
MapMaker是用来构造ConcurrentMap的工具类。为什么可以把MapMaker叫做超级强大?看了下面的例子你就知道了。首先,它可以用来构造ConcurrentHashMap:
1 2 |
|
或者构造用各种不同reference作为key和value的Map:
1 2 |
|
或者构造有自动移除时间过期项的Map:
1 2 |
|
或者构造有最大限制数目的Map:
1 2 3 |
|
或者提供当Map里面不包含所get的项,而需要自动加入到Map的功能。这个功能当Map作为缓存的时候很有用:
1 2 3 4 5 6 |
|
这些还不是最强大的特性,最厉害的是MapMaker可以提供拥有以上所有特性的Map:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Ordering class:灵活的多字段排序比较器
需对集合排序或者求最大值最小值,首推java.util.Collections类,但关键是要提供Comparator接口的实现。假设有个待排序的List
1 2 3 4 5 6 7 8 |
|
这看上去有点眼晕,如果用一串if-else也好不到哪里去。看看ComparisonChain能做到什么:
1 2 3 4 5 6 7 |
|
如果排序关键字要用自定义比较器,compare方法也有接受Comparator的重载版本。譬如Foo里面每个排序关键字都已经有了各自的Comparator,那么利用ComparisonChain可以:
1 2 3 4 5 6 7 |
|
Ordring类还提供了一个组合Comparator对象的方法。而且Ordring本身实现了Comparator接口所以它能直接作为Comparator使用:
1 2 3 |
|
其他特性
过滤器:利用Collection2.filter()方法过滤集合中不符合条件的元素。譬如过滤一个List里面小于10的元素:
1 2 3 4 5 6 |
|
当然,你可以自己写一个循环来实现这个功能,但是这样不能保证之后小于10的元素不被放入集合。filter的强大之处在于返回的filterCollection仍然有排斥小于10的元素的特性,如果调filterCollection.add(9)就会得到一个IllegalArgumentException.
转换器:利用Collections.transform方法来转换集合中的元素。譬如把一个Set里面所有元素都转化成带格式的String来产生新的Collection:
1 2 3 4 5 6 |
|