Guava 集合類

不可變集合類

public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
        "red",
        "orange",
        "yellow",
        "green",
        "blue",
        "purple");

class Foo {
    Set<Bar> bars;
    Foo(Set<Bar> bars) {
        this.bars = ImmutableSet.copyOf(bars); // defensive copy!
    }
}

為什么要使用不可變集合
不可變對象有很多優(yōu)點廓握,包括:

當(dāng)對象被不可信的庫調(diào)用時过椎,不可變形式是安全的;
不可變對象被多個線程調(diào)用時浙芙,不存在競態(tài)條件問題
不可變集合不需要考慮變化捐迫,因此可以節(jié)省時間和空間乾翔。所有不可變的集合都比它們的可變形式有更好的內(nèi)存利用率(分析和測試細節(jié));
不可變對象因為有固定不變弓乙,可以作為常量來安全使用末融。
創(chuàng)建對象的不可變拷貝是一項很好的防御性編程技巧。Guava為所有JDK標(biāo)準(zhǔn)集合類型和Guava新集合類型都提供了簡單易用的不可變版本暇韧。

JDK也提供了Collections.unmodifiableXXX方法把集合包裝為不可變形式勾习,但我們認為不夠好:

笨重而且累贅:不能舒適地用在所有想做防御性拷貝的場景;
不安全:要保證沒人通過原集合的引用進行修改懈玻,返回的集合才是事實上不可變的巧婶;
低效:包裝過的集合仍然保有可變集合的開銷,比如并發(fā)修改的檢查、散列表的額外空間艺栈,等等英岭。
如果你沒有修改某個集合的需求,或者希望某個集合保持不變時湿右,把它防御性地拷貝到不可變集合是個很好的實踐诅妹。

重要提示:所有Guava不可變集合的實現(xiàn)都不接受null值。我們對Google內(nèi)部的代碼庫做過詳細研究毅人,發(fā)現(xiàn)只有5%的情況需要在集合中允許null元素吭狡,剩下的95%場景都是遇到null值就快速失敗。如果你需要在不可變集合中使用null丈莺,請使用JDK中的Collections.unmodifiableXXX方法划煮。更多細節(jié)建議請參考“使用和避免null”。

怎么使用不可變集合
不可變集合可以用如下多種方式創(chuàng)建:

1. copyOf方法缔俄,如ImmutableSet.copyOf(set);
of方法弛秋,如ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”, 1, “b”, 2);
2. Builder工具,如
public static final ImmutableSet<Color> GOOGLE_COLORS =
        ImmutableSet.<Color>builder()
            .addAll(WEBSAFE_COLORS)
            .add(new Color(0, 191, 255))
            .build();
此外俐载,對有序不可變集合來說蟹略,排序是在構(gòu)造集合的時候完成的,如:

3. ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
會在構(gòu)造時就把元素排序為a, b, c, d瞎疼。

我測試發(fā)現(xiàn): of方法得到的不可變集合科乎,如果原集合增加,它也隨之增加贼急,其他的不受影響。

比想象中更智能的copyOf

請注意捏萍,ImmutableXXX.copyOf方法會嘗試在安全的時候避免做拷貝——實際的實現(xiàn)細節(jié)不詳太抓,但通常來說是很智能的,比如:

ImmutableSet<String> foobar = ImmutableSet.of("foo", "bar", "baz");
thingamajig(foobar);

void thingamajig(Collection<String> collection) {
    ImmutableList<String> defensiveCopy = ImmutableList.copyOf(collection);
    ...
}

