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函數匯總就好 。

推薦閱讀