亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

詳解Map.merge()

Ocean / 1200人閱讀

摘要:今天介紹的方法,讓我們來看看它的強(qiáng)大之處。這可能是中最通用的操作。我們通過將初始化為零來確保存在,因此增量始終有效。這樣的搭配場景是對于那些自動(dòng)執(zhí)行插入或者更新操作的單線程安全的邏輯。

今天介紹Map的merge方法,讓我們來看看它的強(qiáng)大之處。

在JDK的API中,這樣的一個(gè)方法它是很特別的,它很新穎,它是值得我們花時(shí)間去了解的,同時(shí)也推薦你可以運(yùn)用到實(shí)際的項(xiàng)目代碼中,對你們應(yīng)該幫助很大。Map.merge())。這可能是Map中最通用的操作。但它也相當(dāng)模糊,幾乎很少人會(huì)去使用它。

背景介紹

merge() 可以解釋如下:它將新的值賦值給到key中(如果不存在)或更新具有給定值的現(xiàn)有key(UPSERT)。讓我們從最基本的例子開始:計(jì)算唯一的單詞出現(xiàn)次數(shù)。在java8之前的時(shí)候,代碼非?;靵y,實(shí)際的實(shí)現(xiàn)其實(shí)已經(jīng)失去了本質(zhì)層面的設(shè)計(jì)意義。

var map = new HashMap();
words.forEach(word -> {
    var prev = map.get(word);
    if (prev == null) {
        map.put(word, 1);
    } else {
        map.put(word, prev + 1);
    }
});

按照上述代碼的邏輯,假設(shè)給定一個(gè)輸入集合,輸出的結(jié)果如下;

var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz");
//...
{Bar=1, Fizz=2, Foo=3, Buzz=2}
改進(jìn)V1

現(xiàn)在讓我們來重構(gòu)它,主要去掉它的一些判斷邏輯;

words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.put(word, map.get(word) + 1);
});

這樣的改進(jìn),是可以滿足我們的重構(gòu)要求。putIfAbsent()的具體用法就不過多描述。putIfAbsent那一行代碼是一定需要的,否則,后面的邏輯也就會(huì)報(bào)錯(cuò)。而在下面代碼中,又出現(xiàn)了putget這一點(diǎn)會(huì)很奇怪,讓我們再繼續(xù)的進(jìn)行改進(jìn)設(shè)計(jì)。

改進(jìn)V2
words.forEach(word -> {
    map.putIfAbsent(word, 0);
    map.computeIfPresent(word, (w, prev) -> prev + 1);
});

computeIfPresent是僅當(dāng) word中的的key存在的時(shí)候才調(diào)用給定的轉(zhuǎn)換。否則它什么都不處理。我們通過將key初始化為零來確保key存在,因此增量始終有效。這樣的實(shí)現(xiàn)是不是已經(jīng)足夠完美?未必,還有其他的思路可以減少額外的初始化。

words.forEach(word ->
        map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);

compute ()就像是computeIfPresent(),但無論給定key的存在與否如何都會(huì)調(diào)用它。如果key的值不存在,則prev參數(shù)為null。將簡單移動(dòng)if 到隱藏在lambda中的三元表達(dá)式也遠(yuǎn)遠(yuǎn)沒有達(dá)到最佳的表現(xiàn)。在我向你展示最終版本之前,讓我們看一下稍微簡化的默認(rèn)實(shí)現(xiàn)Map.merge()源碼分析。

改進(jìn)V3
merge()源碼
default V merge(K key, V value, BiFunction remappingFunction) {
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    if (newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

代碼片段勝過千言萬語。 閱讀源碼總是能夠發(fā)現(xiàn)新大陸,merge() 適用于兩種情況。如果給定的key不存在,它就變成了put(key, value)。但是,如果key已經(jīng)存在一些值,我們 remappingFunction 可以選擇合并的方式。這個(gè)功能是完美契機(jī)上面的場景:

只需返回新值即可覆蓋舊值: (old, new) -> new

只需返回舊值即可保留舊值: (old, new) -> old

以某種方式合并兩者,例如: (old, new) -> old + new

甚至刪除舊值: (old, new) -> null

如你所見,它 merge() 是非常通用的。那么,我們的問題該如何使用merge()呢?代碼如下:

words.forEach(word ->
        map.merge(word, 1, (prev, one) -> prev + one)
);

你可以按照如下思路理解:如果沒有key,那么初始化的value等于1;否則,將1添加到現(xiàn)有值。代碼中的 one 是一個(gè)常量,因?yàn)槲覀兊膱鼍爸?,默認(rèn)一直是加1,具體變化可以隨意切換。

場景
想象一下,merge()真的那么好用嗎?它的場景可以有什么?

舉一個(gè)例子。你有一個(gè)帳戶操作類

class Operation {
    private final String accNo;
    private final BigDecimal amount;
}

以及針對不同帳戶的一系列操作:

operations = List.of(
    new Operation("123", new BigDecimal("10")),
    new Operation("456", new BigDecimal("1200")),
    new Operation("123", new BigDecimal("-4")),
    new Operation("123", new BigDecimal("8")),
    new Operation("456", new BigDecimal("800")),
    new Operation("456", new BigDecimal("-1500")),
    new Operation("123", new BigDecimal("2")),
    new Operation("123", new BigDecimal("-6.5")),
    new Operation("456", new BigDecimal("-600"))
);

我們希望為每個(gè)帳戶計(jì)算余額(總運(yùn)營金額)。假如不用merge(),就變得非常麻煩了:

Map balances = new HashMap();
operations.forEach(op -> {
    var key = op.getAccNo();
    balances.putIfAbsent(key, BigDecimal.ZERO);
    balances.computeIfPresent(key, (accNo, prev) -> prev.add(op.getAmount()));
});

使用merge之后的代碼

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), 
                (soFar, amount) -> soFar.add(amount))
);

