Guava在继承的基础上创建了一些的新的集合类型,这些新的集合类型不是强化版的JDK集合类型,也不是用来完全替代JDK类型的,他们是设计用来完成一些特殊用途的。
Multiset
如果我们需要记载一个字符串出现的次数,最平常的做法如下
Map<String, Integer> counts = new HashMap<String, Integer>();
for (String word : words) {
Integer count = counts.get(word);
if (count == null) {
counts.put(word, 1);
} else {
counts.put(word, count + 1);
}
}
这样显得代码臃肿且易错,Multiset的引入正是为了解决这个问题,和普通的Set不同的是对于每个存在于Multiset存在的元素,Multiset还保存了一分这个元素出现的次数。所以上面代码改变为:
HashMultiset<String>
由于Multiset是一个集合,它也包含了例如add(E), size(), iterator()等基本操作同时,它特有的方法包括
count(E) 元素出现的次数
elementSet() 返回元素E的Set<E> 版本
add(E, int) 增加元素的出现次数
remove(E, int) 减少元素的出现次数
setCount(E, int) 设置元素的出现次数
Multiset有多个JDK集合的对应实现
Map Corresponding Multiset Supports null elements
HashMap HashMultiset Yes
TreeMap TreeMultiset Yes (if the comparator does)
LinkedHashMap LinkedHashMultiset Yes
ConcurrentHashMap ConcurrentHashMultiset No
ImmutableMap ImmutableMultiset No
SortedMultiset
是一个Multiset的变种,它可以很方便的查处在一定范围内的元素出现的整体格式,例如
SortedMultiset.subMultiset(0, BoundType.CLOSED, 100, BoundType.OPEN).size() 可以放我们知道 0-100 数字在整个区间出现的次数。
在平时我们在某些时候会用到类似 Map<K, List<V>> or Map<K, Set<V>> 的数据结构,为这样的数据结构书写代码也很不方便,Multimap很好的解决了这个问题, 请看下例
ArrayListMultimap<String,String> multiMap = ArrayListMultimap.create(); multiMap.put("Bar","1"); multiMap.put("Bar","2"); multiMap.put("Bar","3"); multiMap.put("Bar","3"); multiMap.put("Bar","3"); 结果 a -> [1,2,3,3,3] size()结果5 HashMultimap<String,String> multiMap = HashMultimap.create(); multiMap.put("Bar","1"); multiMap.put("Bar","2"); multiMap.put("Bar","3"); multiMap.put("Bar","3"); multiMap.put("Bar","3"); 结果 a -> [1,2,3] size()结果3
我们也可以用Multimap.get(key)得到对应的集合从而直接操作。 需要注意的事情
Multimap.entries() 返回所有的键值对,例如上例,将会返回<Bar,1>,<Bar,2>,<Bar,3>,<Bar,3>,<Bar,3>. 如果想得到键的集合,需要用Multimap.asMap().keySet()
Multimap.size() 返回所有键值对的个数。
Multimap 也有不用的实现主要是实现的集合不一样:
Implementation Keys behave like... Values behave like..
ArrayListMultimap HashMap ArrayList
HashMultimap HashMap HashSet
LinkedListMultimap* LinkedHashMap* LinkedList*
LinkedHashMultimap** LinkedHashMap LinkedHashSet
TreeMultimap TreeMap TreeSet
ImmutableListMultimap ImmutableMap ImmutableList
ImmutableSetMultimap ImmutableMap ImmutableSet
某些时候,我们需要通过键找到值, 也同时需要通过值找到键。普通的做法是用两个Map还维护
Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
// what happens if "Bob" or 42 are already present?
// weird bugs can arise if we forget to keep these in sync...
这个问题可以通过Bimap优雅的实现
BiMap<String, Integer> userId = HashBiMap.create();
...
String userForId = userId.inverse().get(id);
现实中需要用到Table数据结构的情形笔笔皆是 ,其用法非常直观简单,请看一下用法
HashBasedTable<Integer,Integer,String> table = HashBasedTable.create(); table.put(1,1,"Rook"); table.put(1,2,"Knight"); table.put(1,3,"Bishop"); boolean contains11 = table.contains(1,1); boolean containColumn2 = table.containsColumn(2); boolean containsRow1 = table.containsRow(1); boolan containsRook = table.containsValue("Rook"); table.remove(1,3); table.get(3,4); Map<Integer,String> columnMap = table.column(1); Map<Integer,String> rowMap = table.row(2);
Table 的一些实现
HashBasedTable 后台由HashMap<R, HashMap<C, V>>支持
TreeBasedTable, 后台由 TreeMap<R, TreeMap<C, V>>支持
ImmutableTable, 后台由 ImmutableMap<R, ImmutableMap<C, V>>支持, 对于稀疏或者高密度数据效率好一些
ArrayTable, 后台由二维数组支持,创建的时候需要制定行和列长度。
这个集合很奇葩基本上就是 Map<Class, Object> 的翻版, 例如
ClassToInstanceMap<Number> numberDefaults = MutableClassToInstanceMap.create();
numberDefaults.putInstance(Integer.class, Integer.valueOf(0));
需要注意的是ClassToInstanceMap<B> implements Map<Class<? extends B>, B>, 也就是Map里面的key值必须是ClassToInstanceMap定义类型的子类类型。
顾名思义,RangeSet包含了范围的集合,这些范围是非空的,且可以是不连续的,上个例子先:
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); // {[1, 10]}
rangeSet.add(Range.closedOpen(11, 15)); // disconnected range: {[1, 10], [11, 15)}
rangeSet.add(Range.closedOpen(15, 20)); // connected range; {[1, 10], [11, 20)}
rangeSet.add(Range.openClosed(0, 0)); // empty range; {[1, 10], [11, 20)}
rangeSet.remove(Range.open(5, 10)); // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}
常用方法:
contains(C) 最基本操作, 查看一个element 是否在范围之内
rangeContaining(C):查看是哪一个range 包含了range. 没有就返回null
encloses(Range<C>): 判断一段范围是否在rangeset之类
span(): 返回一个包含了set所有区间的最小的range
比较简单,直接见例子:
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); // {[1, 10] => "foo"}
rangeMap.put(Range.open(3, 6), "bar"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 10] => "foo"}
rangeMap.put(Range.open(10, 20), "foo"); // {[1, 3] => "foo", (3, 6) => "bar", [6, 10] => "foo", (10, 20) => "foo"}
rangeMap.remove(Range.closed(5, 11)); // {[1, 3] => "foo", (3, 5) => "bar", (11, 20) => "foo"}