Java8(3):Java8 中 Map 接口的新方法

我們提一個(gè)需求:給定一個(gè) List<String>柏腻,統(tǒng)計(jì)每個(gè)元素出現(xiàn)的所有位置。

比如系吭,給定 list["a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g"] 五嫂,那么應(yīng)該返回:

a : [0]
b : [1, 2]
c : [3, 4, 5]
d : [6, 7, 8]
f : [9, 10]
g : [11]

很明顯,我們很適合使用 Map 來(lái)完成這件事情:

public static Map<String, List<Integer>> getElementPositions(List<String> list) {
    Map<String, List<Integer>> positionsMap = new HashMap<>();

    for (int i = 0; i < list.size(); i++) {
        String str = list.get(i);
        List<Integer> positions = positionsMap.get(str);

        if (positions == null) { // 如果 positionsMap 還不存在 str 這個(gè)鍵及其對(duì)應(yīng)的 List<Integer>
            positions = new ArrayList<>(1);
            positionsMap.put(str, positions); // 將 str 及其對(duì)應(yīng)的 positions 放入 positionsMap
        }

        positions.add(i); // 將索引加入 str 相關(guān)聯(lián)的 List<Integer> 中
    }

    return positionsMap;
}

public static void main(String[] args) throws Exception {
    List<String> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

    System.out.println("使用 Java8 之前的 API:");
    Map<String, List<Integer>> elementPositions = getElementPositions(list);
    System.out.println(elementPositions);
}

運(yùn)行結(jié)果:

Java8 之前 API 的運(yùn)行結(jié)果

Java8 時(shí),Map<K, V> 接口添加了一個(gè)新的方法沃缘,putIfAbsent(K key, V value)躯枢,功能是:
如果當(dāng)前 Map 不存在鍵 key 或者該 key 關(guān)聯(lián)的值為 null,那么就執(zhí)行 put(key, value)槐臀;否則锄蹂,便不執(zhí)行 put 操作。該方法等價(jià)于如下代碼:

putIfAbsent 的等價(jià)代碼

(題外話(huà):putIfAbsent 方法與 put 方法一樣水慨,返回的是方法調(diào)用之前與參數(shù) key 相關(guān)聯(lián)的 value

使用 putIfAbsent 修改 getElementPositions 方法:

public static Map<String, List<Integer>> getElementPositions(List<String> list) {
    Map<String, List<Integer>> positionsMap = new HashMap<>();

    for (int i = 0; i < list.size(); i++) {
        String str = list.get(i);
        positionsMap.putIfAbsent(str, new ArrayList<>(1)); // 如果 positionsMap 不存在鍵 str 或者 str 關(guān)聯(lián)的 List<Integer> 為 null得糜,那么就會(huì)進(jìn)行 put;否則不執(zhí)行 put
        positionsMap.get(str).add(i);
    }

    return positionsMap;
}

public static void main(String[] args) throws Exception {
    List<String> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

    System.out.println("使用 putIfAbsent:");
    Map<String, List<Integer>> elementPositions = getElementPositions(list);
    System.out.println(elementPositions);
}

運(yùn)行結(jié)果:

使用 putIfAbsent 的運(yùn)行結(jié)果

可以看到使用 putIfAbsent 之后的 getElementPositions 簡(jiǎn)潔了一點(diǎn)晰洒,那還能更簡(jiǎn)潔嗎朝抖?

查看 Map 接口的方法,可以發(fā)現(xiàn)在 JDK1.8 時(shí)谍珊,還添加了如下兩個(gè)方法:

compute 方法 和 computeIfAbsent 方法

查看 compute 方法的 API 文檔槽棍,可以發(fā)現(xiàn) compute 方法與如下代碼等價(jià)

 V oldValue = map.get(key);
 V newValue = remappingFunction.apply(key, oldValue);
 if (oldValue != null ) {
    if (newValue != null)
       map.put(key, newValue);
    else
       map.remove(key);
 } else { // 即 原來(lái)的 key 不存在 Map 中或該 key 關(guān)聯(lián)的 value 為 null
    if (newValue != null)
       map.put(key, newValue);
    else
       return null;
 }

compute 方法和原來(lái) put 方法的區(qū)別在于:

put(K key, V value) 方法,如果 keyMap 中不存在抬驴,那么直接加入;如果已經(jīng)存在缆巧,那么使用新的 value 替換舊的 value布持;

compute(K key, BiFunction remappingFunction) 方法可以通過(guò)一個(gè) BiFunction 來(lái)計(jì)算出新的 valueBiFunction 的參數(shù)為舊的 keyvalue陕悬,返回計(jì)算出新的 value —— 與 put 方法不同题暖,compute 方法返回的會(huì)是最新的與 key 相關(guān)聯(lián)的 value,而不是舊的 value捉超。
所以可以使用 compute 方法改寫(xiě) getElementPositions 如下:

public static Map<String, List<Integer>> getElementPositions(List<String> list) {
    Map<String, List<Integer>> positionsMap = new HashMap<>();

    for (int i = 0; i < list.size(); i++) {
        positionsMap.compute(list.get(i), (k, v) -> v == null ? new ArrayList<>(1) : v).add(i);
    }

    return positionsMap;
}

public static void main(String[] args) throws Exception {
    List<String> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

    System.out.println("使用 compute:");
    Map<String, List<Integer>> elementPositions = getElementPositions(list);
    System.out.println(elementPositions);
}

(k, v) -> v == null ? new ArrayList<>(1) : v 即 如果當(dāng)前的 valuenull胧卤,那么 該 BiFunction 的返回值為 new ArrayList<>(1);如果不為 null拼岳,那么返回值便是本身枝誊。而且因?yàn)?compute 方法會(huì)返回新的 value —— 此時(shí)便是與 list.get(i)key) 相關(guān)聯(lián)的 ArrayList —— 所以我們可以直接調(diào)用其 add 方法。