再進(jìn)行優(yōu)化的邏輯。

operations.forEach(op ->
        balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add)
);

當(dāng)然結(jié)果是正確的,這樣簡潔的代碼心動(dòng)嗎?對于每個(gè)操作,add在給定的amount給定accNo。

{ 123 = 9.5,456 = - 100 }
ConcurrentHashMap

當(dāng)我們再延伸到ConcurrentHashMap來,當(dāng) Map.merge的出現(xiàn),和ConcurrentHashMap的結(jié)合那是非常的完美的。這樣的搭配場景是對于那些自動(dòng)執(zhí)行插入或者更新操作的單線程安全的邏輯。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/73832.html

相關(guān)文章

  • java集合-Map

    摘要:增強(qiáng)的集合都可以是任何引用類型的數(shù)據(jù),的不允許重復(fù)即同一個(gè)對象的任何兩個(gè)通過方法比較總是返回。的這些實(shí)現(xiàn)類和子接口中集的存儲(chǔ)形式和對應(yīng)集合中元素的存儲(chǔ)形式完全相同。根據(jù)的自然順序,即枚舉值的定義順序,來維護(hù)對的順序。 Java8增強(qiáng)的Map集合 Key-value都可以是任何引用類型的數(shù)據(jù),Map的Key不允許重復(fù)即同一個(gè)Map對象的任何兩個(gè)key通過equals方法比較總是返回...

    Little_XM 評論0 收藏0
  • Java 8 并發(fā)教程:原子變量和 ConcurrentMa

    摘要:并發(fā)教程原子變量和原文譯者飛龍協(xié)議歡迎閱讀我的多線程編程系列教程的第三部分。如果你能夠在多線程中同時(shí)且安全地執(zhí)行某個(gè)操作,而不需要關(guān)鍵字或上一章中的鎖,那么這個(gè)操作就是原子的。當(dāng)多線程的更新比讀取更頻繁時(shí),這個(gè)類通常比原子數(shù)值類性能更好。 Java 8 并發(fā)教程:原子變量和 ConcurrentMap 原文:Java 8 Concurrency Tutorial: Synchroni...

    bitkylin 評論0 收藏0
  • Java實(shí)戰(zhàn)之Java8指南

    摘要:首先我們定義一個(gè)有兩個(gè)不同控制器的然后,我們創(chuàng)建一個(gè)特定的工廠接口來創(chuàng)建新的對象不需要手動(dòng)的去繼承實(shí)現(xiàn)該工廠接口,我們只需要將控制器的引用傳遞給該接口對象就好了的控制器會(huì)自動(dòng)選擇合適的構(gòu)造器方法。這種指向時(shí)間軸的對象即是類。 本文為翻譯文章,原文地址 這里 歡迎來到本人對于Java 8的系列介紹教程,本教程會(huì)引導(dǎo)你一步步領(lǐng)略最新的語法特性。通過一些簡單的代碼示例你即可以學(xué)到默認(rèn)的接口方...

    nemo 評論0 收藏0
  • React系列之 Immutable

    摘要:原文地址什么是是指一旦被創(chuàng)建就不可以被改變的數(shù)據(jù),通過使用不可變數(shù)據(jù)可以讓我們很方便的去處理數(shù)據(jù)的狀態(tài)變化檢測等問題,而且讓我們的程序變得更加的可預(yù)見怎么用大體使用深度轉(zhuǎn)換和為和淺轉(zhuǎn)換給倒數(shù)第一個(gè)賦值更多可以查看這里為什么要用其實(shí)從上面 原文地址:https://gmiam.com/post/react-... 什么是 Immutable Data ? Immutable Data 是...

    cc17 評論0 收藏0
  • 多線程

    摘要:線程啟動(dòng)后系統(tǒng)就自動(dòng)調(diào)用方法。守護(hù)線程的使用必須在之前設(shè)置,否則會(huì)跑出一個(gè)異常。你不能把正在運(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程。在線程中產(chǎn)生的新線程也是的。線程的同步控制使用方法可以釋放對象鎖,使用或可以讓等待的一個(gè)或所有線程進(jìn)入就緒狀態(tài)。 線程的創(chuàng)建 線程:程序中單個(gè)順序的流控制稱為線程 一個(gè)進(jìn)程中可以含有多個(gè)線程 在操作系統(tǒng)中可以查看線程數(shù) 如:在Windows中,在任務(wù)管理器,右鍵,...

    Scholer 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<