在這段代碼中令杈,ImmutableList.copyOf(foobar)會智能地直接返回foobar.asList(),它是一個ImmutableSet的常量時間復(fù)雜度的List視圖走敌。
作為一種探索,ImmutableXXX.copyOf(ImmutableCollection)會試圖對如下情況避免線性時間拷貝:

  • 在常量時間內(nèi)使用底層數(shù)據(jù)結(jié)構(gòu)是可能的——例如逗噩,ImmutableSet.copyOf(ImmutableList)就不能在常量時間內(nèi)完成掉丽。
  • 不會造成內(nèi)存泄露——例如,你有個很大的不可變集合ImmutableList<String>
    hugeList异雁, ImmutableList.copyOf(hugeList.subList(0, 10))就會顯式地拷貝捶障,以免不必要地持有hugeList的引用。
  • 不改變語義——所以ImmutableSet.copyOf(myImmutableSortedSet)會顯式地拷貝纲刀,因為和基于比較器的ImmutableSortedSet相比项炼,ImmutableSet對hashCode()和equals有不同語義。
  • 在可能的情況下避免線性拷貝,可以最大限度地減少防御性編程風(fēng)格所帶來的性能開銷锭部。

asList視圖

所有不可變集合都有一個asList()方法提供ImmutableList視圖暂论,來幫助你用列表形式方便地讀取集合元素。例如拌禾,你可以使用sortedSet.asList().get(k)從ImmutableSortedSet中讀取第k個最小元素取胎。

asList()返回的ImmutableList通常是——并不總是——開銷穩(wěn)定的視圖實現(xiàn),而不是簡單地把元素拷貝進List湃窍。也就是說扼菠,asList返回的列表視圖通常比一般的列表平均性能更好,比如坝咐,在底層集合支持的情況下循榆,它總是使用高效的contains方法。

新集合類型

Multiset

統(tǒng)計一個詞在文檔中出現(xiàn)了多少次墨坚,傳統(tǒ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);
    }
}

這種寫法很笨拙秧饮,也容易出錯,并且不支持同時收集多種統(tǒng)計信息泽篮,如總詞數(shù)盗尸。我們可以做的更好。

Guava提供了一個新集合類型 Multiset帽撑,它可以多次添加相等的元素泼各。維基百科從數(shù)學(xué)角度這樣定義Multiset:”集合[set]概念的延伸,它的元素可以重復(fù)出現(xiàn)…與集合[set]相同而與元組[tuple]相反的是亏拉,Multiset元素的順序是無關(guān)緊要的:Multiset {a, a, b}和{a, b, a}是相等的”扣蜻。——譯者注:這里所說的集合[set]是數(shù)學(xué)上的概念及塘,Multiset繼承自JDK中的Collection接口莽使,而不是Set接口,所以包含重復(fù)元素并沒有違反原有的接口契約笙僚。

可以用兩種方式看待Multiset:

  • 沒有元素順序限制的ArrayList<E>
  • Map<E, Integer>芳肌,鍵為元素,值為計數(shù)

Guava的Multiset API也結(jié)合考慮了這兩種方式:
當(dāng)把Multiset看成普通的Collection時肋层,它表現(xiàn)得就像無序的ArrayList:

  • add(E)添加單個給定元素
  • iterator()返回一個迭代器亿笤,包含Multiset的所有元素(包括重復(fù)的元素)
  • size()返回所有元素的總個數(shù)(包括重復(fù)的元素)

當(dāng)把Multiset看作Map<E, Integer>時,它也提供了符合性能期望的查詢操作:

  • count(Object)返回給定元素的計數(shù)栋猖。HashMultiset.count的復(fù)雜度為O(1)净薛,TreeMultiset.count的復(fù)雜度為O(log n)。
  • entrySet()返回Set<Multiset.Entry<E>>掂铐,和Map的entrySet類似罕拂。
  • elementSet()返回所有不重復(fù)元素的Set<E>揍异,和Map的keySet()類似。
  • 所有Multiset實現(xiàn)的內(nèi)存消耗隨著不重復(fù)元素的個數(shù)線性增長爆班。

值得注意的是衷掷,除了極少數(shù)情況,Multiset和JDK中原有的Collection接口契約完全一致——具體來說柿菩,TreeMultiset在判斷元素是否相等時戚嗅,與TreeSet一樣用compare,而不是Object.equals枢舶。另外特別注意懦胞,Multiset.addAll(Collection)可以添加Collection中的所有元素并進行計數(shù),這比用for循環(huán)往Map添加元素和計數(shù)方便多了凉泄。

