《Guava学习笔记之二》:新集合类型

时间:2020-12-21 20:46:58

《Guava学习笔记之二》:新集合类型

先看一张Java和Guava的集合对比图,如下:(图截取于http://ifeve.com/google-guava-immutablecollections/)

《Guava学习笔记之二》:新集合类型

在上篇博文中,我们了解了不可变集合,例如:ImmutableSet、ImmutableList等。

这篇博文,就来看下Multiset、MultiMap、BiMap、Table等。

1、MultiSet

我们都知道在Java中Set集合是不接受重复的元素的哈。但是
MultiSet:接受重复值的Set,即可以多次添加相等的元素

Multiset继承自JDK中的Collection接口,而不是Set接口,所以包含重复元素并没有违反原有的接口契约。

可以用两种方式看待Multiset:

1、没有元素顺序限制的ArrayList

2、Map<E, Integer>,键为元素,值为计数

Guava的Multiset API也结合考虑了这两种方式:
当把Multiset看成普通的Collection时,它表现得就像无序的ArrayList:

  • 1、add(E)添加单个给定元素
  • 2、iterator()返回一个迭代器,包含Multiset的所有元素(包括重复的元素)
  • 3、size()返回所有元素的总个数(包括重复的元素)

当把Multiset看作Map

    @Test
public void testMultiSet(){
Set<String> set = Sets.newHashSet("a","b","c");
Multiset<String> multiset = HashMultiset.create();
multiset.addAll(set);
multiset.add("a");
System.out.println(multiset);
Set<String> keys = multiset.elementSet();
for(String key:keys){
System.out.println(key+", count:" +multiset.count(key));
}
}

输出:

[a x 2, b, c]
a, count:2
b, count:1
c, count:1

2、MultiMap

每个有经验的Java程序员都在某处实现过Map<K, List<V>>或Map<K, Set<V>>,并且要忍受这个结构的笨拙。例如,Map<K, Set<V>>通常用来表示非标定有向图。Guava的 Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。

可以用两种方式思考Multimap的概念:”键-单个值映射”的集合:

a -> 1 a -> 2 a ->4 b -> 3 c -> 5

或者”键-值集合映射”的映射:

a -> [1, 2, 4] b -> 3 c -> 5

一般来说,Multimap接口应该用第一种方式看待,但asMap()视图返回Map<K, Collection<V>>,让你可以按另一种方式看待Multimap。重要的是,不会有任何键映射到空集合:一个键要么至少到一个值,要么根本就不在Multimap中。

很少会直接使用Multimap接口,更多时候你会用ListMultimap或SetMultimap接口,它们分别把键映射到List或Set。

Multimap.get(key)以集合形式返回键所对应的值视图,即使没有任何对应的值,也会返回空集合。ListMultimap.get(key)返回List,SetMultimap.get(key)返回Set。

Multimap的视图

Multimap还支持若干强大的视图:

1、asMap为Multimap<K, V>提供Map<K,Collection<V>>形式的视图。返回的Map支持remove操作,并且会反映到底层的Multimap,但它不支持put或putAll操作。更重要的是,如果你想为Multimap中没有的键返回null,而不是一个新的、可写的空集合,你就可以使用asMap().get(key)。(你可以并且应当把asMap.get(key)返回的结果转化为适当的集合类型——如SetMultimap.asMap.get(key)的结果转为Set,ListMultimap.asMap.get(key)的结果转为List.

2、entries用Collection<Map.Entry<K, V>>返回Multimap中所有”键-单个值映射”——包括重复键。(对SetMultimap,返回的是Set)

3、keySet用Set表示Multimap中所有不同的键。

4、keys用Multiset表示Multimap中的所有键,每个键重复出现的次数等于它映射的值的个数。可以从这个Multiset中移除元素,但不能做添加操作;移除操作会反映到底层的Multimap。

5、values()用一个”扁平”的Collection包含Multimap中的所有值。这有一点类似于Iterables.concat(multimap.asMap().values()),但它直接返回了单个Collection,而不像multimap.asMap().values()那样是按键区分开的Collection。

看一个例子:

    @Test
public void testMultimap(){
Multimap<String,Integer> multimap = ArrayListMultimap.create();
multimap.put("a", 1);
multimap.put("a", 2);
multimap.put("b", 3);
multimap.put("c", 4);
System.out.println(multimap);
System.out.println(multimap.size());//所有元素的个数
System.out.println(multimap.keySet().size());//不同的key的个数
//检查multimap是否有"x"这个键
String key ="x";
//这个检查就说明了即使multimap中不含key,也不是返回null,而是返回的是一个空集合
if(multimap.get(key)!=null){
System.out.println("multimap中含有key:"+key);
}
//asMap的使用
Map<String,Collection<Integer>> map = multimap.asMap();

if(map.get(key)==null){//这里可以直接用 map.containsKey(key)
System.out.println("multimap中不含有key:"+key);
}
}

运行结果:

{a=[1, 2], b=[3], c=[4]}
4
3
multimap中含有key:x
multimap中不含有key:x

Multimap不是Map,Multimap<K, V>不是Map<K,Collection<V>>,虽然某些Multimap实现中可能使用了map。它们之间的显著区别包括:

1、Multimap.get(key)总是返回非null、但是可能空的集合。这并不意味着Multimap为相应的键花费内存创建了集合,而只是提供一个集合视图方便你为键增加映射值(注:如果有这样的键,返回的集合只是包装了Multimap中已有的集合;如果没有这样的键,返回的空集合也只是持有Multimap引用的栈对象,让你可以用来操作底层的Multimap。因此,返回的集合不会占据太多内存,数据实际上还是存放在Multimap中)。如果你更喜欢像Map那样,为Multimap中没有的键返回null,请使用asMap()视图获取一个Map<K, Collection<V>>。(或者用静态方法Multimaps.asMap()为ListMultimap返回一个Map<K, List<V>>。对于SetMultimap和SortedSetMultimap,也有类似的静态方法存在)

2、当且仅当有值映射到键时,Multimap.containsKey(key)才会返回true。尤其需要注意的是,如果键k之前映射过一个或多个值,但它们都被移除后,Multimap.containsKey(key)会返回false。

3、Multimap.entries()返回Multimap中所有”键-单个值映射”——包括重复键。如果你想要得到所有”键-值集合映射”,请使用asMap().entrySet()。

4、Multimap.size()返回所有”键-单个值映射”的个数,而非不同键的个数。要得到不同键的个数,请改用Multimap.keySet().size()

《Guava学习笔记之二》:新集合类型

关于更多guava新的集合可以看这里:http://ifeve.com/google-guava-newcollectiontypes/

小结

关于这些新的集合,只需要知道有这些东西就可以了,虽然有一些注意事项,但是在我们使用的时候,我们只需要查阅下资料和API就可以很快的上手使用。

参考资料

1、http://ifeve.com/google-guava-newcollectiontypes/