专栏首页呼延Map接口在1.8版本新增的几个方法

Map接口在1.8版本新增的几个方法

前言

Map接口在1.8版本新增以下几个有趣的方法,今天参考源码来学习一下.

  • getOrDefault
  • replaceAll
  • putIfAbsent
  • remove
  • replace
  • computeIfAbsent
  • computeIfPresent
  • compute
  • merge

V getOrDefault(Object key, V defaultValue)

这可以说是最常用的方法了吧,获取指定key的value,当key不存在的时候返回一个默认值,也就是第二个参数.

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }

void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)

将所有value替换成给定lambda的计算结果,lambda的作用为根据key和value算出新的value.

源代码如下:

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }

示例代码如下:

    @Test
    public void test1() {
        Map<Integer, Integer> test = new HashMap<>();
        test.put(1, 1);
        test.put(2, 2);
        System.out.println(test.toString());

        test.replaceAll((k, v) -> k + v);
        System.out.println(test.toString());
    }

这段代码中传递了一个lambda,作用是将key和value相加作为新的value.

输出结果如下:

{1=1, 2=2}
{1=2, 2=4}

V putIfAbsent(K key, V value)

当key不存在的时候,写入新值.始终返回执行操作后的新值.

源代码如下:

default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }

        return v;
    }

测试代码及输出如下:

    @Test
    public void test2() {
        Map<Integer, Integer> test = new HashMap<>();
        test.put(1, 1);
        test.put(2, 2);
        System.out.println(test.toString());

        test.putIfAbsent(1, 3);
        test.putIfAbsent(3, 3);
        System.out.println(test.toString());
    }
------------------------
{1=1, 2=2}
{1=1, 2=2, 3=3}

boolean remove(Object key, Object value)

如果给定的key在map中的value与给定值相等,则移除并且返回true,否则返回false.

default boolean remove(Object key, Object value) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }

replace

这个有两个重载方法.

default boolean replace(K key, V oldValue, V newValue)

当key在map中的value与给定的oldValue相等,则用newValue替换掉并且返回true,否则返回false.

default boolean replace(K key, V oldValue, V newValue) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, oldValue) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        put(key, newValue);
        return true;
    }

V replace(K key, V value)

当key存在,就替换掉并且返回新值,否则返回null.

default V replace(K key, V value) {
        V curValue;
        if (((curValue = get(key)) != null) || containsKey(key)) {
            curValue = put(key, value);
        }
        return curValue;
    }

V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

如果key不存在,则使用lambda计算并写入新值.永远返回执行操作后的新值.(可以存在,不做任何操作);放回计算的新值.

这个方法可以为一些耗时或者耗资源的操作构建本地缓存,当元素存在时直接返回,当不存在的时候进行耗时进行并存储,下一次可以直接返回.

default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }

测试代码及输出如下:

    @Test
    public void test3() {
        Map<Integer, Integer> test = new HashMap<>();
        test.put(1, 1);
        test.put(2, 2);
        System.out.println(test.toString());

        // 1 存在,不做任何操作
        test.computeIfAbsent(1, key -> key + 2);
        // 3 不存在,将3 +2 = 5.
        test.computeIfAbsent(3, key -> key + 2);
        System.out.println(test.toString());
    }

------------------------
{1=1, 2=2}
{1=1, 2=2, 3=5}

V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

当key存在时,计算新值,如果新值不为空,则将新值写入,如果新值为空,则移除掉此key.返回新值或者null.

default V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        if ((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue);
            if (newValue != null) {
                put(key, newValue);
                return newValue;
            } else {
                remove(key);
                return null;
            }
        } else {
            return null;
        }
    }

测试代码及输出如下:

@Test
    public void test4() {
        Map<Integer, Integer> test = new HashMap<>();
        test.put(1, 1);
        test.put(2, 2);
        System.out.println(test.toString());

        // 1 存在,计算
        test.computeIfPresent(1, (key, oldValue) -> key + oldValue + 2);
        // 3 不存在,不作操作
        test.computeIfPresent(3, (key, oldValue) -> key + oldValue +  2);
        System.out.println(test.toString());
    }
-------------------------------
{1=1, 2=2}
{1=4, 2=2}

这个方法基本上是上一个方法的存在版本,但是要注意传入的lambda,参数是两个,computeIfAbsent的lambda传入key,计算值. 而computeIfPresent传入key和旧的value,并且由他们两个计算得到新的值.

V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

直接计算新值,新值为空,则删除key并且返回null,新值不为空则写入并且返回新值.

