JDK 8 - Map的深入使用(computeIfAbsent/putIfAbsent/getOrDefault/replace一系列方法的说明及用法)

前言

得益于 Java 8 的 default 方法特性,Java 8 对 Map 增加了不少实用的默认方法。
像 getOrDefault, forEach, replace(二参),replace(三参), replaceAll, putIfAbsent, remove(key, value), computeIfPresent, computeIfAbsent, compute 和merge 方法。
当然,这些方法都是在Map中的接口中的方法(非抽象),都是用default标注的方法,有自己的默认实现,当然我们一般也不用去重写它们,毕竟官方给出的方法实现还是很不错的。
而我们在对于Map一类的操作,经常会用到if(map.containsKey(key))… 这样的老套操作,为了我们能更好的装逼,我们需要熟练掌握上面的一些方法

首先你需要知道一些东西

我们在学hashmap中了解到:
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能是该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。

我们需要了解几个英文单词!
compute (v) 计算,估算,推断
absent (v) 使缺席;(adj)缺席的,缺少的
present (v) 出现,出席;(adj)现在的,出席的
put (v) 安置,赋予,移动
replace (v) 取代,替换

我们还需要了解的地方是:
Map 新增方法对 present 的判断是 map.containsKey(key) && map.get(key) != null,简单就是 map.get(key) != null,也就是即使 key 存在,但对应的值为 null 的话也视为 absent。absent 就是 map.get(key) == null。

虽然不同 Map 实现对 key/value 是否能为 null 有不同的约束, HashMap, LinkedHashMap, key 和 value 都可以为 null 值,TreeMap 的 key 为不能为 null, 但 value 可以为 null, 而 Hashtable, ConcurrentMap 则 key 和 value 都不能为 null。而我们只需要记住 absent/present 的判断是 map.get(key) 是否为 null。

另外,不了解lambda表达式的同学或者不了解函数式接口的同学可以先去网上查一查资料,示例代码多用到lambda表达式的,当然如果你不了解也没关系,了解可以帮助你更通畅的理解方法和代,不了解你大可以直接记住结论,直接用效果也不会打折扣的!

另外我还想说的一点是,我们参照的代码是Map接口中的用default声明的对应方法的代码,并不是具体的实现类的代码。
就像我们下面展示的第一个方法是
Map中的 default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
而不是HashMap中的 public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction)
在HashMap中对应功能的实现是一样的,但是具体代码的细节会有很大差异,HashMap的实现会麻烦一些,其他的如ConcurrentHashMap的compute()方法实现更加麻烦,但是比如TreeMap就没有对compute()方法再重写了,用的就是原来Map接口中的compute方法。
我在此声明这些是想说,我经验不足,现在对于HashMap中红黑树的研究还很浅薄,没办法给出对于某个类中方法的绝对严谨正确的实现(比如HashMap的compute方法),我只能简单说一说Map接口中的一些方法。
但是同名方法虽然有的实现类有具体的实现方法,但是只要记住基本思路就是Map接口中的方法的思路就是了。

接下来我们进入正题,开始看看方法吧

1.compute(key,bifunction)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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) {
if (oldValue != null || containsKey(key)) {
remove(key);
return null;
} else {
return null;
}
} else {
put(key, newValue);
return newValue;
}
}

代码解释:
先把旧值计算出来,然后通过key和旧值(oldvalue)来计算一个新的值(newvalue)(至于具体怎么计算,是写代码的人自己决定的)
然后判断我们计算出来的这个新值是否为null,
如果新值newvalue为null,有三种情况
key存在并且以前的旧值不为null
key存在并且以前的旧值为null
key值不存在
如果是前两种情况
<也就是key存在,注意:key为null不一定就代表key不存在哦,还有get(key)为null也不代表key不存在哦>,那么我们删除对应的key,返回null
如果是第三种,也就是本来key就不存在,那么我们什么也不做(本就什么也做不了),返回null
如果新值newvalue不为null
那么我们不管key存在还是不存在,当然也不管key原来对应的值是否为null
我们都把key和newvalue关联起来,写进我们的map中(当然如果不存在呢就是把k-newvalue写入map,如果存在就叫新值覆盖旧值了),返回新值newvalue
也就是执行对应的具体实现的put(key,newvalue)方法,
即HashMap找你的具体put的方法,TreeMap找你的具体的put方法,执行完之后返回新值即可。
顺便说一句,我们Map的put方法好像都是一个道理
以HashMap为例吧:HashMap的put方法,如果key值不存在,将(k-v对)安置到集合中,返回值是null,但是key值如果存在,然后用新值替换旧值,然后返回原先被替换掉的value值也就是旧的value值。

