译文:Java8 Map Enhancement

  此篇为Jooq官方博客的一篇译文,特此声明。

Java 8 的好处: Map Enhancements

此次增强的 大部分API实际上是 新的Streams API的一部分。但是一些新的特性同样加入到 java.util.List 之中 并且最重要的是对 java.util.Map 的增强。

为了保证向后兼容, 所有被加入到Interface中的方法皆为默认方法。因此我们在使用过程中会有一些意外的小惊喜。

compute() methods

过去,我们常获取一个map集合的view,在view上对其做一些修改、计算然后将其重新插入到map中。如果牵扯到并发编程的话这个过程是啰嗦和困难的。但是在Java8中,我们可以把一个 BiFunction 传递给新(加入的) compute()computeIfAbsent()、或者 computeIfPresent() 方法中 然后让Map实现值替换的语义。下面的例子展示了这一具体过程:

 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来说是非常有用的, 考虑到其如下的保证:

整个的方法调用时自动完成的。当本线程更新这个map时其他更新线程将会被阻塞,因此计算应该是短而简洁的,同时必须禁止更新这个map的其他映射关系。

forEach() method

这真的是一个非常好的增强,它让你传递一个方法引用或者Lambda表达式去 一个一个地获取(key, value) 对. 下面是一个尝试性的例子:

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

输出:

A=1
B=2
C=3

merge() method

这个方法有点不好理解。Javadoc 用到了这样一个例子:

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()来插入。

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值而变得复杂。

实用建议

还有更多的方法, 像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/

译文:Java8 Map Enhancement

时间: 2024-10-06 00:22:12

译文:Java8 Map Enhancement的相关文章

java8 Map的一些简单使用

private static Map<String, Integer> newMap = new HashMap<String, Integer>(); public static void main(String[] args) { newMap.put("hadoop", 100); newMap.put("spark", 50); newMap.put("java", 80); newMap.put("my

【java代码之美】---Java8 Map中的computeIfAbsent方法

Map中的computeIfAbsent方法 Map接口的实现类如HashMap,ConcurrentHashMap,HashTable等继承了此方法,通过此方法可以在特定需求下,让你的代码更加简洁. 一.案例说明 1.概述 在JAVA8的Map接口中,增加了一个方法computeIfAbsent,此方法签名如下: public V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 此方法首先判断缓存

Java8 map和reduce

map final List<Integer> numbers = Arrays.asList(1, 2, 3, 4); final List<Integer> doubleNumbers = numbers.stream() .map(number -> number * 2) .collect(Collectors.toList()); 结果:[2, 4, 6, 8] 也可以搞成其他的类型,初始List是Integer,也可以变成String final List<

java8 map flatmap

map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素.这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble.这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型.之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗: map方法示意图: flatMap:和map类似,不同的是其每个元素转换得

java 8 map遍历方式 (转)

public class LambdaMap { private Map<String, Object> map = new HashMap<>(); @Before public void initData() { map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3&q

Java8-Map

1.Staff实体 public class Staff { private String name; private int age; private String address; public Staff() { } public Staff(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } public String getName() {

Java8之Stream/Map

本篇用代码示例结合JDk源码讲了Java8引入的工具接口Stream以及新Map接口提供的常用默认方法.    参考:http://winterbe.com/posts/2014/03/16/java-8-tutorial/ 1.Stream示例 package com.mavsplus.java8.turtorial.streams; import java.util.ArrayList; import java.util.List; import java.util.Optional; im

Java8遍历Map

                                                    第一篇博客啦~~~ 今天在上海的一个小伙伴面试,面试官问了一个关于Java8的面试题,问题如下: 如何用Java8的语法实现: List<Map<String,String>> list = new ArrayList<Map<String,String>>(); 将这个List里面的所有map的值全部都修改为 他的名字 ('瓜皮'); 首先该同学思考的是

Java8增强的Map集合

Map集合简介 Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组用于保存Map里的vlaue,key和value都可以是任何引用类型的数据. Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false. key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的.确定的value.从Map中取出数据时,只要给出指定的key,就可以取出对应的value. 如果把Map里的所有key