default V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue = get(key);

        V newValue = remappingFunction.apply(key, oldValue);
        if (newValue == null) {
            // delete mapping
            if (oldValue != null || containsKey(key)) {
                // something to remove
                remove(key);
                return null;
            } else {
                // nothing to do. Leave things as they were.
                return null;
            }
        } else {
            // add or replace old mapping
            put(key, newValue);
            return newValue;
        }
    }

测试代码及输出如下:

 @Test
    public void test5() {
        Map<Integer, Integer> test = new HashMap<>();
        test.put(1, 1);
        test.put(2, 2);
        System.out.println(test.toString());

        test.compute(1, (key, oldValue) -> key + 2);
        test.compute(3, (key, oldValue) -> key + 2);
        test.compute(2, (key, oldValue) -> null);
        System.out.println(test.toString());
    }
----------------
{1=1, 2=2}
{1=3, 3=5}

测试代码中对2进行compute->null,结果是2被删除.

V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)

使用旧值和给定的value来计算新值,如果新值为空,则删除key,不为空则写入并且返回.

注意:如果旧值为空,也就是原有的key不存在,新值等于给定值,不会再进行计算.因此下方的测试代码3=10而不是12.

default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }

测试代码及输出如下:

    @Test
    public void test6() {
        Map<Integer, Integer> test = new HashMap<>();
        test.put(1, 1);
        test.put(2, 2);
        System.out.println(test.toString());

        test.merge(1, 10, (v, oldV) -> v + oldV + 2);
        test.merge(3, 10, (v, oldV) -> v + oldV + 2);
        test.merge(2, 10, (v, oldV) -> null);
        System.out.println(test.toString());
    }
--------------------------
{1=1, 2=2}
{1=13, 3=10}

总结

其实看过源码就可以发现,除了Function,BiFunction等函数式接口(也就是用于lambda)的接口是新声明的,其他调用的API都是原先已有的put,get,contain等常用API,因此这些新的方法并不能算是很难用的新功能,只能算是一些免去开发人员重复工作的语法糖,我们当然要多多享受语法糖带来的便利,但是不能忘却原理,要多多熟悉再使用.

完。

ChangeLog

2019-05-13 完成

以上皆为个人所思所得,如有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文链接。

联系邮箱:huyanshi2580@gmail.com

更多学习笔记见个人博客——>呼延十

var gitment = new Gitment({ id: 'Map接口在1.8版本新增的几个方法', // 可选。默认为 location.href owner: 'hublanker', repo: 'blog', oauth: { client_id: '2297651c181f632a31db', client_secret: 'a62f60d8da404586acc965a2ba6a6da9f053703b', }, }) gitment.render('container')



本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Redis系列(十一)redis命令全集

    总的来说,Redis 是一个基于内存的高性能的键值型数据库,也就是常说的 NoSQL, 可以用来作为数据库或者缓存。并且支持多种数据结构,包括字符串,散列,列表...

    呼延十
  • Redis命令全集

    总的来说,Redis是一个基于内存的高性能的键值型数据库,也就是常说的NoSQL,可以用来作为数据库或者缓存.并且支持多种数据结构,包括字符串,散列,列表,集合...

    呼延十
  • Redis系列(十三)应用之分布式锁

    分布式锁有着多种多样的实现方式,今天就来介绍一下 如何用 Redis 实现一个分布式锁。

    呼延十
  • 基于python的Json容错数据自动化输出

    测试工作中往往需要对服务端所返回的Json数据做容错,即需要确保原数据中各项值被替换成异常数据类型时,相关数据传输与处理系统不会发生报错、崩溃等问题。

    用户5521279
  • python|Python中的dict

    1、格式:{key1:value1,key2:value2,key3:value3,….. } ;

    算法与编程之美
  • iOS下载报错:App Transport Security has blocked a cleartext HTTP

    以上方法虽然解决了HTTP不能正常使用的问题,但是苹果提供的安全保障也被关闭了,对于不支持HTTPS协议的网站,可以考虑白名单:

    陈满iOS
  • redis常规命令记录

    概述各类型存储命令介绍字符串字符串操作字符串批量操作字符串位操作计数操作列表集合有序集合哈希HyperLogLog通用命令

    烟草的香味
  • redis初识~String命令介绍

    用户2196435
  • Redis常用命令、5种数据类型的内部编码实现以及实用场景

    相信绝大部分人,应该是99%的人都知道Redis的5种的基本类型、它们分别是:字符串、哈希、列表、集合、有序集合,就如同下图这样:

    Java学习录
  • Spring-LogBack笔记(1) - 基础使用

    TRACE < DEBUG < INFO < WARN < ERROR < FATAL

    yingzi_code

扫码关注云+社区

领取腾讯云代金券