首页IT科技Java8中那些方便又实用的Map函数

Java8中那些方便又实用的Map函数

时间2025-06-21 03:20:40分类IT科技浏览3671
导读:原创:扣钉日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处。...

原创:扣钉日记(微信公众号ID:codelogs)           ,欢迎分享                 ,转载请保留出处           。

简介

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

computeIfAbsent函数

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

List<Payment> payments = getPayments(); Map<Integer, List<Payment>> 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<Payment> payments = getPayments(); Map<Integer, List<Payment>> 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<K, V> extends HashMap<K, V> { Function<K, V> function; public DefaultHashMap(Supplier<V> supplier) { this.function = k -> supplier.get(); } @Override @SuppressWarnings("unchecked") public V get(Object key) { return super.computeIfAbsent((K) key, this.function); } }

然后再这么使用      ,如下:

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

呵呵                 ,这玩得有点像python的defaultdict(list)了?

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

List<Payment> payments = getPayments(); Map<Integer, PayType> 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极有可能相同,使用此方法可以避免大量重复查询                 ,但如果不用computeIfAbcent函数                  ,代码就有点繁琐晦涩了                 。

computeIfPresent函数

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

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

@Data public static class OrderPayment { private Order order; private List<Payment> 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<Order> orders = getOrders(); Map<Long, OrderPayment> orderPaymentMap = new HashMap<>(); for(Order order : orders){ orderPaymentMap.put(order.getOrderId(), new OrderPayment(order)); } List<Payment> payments = getPayments(); //将payment关联到相关的order上 for(Payment payment : payments){ orderPaymentMap.computeIfPresent(payment.getOrderId(), (k, orderPayment) -> orderPayment.addPayment(payment)); } }

compute函数

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

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

List<Payment> payments = getPayments(); Map<Integer, BigDecimal> 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<Payment> payments = getPayments(); Map<Integer, BigDecimal> 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<String, BigDecimal> entry: amountByTypeMap.entrySet()){ Integer payTypeId = entry.getKey(); BigDecimal amount = entry.getValue(); System.out.printf("payTypeId: %s, amount: %s \n", payTypeId, amount); }

再看看在python或go中的写法           ,如下:

for payTypeId, amount in amountByTypeMap.items(): print("payTypeId: %s, amount: %s \n" % (payTypeId, amount))

可以发现                 ,在python中的map遍历写法要少写好几行代码呢      ,不过      ,虽然java在语法层面上并未支持这种写法                 ,但使用map的forEach函数            ,也可以简化出类似的效果来      ,如下:

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

总结

一直以来                 ,java因代码编写太繁琐而被开发者们所广泛诟病            ,但从java8开始,从Map                 、Stream      、var      、multiline-string再到record                 ,java在代码编写层面做了大量的简化                  ,java似乎开窍了?

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
zblog火车头发布模块(快速掌握zblog火车头采集技巧,提升博客内容质量)