情况总结:
↓ ↓ ↓
①newvalue为null,key存在并且旧值不为null,删除key,返回null
②newvalue为null,key存在并且旧值为null,删除key,返回null
③newvalue为null,key不存在,什么也不做,返回null
④newvalue不为null,key不存在,在map中安置该(k-newvalue),返回新值
⑤newvalue不为null,key存在,会进行新值替换旧值,返回新值

不管原来的key和value是什么状态存不存在,直接根据k,v计算新值,要么(新值为null的时候)删除key返回null,要么(新值不为null的时候)强制将(k-v对)安置到map集合中,返回v(新的值)
当然你也可以把compute方法理解成put方法,只不过返回的不再是旧值而是新值
(还要记住新值为null的时候删除key返回null)

代码及结果示例:

//COMPUTE方法展示
        HashMap<String,Integer> map = new HashMap<>();
        map.put("1",1);
        map.put(null,111);
        map.put("2",null);
        map.put("3",3);
        System.out.println( "map操作前:"+       map.toString());
        System.out.println("-------------------------------------");

        //①newvalue为null,key存在并且旧值不为null,删除key,返回null
        Integer i1 = map.compute("1", (k,v) -> null);
        Integer i2 = map.compute(null, (k,v) -> null);//注意这个是key存在哦,虽然是null
        //②newvalue为null,key存在并且旧值为null,删除key,返回null
        Integer i3 = map.compute("2", (k,v) -> null);
        //③newvalue为null,key不存在,什么也不做,返回null
        Integer i4 = map.compute("5", (k,v) -> null);
        //④newvalue不为null,key不存在,在map中安置该(k-newvalue),返回新值
        //这里要说一下,key不存在那对应的value肯定也不存在,要是我们写成下面这个样子的话就肯定会报空指针异常,因为v是空啊。
        //Integer i5 = map.compute("6", (k,v) -> Integer.valueOf(k)+v );
        //因此我们的v暂时先不参与。
        Integer i5 = map.compute("6", (k,v) -> Integer.valueOf(k)+2 );
        Integer i6 = map.compute(null, (k,v) -> 222 );
        //注意,有同学可能会有疑问,这个key虽然为null,但也算是key存在不是吗,那你为什么放在key不存在下面呢
        //答案是因为我们上面执行了  Integer i2 = map.compute(null, (k,v) -> null);以后,map就把key为null的删掉了。
        //⑤newvalue不为null,key存在,会进行新值替换旧值,返回新值
        Integer i7 = map.compute("3", (k,v) -> Integer.valueOf(k)+v );

        
        System.out.println(i1);
        System.out.println(i2);
        System.out.println(i3);
        System.out.println(i4);
        System.out.println(i5);
        System.out.println(i6);
        System.out.println(i7);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{null=111, 1=1, 2=null, 3=3}
-------------------------------------
null
null
null
null
8
222
6
-------------------------------------
map操作后:{null=222, 3=6, 6=8}
Process finished with exit code 0

2.computeIfPresent(key,bifunction)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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;
}
}

代码解释:
设置一个oldvalue变量
先通过key计算value,把值赋给oldvalue并判断oldvalue是否为null
如果为null,好直接return null;
如果不为null
先通过key和旧值(oldvalue)来计算一个新的值(newvalue)
(至于具体怎么计算,是写代码的人自己决定的)
然后判断新值是否为null,有2种情况
如果新值为null,那么我们删除对应的key并且返回null
如果新值不为null,那么我们把key和newvalue关联起来,写进我们的map中
也就是执行对应实现的的put(key,newvalue)方法,
即HashMap找你的put,TreeMap找你的put。
最后返回的执行了对应put方法后的map.get(key),也就是新值newvalue。

方法名称说明:
上面的代码名称为:computeIfPresent
【如果出席的话就执行compute操作】
我们上面说过:Map 新增方法对 present 的判断是 map.containsKey(key) && map.get(key) != null,简单就是 map.get(key) != null,也就是即使 key 存在,但对应的值为 null 的话也视为 absent。absent 就是 map.get(key) == null。
present(出席)我们已经解释地很明白了,当key存在并且对应的value不为null才能看做是present
而compute操作我们也在上面刚刚讲过:
(可以看成是返回newvalue的put呀)
不管原来的key和value是什么状态存不存在,直接根据k,v计算新值,要么(新值为null的时候)删除key返回null,要么(新值不为null的时候)强制将(k-v对)安置到map集合中,返回v(新的值)

