译文: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/

相关文章
相关标签/搜索