運(yùn)行結(jié)果:

使用 compute 的運(yùn)行結(jié)果

很棒~(yú) 還能更簡(jiǎn)潔嗎惜纸? 我們?cè)倏纯?computeIfAbsent 方法:
computeIfAbsentcompute 的關(guān)系,就類(lèi)似于 putIfAbsentput的關(guān)系:
computeIfAbsentkey 不在 Map 中或者與 key 相關(guān)聯(lián)的 valuenull 時(shí)耐版,才執(zhí)行通過(guò)函數(shù)計(jì)算新 value 的操作祠够,否則不執(zhí)行;computeIfAbsent 的返回值也是與 key 相關(guān)聯(lián)的最新的 value粪牲。其默認(rèn)實(shí)現(xiàn)如下:

computeIfAbsent 的默認(rèn)實(shí)現(xiàn)

compute 不同古瓤,computeIfAbsent 接受的函數(shù)操作是 Function 而不是 BiFunction —— 這很好理解,computeIfAbsent 只在 key 不在 Map 中或者與 key 相關(guān)聯(lián)的 valuenull 時(shí)才執(zhí)行函數(shù)操作,那么顯然此時(shí)與 key 相關(guān)的 valuenull落君,所以 computeIfAbsent 只接受 Function 作為參數(shù)即可 —— 該 Function 可以使用 key 作為參數(shù)計(jì)算出新的 value穿香。使用 computeIfAbsent 改寫(xiě) getElementPositions

public static Map<String, List<Integer>> getElementPositions(List<String> list) {
    Map<String, List<Integer>> positionsMap = new HashMap<>();

    for (int i = 0; i < list.size(); i++) {
        positionsMap.computeIfAbsent(list.get(i), k -> new ArrayList<>(1)).add(i);
    }

    return positionsMap;
}

public static void main(String[] args) throws Exception {
    List<String> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");

    System.out.println("使用 computeIfAbsent:");
    Map<String, List<Integer>> elementPositions = getElementPositions(list);
    System.out.println(elementPositions);
}

運(yùn)行結(jié)果:

使用 computeIfAbsent 的運(yùn)行結(jié)果

事實(shí)上,本文使用 putIfAbsent 時(shí)是存在問(wèn)題的叽奥,positionsMap.putIfAbsent(str, new ArrayList<>(1));這句代碼每次調(diào)用時(shí)都會(huì)產(chǎn)生一個(gè)臨時(shí)的 ArrayList —— 當(dāng)遍歷的 List<String> 較大時(shí)扔水,這可能會(huì)帶來(lái)一定的負(fù)面影響;相比之下 computecomputeIfAbsent 的好處在于朝氓,它們接受的參數(shù)為函數(shù)魔市,只會(huì)在必要時(shí)才使用函數(shù)進(jìn)行計(jì)算得出新 value。在本文類(lèi)似需求的情況下赵哲,就適用性和簡(jiǎn)潔性而言待德,computeIfAbsent 要優(yōu)于 compute。在 JDK1.8 的 API 文檔中枫夺,也說(shuō)到在需要生成一個(gè)類(lèi)似于 Map<K, Collection<V>> 的結(jié)構(gòu)時(shí)将宪,computeIfAbsent 很適合這種情況:

computeIfAbsent 的 JavaDoc 文檔

compute 方法適用于什么情況呢?從前面的介紹可知橡庞,compute 方法更適用于更新 key 關(guān)聯(lián)的 value 時(shí)较坛,新值依賴(lài)于舊值的情況 —— 比如統(tǒng)計(jì)一個(gè) List<String> 中每個(gè)元素出現(xiàn)的次數(shù):

public static Map<String, Integer> getElementCounts(List<String> list) {
    Map<String, Integer> countsMap = new HashMap<>();

    list.forEach(str -> countsMap.compute(str, (k, v) -> v == null ? 1 : v + 1)); // 此時(shí):新值 = 舊值 + 1

    return countsMap;
}

public static void main(String[] args) throws Exception {
    List<String> list = Arrays.asList("a", "b", "b", "c", "c", "c", "d", "d", "d", "f", "f", "g");
    System.out.println("使用 compute 計(jì)算元素出現(xiàn)的次數(shù):");
    Map<String, Integer> counts = getElementCounts(list);
    System.out.println(counts);
}

運(yùn)行結(jié)果:

使用 compute 計(jì)算元素出現(xiàn)的次數(shù)

Java8 中還為 Map 添加了一些其他方便于編碼的新方法栈暇,請(qǐng)有興趣的讀者繼續(xù)發(fā)掘籽腕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奖地,隨后出現(xiàn)的幾起案子吧趣,更是在濱河造成了極大的恐慌法竞,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强挫,死亡現(xiàn)場(chǎng)離奇詭異岔霸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)俯渤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)呆细,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人八匠,你說(shuō)我怎么就攤上這事侦鹏。” “怎么了臀叙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵略水,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我劝萤,道長(zhǎng)渊涝,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮跨释,結(jié)果婚禮上胸私,老公的妹妹穿的比我還像新娘。我一直安慰自己鳖谈,他們只是感情好岁疼,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著缆娃,像睡著了一般捷绒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贯要,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天暖侨,我揣著相機(jī)與錄音,去河邊找鬼崇渗。 笑死字逗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宅广。 我是一名探鬼主播葫掉,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼跟狱!你這毒婦竟也來(lái)了挖息?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兽肤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后绪抛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體资铡,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年幢码,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笤休。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡症副,死狀恐怖店雅,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贞铣,我是刑警寧澤闹啦,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站辕坝,受9級(jí)特大地震影響窍奋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一琳袄、第九天 我趴在偏房一處隱蔽的房頂上張望江场。 院中可真熱鬧,春花似錦窖逗、人聲如沸址否。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)佑附。三九已至,卻和暖如春矮慕,著一層夾襖步出監(jiān)牢的瞬間帮匾,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工痴鳄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘟斜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓痪寻,卻偏偏與公主長(zhǎng)得像螺句,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橡类,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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

  • 一顾画、基本數(shù)據(jù)類(lèi)型 注釋 單行注釋?zhuān)?/ 區(qū)域注釋?zhuān)?* */ 文檔注釋?zhuān)?** */ 數(shù)值 對(duì)于byte類(lèi)型而言...
    龍貓小爺閱讀 4,267評(píng)論 0 16
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理取劫,服務(wù)發(fā)現(xiàn),斷路器研侣,智...
    卡卡羅2017閱讀 134,697評(píng)論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法谱邪,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法庶诡,繼承相關(guān)的語(yǔ)法惦银,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,662評(píng)論 18 399
  • Redis 數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)介 Redis 可以存儲(chǔ)鍵與5種不同數(shù)據(jù)結(jié)構(gòu)類(lèi)型之間的映射末誓,這5種數(shù)據(jù)結(jié)構(gòu)類(lèi)型分別為Stri...
    DreamerRzc閱讀 236,887評(píng)論 26 273
  • 相比于“搞定”大家更熟悉的是GTD也就是這本書(shū)的英文名——"Getting Things Done" GTD不是一...
    木易_1992閱讀 370評(píng)論 0 0