操作范围:
computeIfPresent就是执行范围比compute操作小了一些:
不再是不管key和value是什么状态都直接进行运算newvalue
而是对key和value有了限制(present)

情况总结:

↓ ↓ ↓
①key存在并且对应value存在,newvalue不为null,则会替换旧值,返回新值
②key存在并且对应value存在,newvalue为null,则会删除该key,返回null
③key不存在,方法什么也不做,返回null
④key存在但是对应的value为null,方法什么也不做,返回null

代码及结果示例:

 //COMPUTE IF PRESENT方法展示
        HashMap<String,Integer> map = new HashMap<>();
        map.put("1",1);
        map.put("2",2);
        map.put("3",null);
        System.out.println("map操作前:"+map.toString());
        
        //①key存在并且对应value存在,newvalue不为null,则会替换旧值,返回新值
        Integer integer1 = map.computeIfPresent("1", (k,v) -> Integer.valueOf(k)+v );
        //②key存在并且对应value存在,newvalue为null,则会删除该key,返回null
        Integer integer2 = map.computeIfPresent("2", (k,v) -> null );
        //③key不存在,方法什么也不做,返回null
        //注意这里的v也是null,为什么不报空指针异常,是因为方法什么也不做,根本就不会去执行bifunction功能,也当然不会发现异常了
        Integer integer3 = map.computeIfPresent("4", (k,v) -> v*3 );
        //④key存在但是对应的value为null,方法什么也不做,返回null
        Integer integer4 = map.computeIfPresent("3", (k,v) -> v*3 );

        System.out.println("-------------------------------------");
        System.out.println(integer1);
        System.out.println(integer2);
        System.out.println(integer3);
        System.out.println(integer4);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{1=1, 2=2, 3=null}
-------------------------------------
2
null
null
null
-------------------------------------
map操作后:{1=2, 3=null}

Process finished with exit code 0

3.computeIfAbsent(key,function)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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;
}

代码解释:
设置一个变量v;
将get(key)的值赋给v,判断v是否为null
如果旧值即get(key)不为null,那么什么也不做,返回get(key),也就是原来的值
如果为null
那么用key计算新值newvalue,判断新值newvalue是否为null,
如果新值为null,那么什么也不做,返回get(key),也就是原来的值
如果新值不为null,那么在map中安置该(k-newvalue),返回新值

方法名称说明:
上面的代码名称为:computeIfAbsent
方法名称已经说明问题了:
【如果缺席的话就执行compute操作】
【不过要注意,前两个方法计算了newvalue为null就会删除key】
【但是我们这个方法即使计算了newvalue为null也不会删除key值了】
absent(缺席)我们前面已经解释地很明白了,即使 key 存在,但对应的值为 null 的话也视为 absent。absent 就是 map.get(key) == null。
而compute操作我们也在上面刚刚讲过:
不管原来的key和value是什么状态存不存在,直接根据k,v计算新值,要么(新值为null的时候)删除key返回null,要么(新值不为null的时候)强制将(k-v对)安置到map集合中,返回v(新的值)

操作范围:
computeIfAbsent就是执行范围比compute操作小了一些:
不再是不管key和value是什么状态都直接进行运算newvalue
而是value有了限制,不管key为不为null,value一定要是null(absent)

情况总结:
↓ ↓ ↓
①key不存在,newvalue为null,什么也不做,返回null
②key存在但是oldvalue为null,newvalue为null,什么也不做,返回null
注意也不会去删除相应的key哦
③key不存在,newvalue不为null,在map中安置该(k-newvalue),返回新值
④key存在但是oldvalue为nul,newvalue不为null,在map中安置该
(k-newvalue),返回新值
⑤key存在,oldvalue也存在,什么都不做,返回旧值oldvalue

