您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關Map.merge()的示例的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
在JDK的API中,這樣的一個方法它是很特別的,它很新穎,它是值得我們花時間去了解的,同時也推薦你可以運用到實際的項目代碼中,對你們應該幫助很大。Map.merge())。這可能是Map中最通用的操作。但它也相當模糊,幾乎很少人會去使用它。
背景介紹
merge() 可以解釋如下:它將新的值賦值給到key中(如果不存在)或更新具有給定值的現有key(UPSERT)。讓我們從最基本的例子開始:計算唯一的單詞出現次數。在java8之前的時候,代碼非常混亂,實際的實現其實已經失去了本質層面的設計意義。
var map = new HashMap<String, Integer>(); words.forEach(word -> { var prev = map.get(word); if (prev == null) { map.put(word, 1); } else { map.put(word, prev + 1); } });
按照上述代碼的邏輯,假設給定一個輸入集合,輸出的結果如下;
var words = List.of("Foo", "Bar", "Foo", "Buzz", "Foo", "Buzz", "Fizz", "Fizz"); //... {Bar=1, Fizz=2, Foo=3, Buzz=2}
改進V1
現在讓我們來重構它,主要去掉它的一些判斷邏輯;
words.forEach(word -> { map.putIfAbsent(word, 0); map.put(word, map.get(word) + 1); });
這樣的改進,是可以滿足我們的重構要求。putIfAbsent()的具體用法就不過多描述。putIfAbsent那一行代碼是一定需要的,否則,后面的邏輯也就會報錯。而在下面代碼中,又出現了put、get這一點會很奇怪,讓我們再繼續的進行改進設計。
words.forEach(word -> { map.putIfAbsent(word, 0); map.computeIfPresent(word, (w, prev) -> prev + 1); });
computeIfPresent是僅當 word中的的key存在的時候才調用給定的轉換。否則它什么都不處理。我們通過將key初始化為零來確保key存在,因此增量始終有效。這樣的實現是不是已經足夠完美?未必,還有其他的思路可以減少額外的初始化。
words.forEach(word -> map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1) );
compute ()就像是computeIfPresent(),但無論給定key的存在與否如何都會調用它。如果key的值不存在,則prev參數為null。將簡單移動if 到隱藏在lambda中的三元表達式也遠遠沒有達到最佳的表現。在我向你展示最終版本之前,讓我們看一下稍微簡化的默認實現Map.merge()源碼分析。
改進V3
merge()源碼
default V merge(K key, V value, BiFunction<V, V, V> 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; }
代碼片段勝過千言萬語。 閱讀源碼總是能夠發現新大陸,merge() 適用于兩種情況。如果給定的key不存在,它就變成了put(key, value)。但是,如果key已經存在一些值,我們 remappingFunction 可以選擇合并的方式。這個功能是完美契機上面的場景:
(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添加到現有值。代碼中的 one 是一個常量,因為我們的場景中,默認一直是加1,具體變化可以隨意切換。
場景
想象一下,merge()真的那么好用嗎?它的場景可以有什么?
舉一個例子。你有一個帳戶操作類
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")) );
我們希望為每個帳戶計算余額(總運營金額)。假如不用merge(),就變得非常麻煩了:
Map balances = new HashMap<String, BigDecimal>(); 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)) );
再進行優化的邏輯。
operations.forEach(op -> balances.merge(op.getAccNo(), op.getAmount(), BigDecimal::add) );
當然結果是正確的,這樣簡潔的代碼心動嗎?對于每個操作,add
在給定的amount
給定accNo
。
{ 123 = 9.5,456 = - 100 }
ConcurrentHashMap
當我們再延伸到ConcurrentHashMap來,當 Map.merge的出現,和ConcurrentHashMap的結合那是非常的完美的。這樣的搭配場景是對于那些自動執行插入或者更新操作的單線程安全的邏輯。
感謝各位的閱讀!關于Map.merge()的示例就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。