譯文:Java8 Map Enhancement

  此篇爲Jooq官方博客的一篇譯文,特此聲明。html

Java 8 的好處: Map Enhancements

這次加強的 大部分API其實是 新的 Streams API的一部分。可是一些新的特性一樣加入到 java.util.List 之中 而且最重要的是對 java.util.Map 的加強。 java

爲了保證向後兼容, 全部被加入到Interface中的方法皆爲默認方法。所以咱們在使用過程當中會有一些意外的小驚喜。編程

compute() methods

過去,咱們常獲取一個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的其餘映射關係。函數

forEach() method

這真的是一個很是好的加強,它讓你傳遞一個方法引用或者Lambda表達式去 一個一個地獲取(key, value) 對. 下面是一個嘗試性的例子:ui

map.forEach((k, v) -> 
    System.out.println(k + "=" + v));

 

輸出:this

A=1
B=2
C=3

merge() method

這個方法有點很差理解。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()來插入。
tweet this

getOrDefault()

這個函數是無腦的。對嗎?對嗎?大錯特錯!

不幸的是,有兩種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值而變得複雜。tweet this

實用建議

還有更多的方法, 像putIfAbsent() (從ConcurrentHashMap中抽離出來的),remove() (帶鍵值參數),replace()

結論

總的來講,能夠說許多原子操做已經成爲頂級的 Map API,這是好事。但同時關於null值得語義模糊加深了。  「present」 vs. 「absent」, 「contains」, 「default」 這些術語沒有對理解提供幫助, 違反了 API consistent and most importantly, regular. 所以使用新API時鍵值最好都是非null值!

 

更多關於Java 8

同時看下這裏 Eugen Paraschiv’s awesome Java 8 resources page

 

本文翻譯自:http://blog.jooq.org/2014/02/14/java-8-friday-goodies-map-enhancements/

相關文章
相關標籤/搜索