代码及结果示例:

  //COMPUTE IF ABSENT 方法展示
        HashMap<String,Integer> map = new HashMap<>();
        map.put("1",1);
        map.put("2",null);
        map.put("3",null);
        System.out.println("map操作前:"+map.toString());

        //①key不存在,newvalue为null,什么也不做,返回null
        Integer integer1 = map.computeIfAbsent("4", key -> null);//key存在返回value
        //②key存在但是oldvalue为null,newvalue为null,什么也不做,返回null
        //注意也不会去删除相应的key哦
        Integer integer2 = map.computeIfAbsent("2", key -> null);
        //③key不存在,newvalue不为null,在map中安置该(k-newvalue),返回新值
        Integer integer3 = map.computeIfAbsent("5", key -> new Integer(123));
        //④key存在但是oldvalue为nul,newvalue不为null,在map中安置该(k-newvalue),返回新值
        Integer integer4 = map.computeIfAbsent("3", key -> new Integer(321));
        //⑤key存在,oldvalue也存在,什么都不做,返回旧值oldvalue
        Integer integer5 = map.computeIfAbsent("1", key -> new Integer(111));
        
        System.out.println("-------------------------------------");
        System.out.println(integer1);
        System.out.println(integer2);
        System.out.println(integer3);
        System.out.println(integer4);
        System.out.println(integer5);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{1=1, 2=null, 3=null}
-------------------------------------
null
null
123
321
-------------------------------------
map操作后:{1=1, 2=null, 3=321, 5=123}

Process finished with exit code 0

4.putIfAbsent(key,function)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
default V putIfAbsent(K key, V value)
{
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}

代码解释:
用key获取旧value赋值给v变量
对v进行是否为null的判断
如果v为null,那么可以设置新的value,返回旧值
如果v不为null,那么什么都不做,返回旧值

方法名称说明:
上面的代码名称为:putIfAbsent
方法名称已经说明问题了:
【如果缺席的话就执行put操作】
absent(缺席)我们前面已经解释地很明白了,即使 key 存在,但对应的值为 null 的话也视为 absent。absent 就是 map.get(key) == null。
而put操作我们也了解:如果key值不存在,将(k-v对)安置到集合中,返回值是null,但是key值如果存在,然后用新值替换旧值,然后返回原先被替换掉的value值也就是旧的value值。

操作范围:
putIfAbsent就是执行范围比put操作小了一些:
不再是不管key和value是什么状态都直接执行将k-v对安置到map中
而是value有了限制,不管key为不为null,value一定要是null(absent)

情况总结:
↓ ↓ ↓
①key不存在,安置k-v到map中,返回旧值null
②key存在,但是oldvalue为null,用v覆盖原来的oldvalue(null),返回旧值null
③key存在,oldvalue也不为null,那么什么也不做,返回oldvalue

代码及结果示例:

//PUT IF ABSENT 方法展示
        Map<String, Integer> map = new HashMap<>();

        map.put("1",null);
        map.put("2",2);
        System.out.println("map操作前:"+map.toString());

        //①key不存在,安置k-v到map中,返回旧值null
        Integer integer1 = map.putIfAbsent("3", 3);
        //key存在,但是oldvalue为null,用v覆盖原来的oldvalue(null),返回旧值null
        Integer integer2 = map.putIfAbsent("1", 1);
        //key存在,oldvalue也不为null,那么什么也不做,返回oldvalue
        Integer integer3= map.putIfAbsent("2", 222);

        System.out.println("-------------------------------------");
        System.out.println(integer1);
        System.out.println(integer2);
        System.out.println(integer3);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{1=null, 2=2}
-------------------------------------
null
null
2
-------------------------------------
map操作后:{1=1, 2=2, 3=3}

Process finished with exit code 0

5.getOrDefault(Object key,v)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
default V getOrDefault(Object key, V defaultValue)
{
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}

代码解释:
如果这个key存在(注意是存在,key为null也可能是key存在哦),那么就返回key对应的value就可以了
如果key不存在,那么就返回(自己设置的,也是方法的参数)一个和V同类型的变量defaultValue
当然这个方法是不会影响到map集合的

方法名称说明:
上面的代码名称为:getOrDefault
方法名称已经说明问题了:
【获取值或是默认值】

操作范围:
getOrDefault其实没什么操作范围,key的存在与否就是决定返回值的关键

情况总结:
↓ ↓ ↓
①key存在,返回key对应的value
②key不存在,返回自己设置的defaultvalue

