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

由于Multiset是一个集合,它也包含了例如add(E), size(), iterator()等基本操作同时,它特有的方法包括

count(E) 元素出现的次数

elementSet() 返回元素E的Set 版本

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 数字在整个区间出现的次数。



Multimap

在平时我们在某些时候会用到类似 Map> or Map> 的数据结构,为这样的数据结构书写代码也很不方便,Multimap很好的解决了这个问题, 请看下例

ArrayListMultimap 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 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() 返回所有的键值对,例如上例,将会返回,,,,. 如果想得到键的集合,需要用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


 

BiMap

某些时候,我们需要通过键找到值, 也同时需要通过值找到键。普通的做法是用两个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

现实中需要用到Table数据结构的情形笔笔皆是 ,其用法非常直观简单,请看一下用法

HashBasedTable 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 columnMap = table.column(1);
Map rowMap = table.row(2);

Table 的一些实现


HashBasedTable 后台由HashMap>支持

TreeBasedTable, 后台由 TreeMap>支持

ImmutableTable, 后台由 ImmutableMap>支持, 对于稀疏或者高密度数据效率好一些

ArrayTable, 后台由二维数组支持,创建的时候需要制定行和列长度。


ClassToInstanceMap

这个集合很奇葩基本上就是 Map 的翻版, 例如

ClassToInstanceMap<Number> numberDefaults = MutableClassToInstanceMap.create();
numberDefaults
.putInstance(Integer.class, Integer.valueOf(0));

需要注意的是ClassToInstanceMap implements Map, B>, 也就是Map里面的key值必须是ClassToInstanceMap定义类型的子类类型。


RangeSet

顾名思义,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): 判断一段范围是否在rangeset之类

  • span(): 返回一个包含了set所有区间的最小的range


RangeMap

比较简单,直接见例子:

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"}