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

原创:扣钉日记(微信公众号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)
【Java8中那些方便又实用的Map函数】临时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 。
这个函数一般用在两个集合做等值关联的时候,可少写一次判断逻辑,如下:
@Datapublic 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函数汇总就好 。

推荐阅读