代码及结果示例:

   //getOrDefault 方法展示
        Map<String, Integer> map = new HashMap<>();
        map.put(null,null);
        map.put("1",null);
        map.put("2",2);
        System.out.println("map操作前:"+map.toString());

        //①key存在,返回key对应的value
        Integer orDefault1 = map.getOrDefault(null, 123);
        Integer orDefault2 = map.getOrDefault("1", 111);
        Integer orDefault3 = map.getOrDefault("2", 222);
        //②key不存在,返回自己设置的defaultvalue
        Integer orDefault4 = map.getOrDefault("3", 333);


        System.out.println("-------------------------------------");
        System.out.println(orDefault1);
        System.out.println(orDefault2);
        System.out.println(orDefault3);
        System.out.println(orDefault4);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{null=null, 1=null, 2=2}
-------------------------------------
null
null
2
333
-------------------------------------
map操作后:{null=null, 1=null, 2=2}

Process finished with exit code 0

6.replace(key,value)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
default V replace(K key, V value)
{
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}

代码解释:
如果这个key不存在,什么也不做,返回null
如果这个key存在(注意是存在,key为null也可能是key存在哦),不管key对应的value值是否为null
那么都用传进来的新value值覆盖原本的旧value
然后返回旧value就可以啦

方法名称说明:
上面的代码名称为:replace(二参)
方法名称已经说明问题了:
【取代(当然是取代value咯)】

操作范围:
replace(二参)的操作范围跟上面那个getOrDefault方法有点类似,key的存在与否决定了
是否取代。
key不存在,都没有对应的value,谈什么取代
key存在,就取代!

情况总结:
↓ ↓ ↓
①key不存在,什么也不做,返回null
②key存在,用自己参数中的value取代原来的旧value。返回旧value

代码及结果示例:

//replace (两参)方法
        Map<String, Integer> map = new HashMap<>();
        map.put(null,null);
        map.put("1",null);
        map.put("2",2);
        System.out.println("map操作前:"+map.toString());

        //①key不存在,什么也不做,返回null
        Integer replace1 = map.replace("3", 333);
        //②key存在,用自己参数中的value取代原来的旧value。返回旧value
        Integer replace2 = map.replace(null, 12345);
        Integer replace3 = map.replace("1", 111);
        Integer replace4 = map.replace("2", null);


        System.out.println("-------------------------------------");
        System.out.println(replace1);
        System.out.println(replace2);
        System.out.println(replace3);
        System.out.println(replace4);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{null=null, 1=null, 2=2}
-------------------------------------
null
null
null
2
-------------------------------------
map操作后:{null=12345, 1=111, 2=null}

Process finished with exit code 0

7.replace(key,oldvalue,newvalue)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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;
}

代码解释:

先说一下Objects的equals方法:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
要么 ,a和b是同一个对象。
要么,a和b在自己的类中满足a.equals(b)
否则就返回false
比如String类型就是判定内容是不是一样。
Integer类就是判定值一样不一样
具体使用看类中重写的equals方法哦。
(小提示,如果你自己写的类重写了equals()方法,一般也把hashcode()也重写了,没有坏处)

先用key获取对应的旧的value值赋给curValue变量。
再进行判断:
如果curValue变量与我们方法给的参数变量oldValue不满足 Object.equals(A,B)
那么返回false
或者如果key压根不存在也返回false
如果key存在,并且key对应的旧的Value与我们方法给的oldvalue
能满足Object.equals(A,B),那么就用第三个参数newvalue对oldvalue进行覆
然后返回true。

方法名称说明:
上面的代码名称为:replace(三参)
方法名称已经说明问题了:
【取代(当然是取代value咯)】

操作范围:
replace(三参)的操作范围与replcae(二参)不同,
key的存在与否已经起不了决定性作用了
key不存在,都没有对应的value,当然没得取代。
key单存在还不够,还必须得key计算出的value与我们的第二个参数oldvalue满足
Object.equals(A,B)才能进行取代。

情况总结:
↓ ↓ ↓
①key不存在,什么也不做,返回false
②key存在,但是计算出的value与我们的第二个参数oldvalue不满足
Object.equals(A,B),什么也不做,返回false
③key存在,并且计算出的value与我们的第二个参数oldvalue满足
Object.equals(A,B),那么就用参数中的newvalue覆盖原来的oldvalue,
返回true

代码及结果示例:

