java8之在Map中新增的好用api
创始人
2025-06-01 00:54:48
0

java8之后,常用的Map接口中添加了一些非常实用的函数,可以大大简化一些特定场景的代码编写,提升代码可读性。

computeIfAbsent函数

比如,很多时候我们需要对数据进行分组,变成Map>的形式,在java8之前,一般如下实现:

List payments = getPayments();
Map> paymentByTypeMap = new HashMap<>();
for(Payment payment : payments){if(!paymentByTypeMap.containsKey(payment.getPayTypeId())){paymentByTypeMap.put(payment.getPayTypeId(), new ArrayList<>());}paymentByTypeMap.get(payment.getPayTypeId()).add(payment);
}

可以发现仅仅做一个分组操作,代码却需要考虑得比较细致,在Map中无相应值时需要先塞一个空List进去。

但如果使用java8提供的computeIfAbsent方法,代码则会简化很多,如下:

List payments = getPayments();
Map> paymentByTypeMap = new HashMap<>();
for(Payment payment : payments){paymentByTypeMap.computeIfAbsent(payment.getPayTypeId(), k -> new ArrayList<>()).add(payment);
}

computeIfAbsent 方法的逻辑是,如果map中没有(Absent)相应的key,则执行lambda表达式生成一个默认值并放入map中并返回,否则返回map中已有的值。

带默认值Map

由于这种需要默认值的Map太常用了,我一般会封装一个工具类出来使用,如下:

public class DefaultHashMap extends HashMap {Function function;public DefaultHashMap(Supplier supplier) {this.function = k -> supplier.get();}@Override@SuppressWarnings("unchecked")public V get(Object key) {return super.computeIfAbsent((K) key, this.function);}
}

然后再这么使用,如下:

List payments = getPayments();
Map> paymentByTypeMap = new DefaultHashMap<>(ArrayList::new);
for(Payment payment : payments){paymentByTypeMap.get(payment.getPayTypeId()).add(payment);
}

临时Cache

有时,在一个for循环中,需要一个临时的Cache在循环中复用查询结果,也可以使用computeIfAbsent,如下:

List payments = getPayments();
Map payTypeCacheMap = new HashMap<>();
for(Payment payment : payments){PayType payType = payTypeCacheMap.computeIfAbsent(payment.getPayTypeId(), k -> payTypeMapper.queryByPayType(k));payment.setPayTypeName(payType.getPayTypeName());
}

因为payments中不同payment的pay_type_id极有可能相同,使用此方法可以避免大量重复查询,但如果不用computeIfAbsent函数,代码就有点繁琐晦涩了。

computeIfPresent函数

computeIfPresent函数与computeIfAbsent的逻辑是相反的,如果map中存在(Present)相应的key,则对其value执行lambda表达式生成一个新值并放入map中并返回,否则返回null。

这个函数一般用在两个集合做等值关联的时候,可少写一次判断逻辑,如下:

@Data
public static class OrderPayment {private Order order;private List payments;public OrderPayment(Order order) {this.order = order;this.payments = new ArrayList<>();}public OrderPayment addPayment(Payment payment){this.payments.add(payment);return this;}
}
public static void getOrderWithPayment(){List orders = getOrders();Map orderPaymentMap = new HashMap<>();for(Order order : orders){orderPaymentMap.put(order.getOrderId(), new OrderPayment(order));}List payments = getPayments();//将payment关联到相关的order上for(Payment payment : payments){orderPaymentMap.computeIfPresent(payment.getOrderId(),(k, orderPayment) -> orderPayment.addPayment(payment));}
}

compute函数

compute函数,其实和computeIfPresent、computeIfAbsent函数是类似的,不过它不关心map中到底有没有值,都执行lambda表达式计算新值并放入map中并返回。

这个函数适合做分组迭代计算,像分组汇总金额的情况,就适合使用compute函数,如下:

