Guava 引入了很多 JDK 没有的、但明显有用的新集合类型。
这些新类型是为了和 JDK 集合框架共存,而没有往 JDK 集合抽象中硬塞其他概念。
作为一般规则,Guava 集合非常精准地遵循了 JDK 接口契约。
Multiset
我们都知道 Set 是无序不重复的,与之相反的是 List 是有序可重复的。 Multiset 是几个意思?
没错 ,Multiset 占据了 List 和 Set 之间的一个灰色地带:允许重复,但是不保证顺序。
举个例子,使用 JDK 如果我们想:“统计每个单词出现的次数” 一般这样写:
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 就轻松多了,比如
List<String> languages = List.of("java", "python", "javascript", "java");
HashMultiset<String> multiset = HashMultiset.create(languages);
System.out.println(multiset.count("java")); //结果是:2
System.out.println(multiset.count("python")); //结果是:1
// 如果你想要不重复元素集合,还可以直接转成 Set
// Set<String> words = multiset.elementSet();
如果你用传统的 HashMap 做统计,那么后续如果再增加元素,你想变更统计结果是不还得再写个 for 循环往 Map 添加元素计数?用 Multiset 轻松多了,直接 add 就行:
List<String> languages = List.of("java", "python", "javascript", "java");
HashMultiset<String> multiset = HashMultiset.create(languages);
multiset.add("python");
multiset.addAll(Lists.newArrayList("go", "java", "c"));
multiset.elementSet().forEach(x -> {
System.out.println(x + " 的出现次数: " + multiset.count(x));
});
结果:
python 的出现次数: 2
java 的出现次数: 3
c 的出现次数: 1
go 的出现次数: 1
javascript 的出现次数: 1
当然如果你的需求比较简单,比如只是简单统计去重后的个数什么的,用 JDK8 以上的流式编程一行代码就能搞定,不用搞这么复杂
words.stream().distinct().count();
下面是 multiset 的一些常用方法:
方法 | 描述 |
---|---|
count(E) | 给定元素在 Multiset 中的计数 |
elementSet() | Multiset 中不重复元素的集合,类型为 Set |
entrySet() | 和 Map 的 entrySet 类似,返回 Set<Multiset.Entry >,其中包含的 Entry 支持 getElement() 和 getCount() 方法 |
add(E, int) | 增加给定元素在 Multiset 中的计数 |
remove(E, int) | 减少给定元素在 Multiset 中的计数 |
setCount(E, int) | 设置给定元素在 Multiset 中的计数,不可以为负数 |
size() | 返回集合元素的总个数(包括重复的元素) |
Guava 提供了多种 Multiset 的实现,大致对应 JDK 中 Map 的各种实现:
Map | 对应的 Multiset | 是否支持 null 元素 |
---|---|---|
HashMap | HashMultiset | 是 |
TreeMap | TreeMultiset | 是(如果 comparator 支持的话) |
LinkedHashMap | LinkedHashMultiset | 是 |
ConcurrentHashMap | ConcurrentHashMultiset | 否 |
ImmutableMap | ImmutableMultiset | 否 |
总结
使用 Multiset 可以减少 Map 的复杂操作,从而减少代码量,代码量少了,bug 自然少。早点儿下班。
Multimap
有的时候我们需要一个 key 对应多个 value 的这种结构,通常我们会构造类似这样的数据结构:
Map<K, List<V>> 或 Map<K, Set<V>>
,甚至可能更复杂,基于这个还有嵌套数据结构。
如果你需要找到 List 中的某个值是否存在,或者删除 List 中的一个元素 ,又或者要遍历整个数据结构,那么要写一坨代码,老费劲了。
我们来看用 Multimap 怎么做,比如:
ArrayListMultimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("Fruits", "Bannana");
multimap.put("Fruits", "Apple");
multimap.put("Fruits", "Pear");
multimap.put("Vegetables", "Carrot");
multimap.put("language", "java");
multimap.put("language", "python");
multimap.put("language", "go");
multimap.put("language", "python");
下面是输出的结果: