JDK 1.8 之 Map.merge()
程序员文章站
2023-11-22 23:58:22
Map 中 ConcurrentHashMap是线程安全的,但不是所有操作都是,例如 get() 之后再 put() 就不是了,这时使用 merge() 确保没有更新会丢失。因为Map.merge()意味着我们可以原子地执行插入或更新操作,它是线程安全的。 ......
map
中concurrenthashmap
是线程安全的,但不是所有操作都是,例如get()
之后再put()
就不是了,这时使用merge()
确保没有更新会丢失。
因为map.merge()
意味着我们可以原子地执行插入或更新操作,它是线程安全的。
一、源码解析
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; }
该方法接收三个参数,一个 key
值,一个 value
,一个 remappingfunction
。如果给定的key
不存在,它就变成了put(key, value)
;但是,如果key
已经存在一些值,我们 remappingfunction
可以选择合并的方式:
- 只返回新值即可覆盖旧值:
(old, new) -> new
; - 只需返回旧值即可保留旧值:
(old, new) -> old
; - 合并两者,例如:
(old, new) -> old + new
; - 删除旧值:
(old, new) -> null
。
二、使用场景
merge()
方法在统计时用的场景比较多,例如:有一个学生成绩对象的列表,对象包含学生姓名、科目、科目分数三个属性,求得每个学生的总成绩。
2.1 准备数据
- 学生对象
studententity.java
@data public class studententity { /** * 学生姓名 */ private string studentname; /** * 学科 */ private string subject; /** * 分数 */ private integer score; }
- 学生成绩数据
private list<studententity> buildatestlist() { list<studententity> studententitylist = new arraylist<>(); studententity studententity1 = new studententity() {{ setstudentname("张三"); setsubject("语文"); setscore(60); }}; studententity studententity2 = new studententity() {{ setstudentname("张三"); setsubject("数学"); setscore(70); }}; studententity studententity3 = new studententity() {{ setstudentname("张三"); setsubject("英语"); setscore(80); }}; studententity studententity4 = new studententity() {{ setstudentname("李四"); setsubject("语文"); setscore(85); }}; studententity studententity5 = new studententity() {{ setstudentname("李四"); setsubject("数学"); setscore(75); }}; studententity studententity6 = new studententity() {{ setstudentname("李四"); setsubject("英语"); setscore(65); }}; studententity studententity7 = new studententity() {{ setstudentname("王五"); setsubject("语文"); setscore(80); }}; studententity studententity8 = new studententity() {{ setstudentname("王五"); setsubject("数学"); setscore(85); }}; studententity studententity9 = new studententity() {{ setstudentname("王五"); setsubject("英语"); setscore(90); }}; studententitylist.add(studententity1); studententitylist.add(studententity2); studententitylist.add(studententity3); studententitylist.add(studententity4); studententitylist.add(studententity5); studententitylist.add(studententity6); studententitylist.add(studententity7); studententitylist.add(studententity8); studententitylist.add(studententity9); return studententitylist; }
2.2 一般方案
思路:用map
的一组key/value
存储一个学生的总成绩(学生姓名作为key
,总成绩为value
)
-
map
中不存在指定的key
时,将传入的value
设置为key
的值; - 当
key
存在值时,取出存在的值与当前值相加,然后放入map
中。
public void normalmethod() { long starttime = system.currenttimemillis(); // 造一个学生成绩列表 list<studententity> studententitylist = buildatestlist(); map<string, integer> studentscore = new hashmap<>(); studententitylist.foreach(studententity -> { if (studentscore.containskey(studententity.getstudentname())) { studentscore.put(studententity.getstudentname(), studentscore.get(studententity.getstudentname()) + studententity.getscore()); } else { studentscore.put(studententity.getstudentname(), studententity.getscore()); } }); log.info("各个学生成绩:{},耗时:{}ms",studentscore, system.currenttimemillis() - starttime); }
2.3 map.merge()
很明显,这里需要采用
remappingfunction
的合并方式。
public void mergemethod() { long starttime = system.currenttimemillis(); // 造一个学生成绩列表 list<studententity> studententitylist = buildatestlist(); map<string, integer> studentscore = new hashmap<>(); studententitylist.foreach(studententity -> studentscore.merge( studententity.getstudentname(), studententity.getscore(), integer::sum)); log.info("各个学生成绩:{},耗时:{}ms",studentscore, system.currenttimemillis() - starttime); }
2.4 测试及小结
- 测试方法
@test public void testall() { // 一般写法 normalmethod(); // merge()方法 mergemethod(); }
- 测试结果
00:21:28.305 [main] info cn.van.jdk.eight.map.merge.mapofmergetest - 各个学生成绩:{李四=225, 张三=210, 王五=255},耗时:75ms 00:21:28.310 [main] info cn.van.jdk.eight.map.merge.mapofmergetest - 各个学生成绩:{李四=225, 张三=210, 王五=255},耗时:2ms
- 结果小结
-
merger()
方法使用起来在一定程度上减少了代码量,使得代码更加简洁。同时,通过打印的方法耗时可以看出,merge()
方法效率更高。 -
map.merge()
的出现,和concurrenthashmap
的结合,完美处理那些自动执行插入或者更新操作的单线程安全的逻辑.