List payments = getPayments();
Map amountByTypeMap = new HashMap<>();
for(Payment payment : payments){amountByTypeMap.compute(payment.getPayTypeId(), (key, oldVal) -> oldVal == null ? payment.getAmount() : oldVal.add(payment.getAmount()));
}

当oldValue是null,表示map中第一次计算相应key的值,直接给amount就好,而后面再次累积计算时,直接通过add函数汇总就好。

merge函数

可以发现,上面在使用compute汇总金额时,lambda表达式中需要判断是否是第一次计算key值,稍微麻烦了点,而使用merge函数的话,可以进一步简化代码,如下:

List payments = getPayments();
Map amountByTypeMap = new HashMap<>();
for(Payment payment : payments){amountByTypeMap.merge(payment.getPayTypeId(), payment.getAmount(), BigDecimal::add);
}

这个函数太简洁了,merge的第一个参数是key,第二个参数是value,第三个参数是值合并函数。
当是第一次计算相应key的值时,直接放入value到map中,后面再次计算时,使用值合并函数BigDecimal::add计算出新的汇总值,并放入map中即可。

putIfAbsent函数

putIfAbsent从命名上也能知道作用了,当map中没有相应key时才put值到map中,主要用于如下场景:
如将list转换为map时,若list中有重复值时,put与putIfAbsent的区别如下:

  • put保留最晚插入的数据。
  • putIfAbsent保留最早插入的数据。

forEach函数

说实话,java中要遍历map,写法上是比较啰嗦的,不管是entrySet方式还是keySet方式,如下:

for(Map.Entry entry: amountByTypeMap.entrySet()){Integer payTypeId = entry.getKey();BigDecimal amount = entry.getValue();System.out.printf("payTypeId: %s, amount: %s \n", payTypeId, amount);
}

使用foreach后:

amountByTypeMap.forEach((payTypeId, amount) -> {System.out.printf("payTypeId: %s, amount: %s \n", payTypeId, amount);
});

相关内容

热门资讯

茅台精品、15年价格下调 平价... 中国商报(记者 周子荑 文/图)日前,有市场消息显示,贵州茅台大力度下调了茅台精品、茅台15年两款产...
A股单日成交额3.64万亿元创... A股三大股指1月12日开盘涨跌互现。早盘高位震荡,午前直线拉升。午后两市继续震荡走高,成交额稳步放大...
“全球大模型第一股”,为何诞生... 最近几天的聚光灯,无疑打在了智谱身上。1月8日,智谱正式在香港联合交易所挂牌上市,成为“全球大模型第...
A股成交额首次达到3.5万亿元... 据人民财讯1月12日电,据wind数据,沪深北三市成交额超3.5万亿元,刷新纪录,较上日此时放量48...
三花智控,市值站上2400亿 文丨泰罗这两天,三花智控A股股价再度突破历史新高,市值也来到2400亿上方。去年,三花智控不仅成功登...
可控核聚变概念,午后拉升 1月12日,可控核聚变概念股午后拉升,截至发稿,板块指数涨超4%。 中泰股份20%涨停,中国核建、雪...
朝鲜“万套住宅”震撼亮相,金正... 朝鲜劳动党总书记、国务委员长金正恩1月10日莅临现场指导了进入完工阶段的朝鲜首都平壤和盛地区第四期一...
山寨顶流:一张明星脸,月入上百... 山寨明星,愈演愈烈。“刘亦菲”身着礼服跳热舞、“张颂文”手持冻鱼旋转跳跃、“贝微微”穿越次元出现在真...
小众职业兴起 新规则亟待建立 中国商报(记者 马嘉)“手机需要随时待命,消息必须秒回。”当这样的要求从一种日常压力转变为一份职业的...
宝能集团:化债攻坚见实效,主业... 2026年元旦的深圳,冬日多了一丝寒冷,暖阳穿破薄雾映照着新的希望。在深圳宝能汽车工厂,生产线改造正...