//replcae (三参)方法
        Map<String, Integer> map = new HashMap<>();
        map.put(null,null);
        map.put("1",null);
        map.put("2",2);
        System.out.println("map操作前:"+map.toString());

        //①key不存在,什么也不做,返回false
        boolean replace1 = map.replace("3", 33, 333);
        //②key存在,但是计算出的value与我们的第二个参数oldvalue不满足
        //Object.equals(A,B),什么也不做,返回false
        boolean replace2 = map.replace(null, 99, 999);
        boolean replace3 = map.replace("1", 11, 111);
        //③key存在,并且计算出的value与我们的第二个参数oldvalue满足
        //Object.equals(A,B),那么就用参数中的newvalue覆盖原来的oldvalue,返回true
        boolean replace4 = map.replace("2", 2, 222);
        
        System.out.println("-------------------------------------");
        System.out.println(replace1);
        System.out.println(replace2);
        System.out.println(replace3);
        System.out.println(replace4);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{null=null, 1=null, 2=2}
-------------------------------------
false
false
false
true
-------------------------------------
map操作后:{null=null, 1=null, 2=222}

Process finished with exit code 0

8.replaceAll(bifunction)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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);
}
}
}

代码解释:
先用entrySet()方法获得一个Set集合(集合里面放的是entry,也就是k-v对),然后遍历这个集合,遍历这些k-v对。
那每一次遍历发生了什么事情呢?
先拿到k和k对应的v,
注意在拿的过程中可能会抛出ConcurrentModificationException异常。
ConcurrentModificationException类型的异常一般都是在迭代集合的过程中该集合在结构上发生改变了,就抛出了ConcurrentModificationException异常。
具体在此我就不赘述了,想知道小伙伴,可以去了解一下:java中的fast-fail机制
fail-fast 机制:即快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中该集合在结构上发生改变的时候,就有可能会发生fail-fast,即抛出ConcurrentModificationException异常。fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大努力去抛出,所以这种机制一般仅用于检测bug。
注意,顺利地拿到了k,v之后
我们利用我们的参数function,这个function的作用是根据我们的k和旧v来返回另一个新v,然后用新v把旧v覆盖即可,当然覆盖的过程也会有fail-fast机制,也可能会抛出ConcurrentModificationException异常。这个方法什么也不返回哦。

方法名称说明:
上面的代码名称为:replaceAll(fuinction)
方法名称已经说明问题了:
【替换全部】

操作范围:
replaceAll(fuinction)没有执行范围,它会对map中的全部entry全部键值对进行遍历,然后把v替换成新的v,新的v是通过原来的k和旧的v计算出来的,至于怎么计算出来的,是写代码的人决定的,也就是function到底是怎么写的。

情况总结:
↓ ↓ ↓
没啥子情况
就是对map中的所有kv对,都根据function把旧v换成新v就是了

代码及结果示例:

//replaceAll 方法
        Map<String, Integer> map = new HashMap<>();
        map.put("1",1);
        map.put("2",2);
        map.put("3",3);
        map.put("4",4);
        map.put("5",5);

        System.out.println("map操作前:"+map.toString());

        map.replaceAll((k,v)->Integer.valueOf(k)*10+v);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{1=1, 2=2, 3=3, 4=4, 5=5}
-------------------------------------
map操作后:{1=11, 2=22, 3=33, 4=44, 5=55}

Process finished with exit code 0

9.remove(k,v)

方法原型:

注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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;
}

代码解释:
先用key获取对应的旧的value值赋给curValue变量。
再进行判断:
如果curValue变量与我们方法给的参数value不满足 Object.equals(A,B)
那么返回false
或者如果key压根不存在也返回false
如果key存在,那么就在map中把key移除,返回true

方法名称说明:
上面的代码名称为:remove
方法名称已经说明问题了:
【移除(当然是移除key咯)】

操作范围:
remove的操作也是key的存在与否很重要,但是不起决定性作用
key存在还不够,还必须得key计算出的value与我们的第二个参数value满足
Object.equals(A,B)才能进行移除

情况总结:
↓ ↓ ↓
①key不存在,什么也不做,返回false
②key存在,但是计算出的value与我们的参数value不满足
Object.equals(A,B),什么也不做,返回false
③key存在,并且计算出的value与我们的参数value满足
Object.equals(A,B),那么就删除map中对应的key,返回true