SortedMultiset

SortedMultiset 是Multiset 接口的變種躏尉,它支持高效地獲取指定范圍的子集。比方說后众,你可以用 latencies.subMultiset(0,BoundType.CLOSED, 100, BoundType.OPEN).size()來統(tǒng)計你的站點中延遲在100毫秒以內(nèi)的訪問胀糜,然后把這個值和latencies.size()相比,以獲取這個延遲水平在總體訪問中的比例蒂誉。

TreeMultiset實現(xiàn)SortedMultiset接口教藻。

Multimap

每個有經(jīng)驗的Java程序員都在某處實現(xiàn)過Map<K, List<V>>或Map<K, Set<V>>,并且要忍受這個結(jié)構(gòu)的笨拙右锨。例如括堤,Map<K, Set<V>>通常用來表示非標(biāo)定有向圖。Guava的 Multimap可以很容易地把一個鍵映射到多個值绍移。換句話說悄窃,Multimap是把鍵映射到任意多個值的一般方式。

可以用兩種方式思考Multimap的概念:”鍵-單個值映射”的集合:

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

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

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

一般來說登夫,Multimap接口應(yīng)該用第一種方式看待广匙,但asMap()視圖返回Map<K, Collection<V>>,讓你可以按另一種方式看待Multimap恼策。重要的是,不會有任何鍵映射到空集合:一個鍵要么至少到一個值潮剪,要么根本就不在Multimap中涣楷。

很少會直接使用Multimap接口,更多時候你會用ListMultimap或SetMultimap接口抗碰,它們分別把鍵映射到List或Set狮斗。

修改Multimap

Multimap.get(key) 以集合形式返回鍵所對應(yīng)的值視圖,即使沒有任何對應(yīng)的值弧蝇,也會返回空集合碳褒。ListMultimap.get(key)返回List折砸,SetMultimap.get(key)返回Set。

對值視圖集合進行的修改最終都會反映到底層的Multimap沙峻。

Multimap的視圖

Multimap還支持若干強大的視圖:

  • asMap為Multimap<K, V>提供Map<K,Collection<V>>形式的視圖睦授。返回的Map支持remove操作,并且會反映到底層的Multimap摔寨,但它不支持put或putAll操作去枷。更重要的是,如果你想為Multimap中沒有的鍵返回null是复,而不是一個新的删顶、可寫的空集合,你就可以使用asMap().get(key)淑廊。(你可以并且應(yīng)當(dāng)把asMap.get(key)返回的結(jié)果轉(zhuǎn)化為適當(dāng)?shù)募项愋汀鏢etMultimap.asMap.get(key)的結(jié)果轉(zhuǎn)為Set逗余,ListMultimap.asMap.get(key)的結(jié)果轉(zhuǎn)為List——Java類型系統(tǒng)不允許ListMultimap直接為asMap.get(key)返回List——譯者注:也可以用Multimaps中的asMap靜態(tài)方法幫你完成類型轉(zhuǎn)換
  • entries用Collection<Map.Entry<K, V>>返回Multimap中所有”鍵-單個值映射”——包括重復(fù)鍵。(對SetMultimap季惩,返回的是Set)
  • keySet用Set表示Multimap中所有不同的鍵录粱。
  • keys用Multiset表示Multimap中的所有鍵,每個鍵重復(fù)出現(xiàn)的次數(shù)等于它映射的值的個數(shù)蜀备」匾。可以從這個Multiset中移除元素,但不能做添加操作碾阁;移除操作會反映到底層的Multimap输虱。
  • values()用一個”扁平”的Collection<V>包含Multimap中的所有值。這有一點類似于Iterables.concat(multimap.asMap().values())脂凶,但它直接返回了單個Collection宪睹,而不像multimap.asMap().values()那樣是按鍵區(qū)分開的Collection。

BiMap

1.雙向映射

2.value唯一蚕钦。 插入相同value報錯亭病,forcePut()強制插入會更改key

3.inverse()方法翻轉(zhuǎn)key與value。不返回新對象嘶居,保持一種視圖關(guān)聯(lián)罪帖,操作會互相影響。


Table

多個索引的數(shù)據(jù)結(jié)構(gòu)的邮屁,通常情況下整袁,我們只能用這種丑陋的Map<FirstName, Map<LastName, Person>>來實現(xiàn)。guava的table提供更方便的實現(xiàn)佑吝。類似一個表的結(jié)構(gòu)坐昙,并且可以切換視圖。


public void test(){
    Table<String, Integer, String> aTable = HashBasedTable.create();
    for (char a = 'A'; a <= 'C'; ++a) {
        for (Integer b = 1; b <= 3; ++b) {
            aTable.put(Character.toString(a), b, String.format("%c%d", a, b));
        }
    }
    System.out.println(aTable);
    System.out.println(aTable.rowKeySet());
    System.out.println(aTable.columnKeySet());
    System.out.println(aTable.row("A"));
    System.out.println(aTable.get("B",2));
    System.out.println(aTable.contains("A",4));
    System.out.println(aTable.columnMap());
    System.out.println(aTable.rowMap());
    System.out.println(aTable.size());
    System.out.println(aTable.remove("A",1));
    System.out.println(aTable);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芋忿,一起剝皮案震驚了整個濱河市炸客,隨后出現(xiàn)的幾起案子疾棵,更是在濱河造成了極大的恐慌,老刑警劉巖痹仙,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件是尔,死亡現(xiàn)場離奇詭異,居然都是意外死亡蝶溶,警方通過查閱死者的電腦和手機嗜历,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抖所,“玉大人梨州,你說我怎么就攤上這事√镌” “怎么了暴匠?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長傻粘。 經(jīng)常有香客問我每窖,道長,這世上最難降的妖魔是什么弦悉? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任窒典,我火速辦了婚禮,結(jié)果婚禮上稽莉,老公的妹妹穿的比我還像新娘瀑志。我一直安慰自己,他們只是感情好污秆,可當(dāng)我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布劈猪。 她就那樣靜靜地躺著,像睡著了一般良拼。 火紅的嫁衣襯著肌膚如雪战得。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天庸推,我揣著相機與錄音常侦,去河邊找鬼。 笑死贬媒,一個胖子當(dāng)著我的面吹牛刮吧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播掖蛤,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼井厌!你這毒婦竟也來了蚓庭?” 一聲冷哼從身側(cè)響起致讥,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎器赞,沒想到半個月后垢袱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡港柜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年请契,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片夏醉。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡爽锥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畔柔,到底是詐尸還是另有隱情氯夷,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布靶擦,位于F島的核電站腮考,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏玄捕。R本人自食惡果不足惜踩蔚,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枚粘。 院中可真熱鬧馅闽,春花似錦、人聲如沸赌结。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柬姚。三九已至拟杉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間量承,已是汗流浹背搬设。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撕捍,地道東北人拿穴。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像忧风,于是被迫代替她去往敵國和親默色。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Collection ├List │├LinkedList │├ArrayList │└Vector │└Stac...
    AndyZX閱讀 880評論 0 1
  • com.google.common.collect 1狮腿、不可變集合 何為“不可變” 無法修改返回容器的內(nèi)容腿宰,注意呕诉,...
    拾壹北閱讀 2,600評論 0 4
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法吃度,內(nèi)部類的語法甩挫,繼承相關(guān)的語法,異常的語法椿每,線程的語...
    子非魚_t_閱讀 31,669評論 18 399
  • 1.順序容器 順序容器是將單一類型元素聚集起來成為容器伊者,然后根據(jù)位置來存儲和訪問這些元素。標(biāo)準(zhǔn)庫常用順序容器如下:...
    Mr希靈閱讀 750評論 0 4
  • 集合類簡介 為什么出現(xiàn)集合類央碟?面向?qū)ο笳Z言對事物的體現(xiàn)都是以對象的形式,所以為了方便對多個對象的操作均函,就要對對象進...
    阿敏其人閱讀 1,424評論 0 7