com.google.common.collect
1攘已、不可變集合
何為“不可變”
無(wú)法修改返回容器的內(nèi)容坠七,注意亚兄,這里指的是無(wú)法直接通過(guò)set或者add方法修改容器內(nèi)reference的指向给梅,而不是禁止reference指向內(nèi)容的修改杂彭。
為什么要使用不可變集合
- 當(dāng)對(duì)象被不可信的庫(kù)調(diào)用時(shí),不可變形式是安全的;
- 不可變對(duì)象被多個(gè)線程調(diào)用時(shí),不存在競(jìng)態(tài)條件問(wèn)題
- 不可變集合不需要考慮變化,因此可以節(jié)省時(shí)間和空間。所有不可變的集合都比它們的可變形式有更好的內(nèi)存利用率(分析和測(cè)試細(xì)節(jié));
- 不可變對(duì)象因?yàn)橛泄潭ú蛔?可以作為常量來(lái)安全使用木蹬。
JDK提供的不可變?nèi)萜鰿ollections.unmodifiableXXX有哪些缺點(diǎn)至耻?
Collections.unmodifiableXXX返回的是原來(lái)容器的視圖:
- 返回的容器無(wú)法修改
- 對(duì)原有容器進(jìn)行修改,會(huì)影響返回容器的內(nèi)容镊叁,沒(méi)有做到真正的不可變:
例如:通過(guò)Collections.unmodifiablelist(originList)得到一個(gè)不可變?nèi)萜鱱nmodifiableList尘颓,當(dāng)修改originList時(shí),unmodifiableList也會(huì)受到影響晦譬。 - 透過(guò)視圖查看容器的內(nèi)容
- 容器內(nèi)容的變更也會(huì)通過(guò)視圖展現(xiàn)出來(lái)
Guava提供的不可變?nèi)萜饔心男┨攸c(diǎn)疤苹?
- 提供不可修改容器的功能,保證返回的容器不能被調(diào)用者修改敛腌,并且原容器的修改不會(huì)影響ImmutableXXX
- 對(duì)不可靠的客戶代碼庫(kù)來(lái)說(shuō)卧土,它使用安全惫皱,可以再未受信任的類庫(kù)中安全使用這些對(duì)象
- 線程安全的:Immutable對(duì)象在多線程下安全,沒(méi)有競(jìng)態(tài)條件
- 不需要支持可變性尤莺,可以盡量節(jié)省空間和時(shí)間開(kāi)銷(xiāo)逸吵。所有的不可變集合實(shí)現(xiàn)都比可變集合更加有限的利用內(nèi)存
- 可以被使用為一個(gè)常量,并且期望在未來(lái)也是保持不變的
Immutable的實(shí)現(xiàn)原理
- 返回的不是原對(duì)象的視圖缝裁,而是原容器的一份拷貝扫皱;調(diào)用add() / set() 方法是報(bào)出異常
- 因?yàn)橹皇莄opy了原容器本身(reference),并不是deep copy捷绑,因此韩脑,對(duì)容器中的元素內(nèi)容的修改,也會(huì)影響ImmutableXXX
Guava提供了哪些不可變集合
創(chuàng)建不可變集合
不可變集合可以用如下多種方式創(chuàng)建:
copyOf 方法,如:
ImmutableSet.copyOf(set);of 方法,如:
ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”, 1, “b”, 2);Builder 工具,如:
public static final ImmutableSet<Color> GOOGLE_COLORS =
ImmutableSet.<Color>builder()
.addAll(WEBSAFE_COLORS)
.add(new Color(0, 191, 255))
.build();
此外,對(duì)有序不可變集合來(lái)說(shuō),排序是在構(gòu)造集合的時(shí)候完成的,如:
ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
會(huì)在構(gòu)造時(shí)就把元素排序?yàn)?a, b, c, d粹污。
asList視圖
所有不可變集合都有一個(gè) asList()方法提供 ImmutableList 視圖,來(lái)幫助你用列表形式方便地讀取集合元素段多。例如,你可以使用
sortedSet.asList().get(k)
從 ImmutableSortedSet 中讀取第 k 個(gè)最小元素。
2壮吩、新集合類型
Guava 引入了很多 JDK 沒(méi)有的进苍、但我們發(fā)現(xiàn)明顯有用的新集合類型。
Multiset
Multise的特定是可以多次添加相等的元素鸭叙。
從數(shù)學(xué)概念上來(lái)講觉啊,它是一個(gè)set:
維基百科從數(shù)學(xué)角度這樣定義 Multiset:
Multiset是集合(set)概念的延伸,它的元素可以重復(fù)出現(xiàn)...
與set相同而與元組相反的是沈贝,Multiset 元素的順序是無(wú)關(guān)緊要的:Multiset {a, a, b}和{a, b, a}是相等的杠人。
Multiset繼承自 JDK 中的 Collection 接口,而不是 Set 接口,可以用兩種方式看待 Multiset:
- 沒(méi)有元素順序限制的 ArrayList
- Map<E, Integer>,鍵為元素,值為計(jì)數(shù)
Guava 的 Multiset API 也結(jié)合考慮了這兩種方式:
(1)當(dāng)把 Multiset 看成普通的 Collection 時(shí),它表現(xiàn)得就像無(wú)序的 ArrayList:
- add(E)添加單個(gè)給定元素
- iterator()返回一個(gè)迭代器,包含 Multiset 的所有元素(包括重復(fù)的元素)
- size()返回所有元素的總個(gè)數(shù)(包括重復(fù)的元素)
(2)當(dāng)把 Multiset 看作 Map<E, Integer>時(shí),它也提供了符合性能期望的查詢操作:
- count(Object)返回給定元素的計(jì)數(shù)宋下。HashMultiset.count 的復(fù)雜度為 O(1),TreeMultiset.count 的復(fù)雜
度為 O(log n)嗡善。 - entrySet()返回 Set<Multiset.Entry>,和 Map 的 entrySet 類似。
- elementSet()返回所有不重復(fù)元素的 Set,和 Map 的 keySet()類似学歧。
- 所有 Multiset 實(shí)現(xiàn)的內(nèi)存消耗隨著不重復(fù)元素的個(gè)數(shù)線性增長(zhǎng)罩引。
由此可見(jiàn),Mutilset很適合需要對(duì)元素計(jì)數(shù)的場(chǎng)景枝笨,比如下面的代碼:
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);
}
}
就可以換用Mutilset來(lái)進(jìn)行統(tǒng)計(jì)袁铐。
Multimap
每個(gè)有經(jīng)驗(yàn)的 Java 程序員都在某處實(shí)現(xiàn)過(guò) Map<K, List>或 Map<K, Set>,并且要忍受這個(gè)結(jié)構(gòu)的笨拙。例如伺帘,Map<K, Set>通常用來(lái)表示非標(biāo)定有向圖昭躺。Guava 的 Multimap 可以很容易地把一個(gè)鍵映射到多個(gè)值忌锯。換句話說(shuō),Multimap 是把鍵映射到任意多個(gè)值的一般方式伪嫁。
BiMap
傳統(tǒng)上,實(shí)現(xiàn)鍵值對(duì)的雙向映射需要維護(hù)兩個(gè)單獨(dú)的 map,并保持它們間的同步。但這種方式很容易出錯(cuò),而且對(duì)于值已經(jīng)在 map 中的情況,會(huì)變得非撑伎澹混亂张咳。
BiMap可以用來(lái)維護(hù)雙向映射帝洪,可以用 inverse()反轉(zhuǎn) BiMap<K, V>的鍵值映射得到。
Table
通常來(lái)說(shuō),當(dāng)你想使用多個(gè)鍵做索引的時(shí)候,你可能會(huì)用類似 Map<FirstName, Map<LastName, Person>>的實(shí)現(xiàn),這種方式很丑陋,使用上也不友好脚猾。Guava 為此提供了新集合類型 Table,它有兩個(gè)支持所有類型的鍵:”行”和”列”葱峡。Table 提供多種視圖,以便你從各種角度使用它:
ClassToInstanceMap
ClassToInstanceMap 是一種特殊的 Map:它的鍵是類型,而值是符合鍵所指類型的對(duì)象。
RangeSet
RangeSet描述了一組不相連的龙助、非空的區(qū)間砰奕。當(dāng)把一個(gè)區(qū)間添加到可變的RangeSet時(shí),所有相連的區(qū)間會(huì)被合并,空區(qū)間會(huì)被忽略。
RangeMap
RangeMap 描述了”不相交的提鸟、非空的區(qū)間”到特定值的映射军援。和 RangeSet 不同,RangeMap 不會(huì)合并相鄰的映射,即便相鄰的區(qū)間映射到相同的值。
強(qiáng)大的集合工具類:java.util.Collections 中未包含的集合工具
靜態(tài)工廠方法
Guava提供了靜態(tài)通常方法用來(lái)更方便地生產(chǎn)集合:
Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");
注意:Guava 引入的新集合類型沒(méi)有暴露原始構(gòu)造器,也沒(méi)有在工具類中提供初始化方法称勋。而是直接在集合類中
提供了靜態(tài)工廠方法,例如:
Multiset<String> multiset = HashMultiset.create();
Iterables
Lists
除了靜態(tài)工廠方法和函數(shù)式編程方法,Lists 為 List 類型的對(duì)象提供了若干工具方法胸哥。
partition(List, int) 把 List 按指定大小分割
reverse(List) 返回給定 List 的反轉(zhuǎn)視圖。注: 如果 List 是不可變的,考慮改用 ImmutableList.reverse()赡鲜。
Sets
集合理論方法
union(Set, Set) 并集
intersection(Set, Set) 交集
difference(Set, Set) 差集 -> contained by set1 and not contained by set2
symmetricDifference(Set, Set) 對(duì)稱差 The returned set contains all elements that are contained in either {@code set1} or {@code set2} but not in both. The iteration order of the returned set is undefined.
powerSet() 返回給定集合的所有子集
用 copyInto(Set) 拷貝進(jìn)另一個(gè)可變集合;
用 immutableCopy()對(duì)自己做不可變拷貝空厌。
Maps
Maps 類有若干值得單獨(dú)說(shuō)明的、很酷的方法银酬。
uniqueIndex
Maps.uniqueIndex(Iterable,Function) 通常針對(duì)的場(chǎng)景是:有一組對(duì)象,它 們?cè)谀硞€(gè)屬性上分別有獨(dú)一無(wú)二的值,而我們希望能夠按照這個(gè)屬性值查找對(duì)象——譯者注:這個(gè)方法返回一個(gè) Map,鍵為 Function 返回的屬性值,值為 Iterable 中相應(yīng)的元素,因此我們可以反復(fù)用這個(gè) Map 進(jìn)行查找操作嘲更。
比方說(shuō),我們有一堆字符串,這些字符串的長(zhǎng)度都是獨(dú)一無(wú)二的,而我們希望能夠按照特定長(zhǎng)度查找字符串:
ImmutableMap<Integer, String> stringsByIndex
= Maps.uniqueIndex(strings,new Function<String, Integer> () {
public Integer apply(String string) {
return string.length();
}
});