Map接口在1.8版本新增以下几个有趣的方法,今天参考源码来学习一下.
这可以说是最常用的方法了吧,获取指定key的value,当key不存在的时候返回一个默认值,也就是第二个参数.
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
将所有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}
当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}
如果给定的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;
}
这个有两个重载方法.
当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;
}
当key存在,就替换掉并且返回新值,否则返回null.
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
如果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}
当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,并且由他们两个计算得到新的值.
直接计算新值,新值为空,则删除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被删除.
使用旧值和给定的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,因此这些新的方法并不能算是很难用的新功能,只能算是一些免去开发人员重复工作的语法糖,我们当然要多多享受语法糖带来的便利,但是不能忘却原理,要多多熟悉再使用.
完。
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')