此篇爲Jooq官方博客的一篇譯文,特此聲明。html
這次加強的 大部分API其實是 新的 Streams API的一部分。可是一些新的特性一樣加入到 java.util.List
之中 而且最重要的是對 java.util.Map 的加強。
java
爲了保證向後兼容, 全部被加入到Interface中的方法皆爲默認方法。所以咱們在使用過程當中會有一些意外的小驚喜。編程
過去,咱們常獲取一個map集合的view,在view上對其作一些修改、計算而後將其從新插入到map中。若是牽扯到併發編程的話這個過程是囉嗦和困難的。可是在Java8中,咱們能夠把一個 BiFunction
傳遞給新(加入的) compute()、
computeIfAbsent()、或者
computeIfPresent()
方法中 而後讓Map實現值替換的語義。下面的例子展現了這一具體過程:api
1 // We'll be using this simple map 2 // Unfortunately, still no map literals in Java 8.. 3 Map<String, Integer> map = new HashMap<>(); 4 map.put("A", 1); 5 map.put("B", 2); 6 map.put("C", 3); 7 8 // Compute a new value for the existing key 9 System.out.println(map.compute("A", 10 (k, v) -> v == null ? 42 : v + 41)); 11 System.out.println(map); 12 13 // This will add a new (key, value) pair 14 System.out.println(map.compute("X", 15 (k, v) -> v == null ? 42 : v + 41)); 16 System.out.println(map);
上面代碼的輸出爲:併發
42 {A=42, B=2, C=3} 42 {A=42, B=2, C=3, X=42}
這些新功能對ConcurrentHashMap來講是很是有用的,
考慮到其以下的保證:app
整個的方法調用時自動完成的。當本線程更新這個map時其餘更新線程將會被阻塞,所以計算應該是短而簡潔的,同時必須禁止更新這個map的其餘映射關係。函數
這真的是一個很是好的加強,它讓你傳遞一個方法引用或者Lambda表達式去 一個一個地獲取(key, value) 對. 下面是一個嘗試性的例子:ui
map.forEach((k, v) ->
System.out.println(k + "=" + v));
輸出:this
A=1 B=2 C=3
這個方法有點很差理解。Javadoc 用到了這樣一個例子:spa
map.merge(key, msg, String::concat)
考慮到下面這條規約:
若是原來該key不存在或者對應value爲空的話,把本次計算的結果插入到map中。不然的話用新值替換掉舊值,若是新值爲null就將該null值插如集合中。
所以上面的語句能夠翻譯爲下面一系列的原子操做:
String value = map.get(key); if (value == null) map.put(key, msg); else map.put(key, value.concat(msg));
這固然不是一個經常使用功能,沒有做爲Top API. 另外,若是map中已經包含 null
值(null
值是被容許的),同時你的remappingFunction
返回 null,
那麼這條記錄將會被刪除。這個是很值得意外的。 來看下面一段程序:
map.put("X", null); System.out.println(map.merge( "X", null, (v1, v2) -> null)); System.out.println(map);
其輸出爲:
null {A=1, B=2, C=3}
更新:我第一次寫這個程序用的是 JDK 8 build 116版本。使用 build 129版本的時候狀況已經徹底不一樣了。首先,傳給merge()的值不容許爲
null。其次
null值
被 merge()
視爲缺乏值。下面的代碼產生一樣的效果:
map.put("X", 1); System.out.println(map.merge( "X", 1, (v1, v2) -> null)); System.out.println(map);
該 merge()
操做從map裏刪除了值。這多是OK的,由於若是用SQL的方式來理解,「merge」 的語義一般就是 INSERT,
UPDATE,和
DELETE
的融合。
可是map是被容許包含 null 值的,
可是不能經過 merge()來插入。
這個函數是無腦的。對嗎?對嗎?大錯特錯!
不幸的是,有兩種Maps。一種支持 null
鍵 和/或 值, 一種不支持 nulls。
先前的 merge()
並無對這兩種map進行區分, 當鍵不存在時,這個全新的 getOrDefault()
僅僅返回默認值。它沒有防止 NullPointerException
:
map.put("X", null); try { System.out.println(map.getOrDefault("X", 21) + 21); } catch (NullPointerException nope) { nope.printStackTrace(); }
這是懶漢行爲 。總的來講,Map API由於null值而變得複雜。
還有更多的方法, 像putIfAbsent()
(從ConcurrentHashMap中抽離出來的),
remove()
(帶鍵值參數),replace()。
總的來講,能夠說許多原子操做已經成爲頂級的 Map API,這是好事。但同時關於null值得語義模糊加深了。 「present」 vs. 「absent」, 「contains」, 「default」 這些術語沒有對理解提供幫助, 違反了 API consistent and most importantly, regular. 所以使用新API時鍵值最好都是非null值!
同時看下這裏 Eugen Paraschiv’s awesome Java 8 resources page
本文翻譯自:http://blog.jooq.org/2014/02/14/java-8-friday-goodies-map-enhancements/