New Collection Type
ref: NewCollectionTypesExplained · google/guava Wiki · GitHub
ref: 【Guava 教學】(7)Multiset、Multimap 與 BiMap
Multiset
多重集合(Multiset)是集合(Set)概念的推廣(Generalization),Set 裡面相同元素只能出現一次,Multiet 多重集合則允許相同元素出現多次,元素在集合中有重複次數(Occurrence)的概念,多重集合又稱為 Bag。
Guava 提供 Multiset,可方便地計算每一個元素發生的次數,且能 iterate 每一個元素。換句話說,Multiset 有兩個功能
類似沒有順序的 ArrayList
add(E) 新增一個元素
iterator() 迭代每一個元素
size()
類似 Map<E, Integer>,儲存元素及數量
count(Object) 計算某個元素的數量
entrySet() 回傳 Set<Multiset.Entry> ,類似 Map 的 entrySet
elementSet() 回傳 Set ,包含所有不同的 elements,類似 Map 的 keySet()
@Test
public void jdk_word_count() {
List<String> words = Arrays.asList("one", "two", "three", "one", "three");
Map<String, Integer> counts = new HashMap<>();
for(String word : words) {
Integer count = counts.get(word);
if (count == null) {
counts.put(word, 1);
} else {
counts.put(word, count + 1);
}
}
System.out.println(counts); // {two=1, one=2, three=2}
Map<String, List<String>> wordBag = new HashMap<>();
for(String word : words) {
List<String> repeatedWds = wordBag.get(word);
if(repeatedWds == null) {
repeatedWds = new ArrayList<>();
wordBag.put(word, repeatedWds);
}
repeatedWds.add(word);
}
// {one=[one, one], two=[two], three=[three, three]}
System.out.println(wordBag);
}
@Test
public void guava_multiset_word_count() {
List<String> words = Arrays.asList("one", "two", "three", "one", "three");
Multiset<String> wordBag = HashMultiset.create(words);
System.out.println("");
// [two, one x 2, three x 2]
System.out.println(wordBag);
// Element: one, Occurrence(s): 2
// Element: two, Occurrence(s): 1
// Element: three, Occurrence(s): 2
for (Multiset.Entry<String> entry : wordBag.entrySet())
{
System.out.println("Element: "+entry.getElement() +", Occurrence(s): " + entry.getCount());
}
for(String word : wordBag) {
// 可直接使用 words 裡面的每一個元素
System.out.print(word);
}
}
Guava 提供的 Multiset implementation,可對應到 JDK map implementation
Map | Corresponding Multiset | Supports null elements |
---|---|---|
HashMap |
HashMultiset |
Yes |
TreeMap |
TreeMultiset |
Yes |
LinkedHashMap |
LinkedHashMultiset |
Yes |
ConcurrentHashMap |
ConcurrentHashMultiset |
No |
ImmutableMap |
ImmutableMultiset |
No |
TreeMultiset 有實作 SortedMultiset 介面,該 interface 可快速取得 sub-multiset,ex:
@Test
public void guava_submultiset_word_count() {
List<String> words = Arrays.asList("one", "two", "three", "one", "three");
TreeMultiset<String> wordBag = TreeMultiset.create(words);
SortedMultiset<String> subWordBag = wordBag.subMultiset("one", BoundType.CLOSED, "two", BoundType.OPEN);
System.out.println("");
// [two, one x 2, three x 2]
System.out.println(wordBag);
// [one x 2, three x 2]
// 根據 String 的排序,取得比 "two" 比較後還小的那些元素集合
// TreeMultiset 裡面的元素都有排序過
System.out.println(subWordBag);
//Element: one, Occurrence(s): 2
//Element: three, Occurrence(s): 2
for (SortedMultiset.Entry<String> entry : subWordBag.entrySet())
{
System.out.println("Element: "+entry.getElement() +", Occurrence(s): " + entry.getCount());
}
for(String word : subWordBag) {
// 可直接使用 subWordBag 裡面的每一個元素
// one one three three
System.out.print(word+" ");
}
}
Multimap
ref: Guava Multimap類 - Guava教學
在 java 只有提供 Map<K, List<V>>
or Map<K, Set<V>>
在 Guava 以 Multimap 提供 keys 對應到任意資料結構的一個介面
Multimap 有一個 asMap() ,會回傳 Map<K, Collection<V>>
比較常用的是 ListMultimap / SetMutimap,比較少直接使用 Multimap interface
Construction & Modify
雖然可以直接用 ListMultimap / SetMutimap 的 create() 產生 Multimap,但比較建議使用 MultimapBuilder
產生
@Test
public void construction() {
List<String> tolist = Arrays.asList("to1@domain");
List<String> cclist = Arrays.asList("cc1@domain", "cc2@domain");
List<String> bcclist = Arrays.asList("bcc1@domain", "bcc2@domain");
// 使用 MultimapBuilder
// creates a ListMultimap with tree keys and array list values
ListMultimap<String, List<String>> treeListMultimap =
MultimapBuilder.treeKeys().arrayListValues().build();
treeListMultimap.put("to", tolist);
treeListMultimap.put("cc", cclist);
treeListMultimap.put("bcc", bcclist);
System.out.println("treeListMultimap:");
// {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
System.out.println(treeListMultimap);
// 直接使用 create()
Multimap<String,List<String>> treeListMultimap2 = ArrayListMultimap.create();
treeListMultimap2.put("to", tolist);
treeListMultimap2.put("cc", cclist);
treeListMultimap2.put("bcc", bcclist);
// creates a SetMultimap with hash keys and enum set values
SetMultimap<String, Grade> hashEnumMultimap =
MultimapBuilder.hashKeys().enumSetValues(Grade.class).build();
hashEnumMultimap.put("Alice", Grade.A);
hashEnumMultimap.put("Bruce", Grade.B);
System.out.println("hashEnumMultimap:");
// {Bruce=[B], Alice=[A]}
System.out.println(hashEnumMultimap);
}
public enum Grade {
A, B, C, D, F, INCOMPLETE
}
@Test
public void modify() {
System.out.println("");
List<String> tolist = Arrays.asList("to1@domain");
List<String> cclist = Arrays.asList("cc1@domain", "cc2@domain");
List<String> bcclist = Arrays.asList("bcc1@domain", "bcc2@domain");
// 使用 MultimapBuilder
// creates a ListMultimap with tree keys and array list values
ListMultimap<String, List<String>> treeListMultimap =
MultimapBuilder.treeKeys().arrayListValues().build();
treeListMultimap.put("to", tolist);
treeListMultimap.put("cc", cclist);
treeListMultimap.put("bcc", bcclist);
// treeListMultimap: {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
System.out.println("treeListMultimap: "+ treeListMultimap);
treeListMultimap.removeAll("cc");
// treeListMultimap2 after removeAll: {bcc=[[bcc1@domain, bcc2@domain]], to=[[to1@domain]]}
System.out.println("treeListMultimap2 after removeAll: "+ treeListMultimap);
List<String> tolist2 = Arrays.asList("to2@domain");
treeListMultimap.put("to", tolist2);
// treeListMultimap2 put new key/value: {bcc=[[bcc1@domain, bcc2@domain]], to=[[to1@domain], [to2@domain]]}
System.out.println("treeListMultimap2 put new key/value: "+ treeListMultimap);
treeListMultimap.clear();
treeListMultimap.put("to", tolist2);
// treeListMultimap2: {to=[[to2@domain]]}
System.out.println("treeListMultimap2: "+ treeListMultimap);
}
views
@Test
public void views() {
System.out.println("");
List<String> tolist = Arrays.asList("to1@domain");
List<String> cclist = Arrays.asList("cc1@domain", "cc2@domain");
List<String> bcclist = Arrays.asList("bcc1@domain", "bcc2@domain");
// 使用 MultimapBuilder
// creates a ListMultimap with tree keys and array list values
ListMultimap<String, List<String>> treeListMultimap =
MultimapBuilder.treeKeys().arrayListValues().build();
treeListMultimap.put("to", tolist);
treeListMultimap.put("cc", cclist);
treeListMultimap.put("bcc", bcclist);
// treeListMultimap: {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
System.out.println("treeListMultimap: "+ treeListMultimap);
// asMap: views any Multimap<K, V> as a Map<K, Collection<V>>
Map<String, Collection<List<String>>> tomap = treeListMultimap.asMap();
// asMap: {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
System.out.println("asMap: "+ tomap);
// entries(): views the Collection<Map.Entry<K, V>>
// entries: [bcc=[bcc1@domain, bcc2@domain], cc=[cc1@domain, cc2@domain], to=[to1@domain]]
Collection<Map.Entry<String, List<String>>> entries = treeListMultimap.entries();
System.out.println("entries: "+ entries);
// keySet: views the distinct keys in the Multimap as a Set
// keySet: [bcc, cc, to]
Set<String> keySet = treeListMultimap.keySet();
System.out.println("keySet: "+ keySet);
// keys: views the keys of the Multimap as a Multiset
// keysMultiset: [bcc, cc, to]
Multiset<String> keysMultiset = treeListMultimap.keys();
System.out.println("keysMultiset: "+ keysMultiset);
// values: views all the values in the Multimap as a "flattened" Collection<V>
// values: [[bcc1@domain, bcc2@domain], [cc1@domain, cc2@domain], [to1@domain]]
Collection<List<String>> values = treeListMultimap.values();
System.out.println("values: "+ values);
}
implementations
建議使用 MultimapBuilder
不是直接 create()
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 |