代码及结果示例:

  //remove 方法
        Map<String, Integer> map = new HashMap<>();
        map.put(null,null);
        map.put("1",null);
        map.put("2",2);
        map.put("3",3);
        System.out.println("map操作前:"+map.toString());

        //①key不存在,什么也不做,返回false
        boolean remove1 = map.remove("4", 444);
        //②key存在,但是计算出的value与我们的参数value不满足
        //Object.equals(A,B),什么也不做,返回false
        boolean remove2 = map.remove("1", 111);
        boolean remove3 = map.remove("2", 222);
        boolean remove4 = map.remove(null,123);

        //③key存在,并且计算出的value与我们的参数value满足
        //Object.equals(A,B),那么就删除map中对应的key,返回true
        boolean remove5 = map.remove(null, null);
        boolean remove6 = map.remove("3", 3);

        System.out.println("-------------------------------------");
        System.out.println(remove1);
        System.out.println(remove2);
        System.out.println(remove3);
        System.out.println(remove4);
        System.out.println(remove5);
        System.out.println(remove6);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{null=null, 1=null, 2=2, 3=3}
-------------------------------------
false
false
false
false
true
true
-------------------------------------
map操作后:{1=null, 2=2}

Process finished with exit code 0

10.merge(k,v,bifunction)

方法原型:
注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
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;
}

代码解释:
注意一个地方,传入的参数value不能为null哦(Objects.requireNonNull(value))
先用key获取对应的旧的value值赋给oldValue变量。
然后给newvalue赋值,如何得到这个值呢?
如果oldvalue为null,就将我们的参数value赋给newvalue
如果oldvalue不为null,就调用function功能——利用oldvalue和参数value
来获取一个新的值赋给newvalue。
得到这个newvalue以后,
如果newvaue为null,就在map中删除这个key,返回null
如果newvalue不为null,就用newvalue来覆盖oldvalue,返回newvalue

方法名称说明:
上面的代码名称为:merge
方法名意为合并,吞并。。其实我也不知道这个起这个名字有什么意义。。。

操作范围:
merge方法也没有操作范围,即不论key和value存不存在,方法内部都有对应的操作。

情况总结:
↓ ↓ ↓
①key不存在,参数value赋给newvalue(参数value不能为null哦,方法中声明了的),在map中安置该k-value,返回参数value的值
②key存在,但是oldvalue为null,参数value赋给newvalue(参数value不能为null哦,方法中声明了的),参数value覆盖newvalue,返回参数value的值
③key存在,oldvalue不为null,然后调用function功能——利用oldvalue和参数value获取了一个新值赋给newvalue,newvalue为null,删除key,返回null
④key存在,oldvalue不为null,然后调用function功能——利用oldvalue和参数value获取了一个新值赋给newvalue,newvalue不为null,用newvalue覆盖oldvalue,返回newvalue

代码及结果示例:

//merge 方法
        Map<String, Integer> map = new HashMap<>();
        map.put(null,null);
        map.put("1",null);
        map.put("2",2);
        map.put("3",3);
        System.out.println("map操作前:"+map.toString());

        //①key不存在,参数value赋给newvalue(参数value不能为null哦,方法中声明了的)
        //在map中安置该k-value,返回参数value的值
        Integer merge1 = map.merge("4", 444, (oldv, v) -> oldv + v);
        //②key存在,但是oldvalue为null,参数value赋给newvalue(参数value不能为null哦,方法中声明了的)
        //参数value覆盖newvalue,返回参数value的值
        Integer merge2 = map.merge("1", 111, (oldv, v) -> oldv + v);
        Integer merge3 = map.merge(null, 123, (oldv, v) -> oldv + v);
        //③key存在,oldvalue不为null,然后调用function功能——利用oldvalue和参数value获取了一个新值赋给newvalue
        //newvalue为null,删除key,返回null
        Integer merge4 = map.merge("2", 111, (oldv, v) -> null);
        //④key存在,oldvalue不为null,然后调用function功能——利用oldvalue和参数value获取了一个新值赋给newvalue
        //newvalue不为null,用newvalue覆盖oldvalue,返回newvalue
        Integer merge5 = map.merge("3", 333, (oldv, v) -> oldv+v);
        
        System.out.println("-------------------------------------");
        System.out.println(merge1);
        System.out.println(merge2);
        System.out.println(merge3);
        System.out.println(merge4);
        System.out.println(merge5);
        System.out.println("-------------------------------------");
        System.out.println("map操作后:"+map.toString());

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前:{null=null, 1=null, 2=2, 3=3}
-------------------------------------
444
111
123
null
336
-------------------------------------
map操作后:{null=123, 1=111, 3=336, 4=444}

Process finished with exit code 0


11.forEach(biconsumer)

方法原型:

注意,下面的是Map接口中的方法,而不是Map的具体实现类中重写的。
default void forEach(BiConsumer<? super K, ? super V> action)
{
Objects.requireNonNull(action);
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);
}
action.accept(k, v);
}
}

代码解释:
先用entrySet()方法获得一个Set集合(集合里面放的是entry,也就是k-v对),然后用for方法遍历这个集合,遍历这些k-v对。
那每一次遍历发生了什么事情呢?
先拿到k和k对应的v,
注意在拿的过程中还是和上面那个replaceAll方法一样可能会抛出ConcurrentModificationException异常,即fail-fast机制会起作用哦。
注意,顺利地拿到了k,v之后
我们利用我们的参数consumer,这个consumer的作用是根据我们的k和旧v来
干一些事情,总之没有返回值就是了,具体干了什么事情,大概率是System.out.println(。。。),用自己的方式遍历一个个键值对entry。

方法名称说明:
上面的代码名称为:forEach(biconsumer)
方法名称已经说明问题了:
【为每一个(entry)(设置如何输出…)】

操作范围:
forEach(biconsumer)也没有执行范围,它会对map中的全部entry全部键值对进行遍历,然后按自己设置的方式进行打印!至于怎么打印,就看biconsumer功能是怎么写的了

情况总结:
↓ ↓ ↓
也没啥子情况
就是把map中的所有kv对,都按自己设置的方式进行打印就是了。

代码及结果示例:

//forEach 方法
        Map<String, Integer> map = new HashMap<>();
        map.put(null,null);
        map.put("1",null);
        map.put("2",2);
        map.put("3",3);
        System.out.println("map操作前用的老旧打印输出方式toString:"+map.toString());


        System.out.println("-------------------------------------");
        System.out.println("对map操作后:");
        map.forEach((k,v)-> System.out.println(k+"---"+v));

运行结果如下:

F:\JDK\Java\jdk1.8.0_161\bin\java.exe ...
map操作前用的老旧打印输出方式toString:{null=null, 1=null, 2=2, 3=3}
-------------------------------------
对map操作后:
null---null
1---null
2---2
3---3

Process finished with exit code 0


补充以及我想说的话

1.对于我们上面的方法中经常会有计算出的值为 null 时直接删除 key 而不是设置对应 key 的值为 null,我想是有原因的:最起码这能照顾到值不能为 null 的 Map 实现,如 Hashtable 和 ConcurrentMap。

2.以上方法给出的顺序是随意的并不代表重要程度,但是一定要明白,经常用的只有其中的个别方法

3.很遗憾我没有为大家准备每个方法的应用场景,因为我也是新了解到才写上去的,对这些方法掌握的很少。大家看完之后,一定要仔细研究一下自己需要的方法的应用场景

我举一个很简单的例子:

假设我们定义下面一个 map和list:
Map<String, List> map = new HashMap<>();
List list=new ArrayList();
list.add(“1”);
list.add(“2”);
list.add(“3”);
list.add(“4”);
list.add(“5”);
如果我们想要把list放到key为list-9527的"格子"里面,大家肯定都不会直接写
list.put(“list-9527”,list);
因为万一人家list-9527对应的格子里面有东西了呢?那么岂不是把人家的东西覆盖了?这样很不好鸭。
那很多人会这么写:
List listxxx = map.get(“list-9527”);
if (listxxx == null) {
map.put(“list-9527”, list);
}

然后突然脑子抽筋,还想在list里加一个A
list.add(“A”);就行了
而这个时候我们有了 computeIfAbsent() 方法
这面这写东西就可以写成一行:
map.computeIfAbsent(“list-9527”, k -> list).add(“A”);
炒鸡方便有没有?
因此我们不能单会方法,懂方法,更要会用方法,懂怎么用方法,这才最重要。
再次抱歉我没能总结出来方法的使用场景,对不住鸭。

4.对于文章中的盲点,比如你不熟Lambda表达式,不了解fail-fast快速失败机制等这些东西都是很重要的。要抓紧时间补上

5.这也算是我开CSDN账号的第一篇学习博文,真的全都是我手敲的,写的不好或者错了的地方希望的大家指正,我会继续努力的,同时觉得不错的话也希望你们能鼓励一下我给我点个赞,谢谢啦

6.最后还是想感谢你们能点开并且观看到最后,谢谢鸭

借鉴文章:https://www.cnblogs.com/sohuhome/p/10094772.html

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值