Java8中方便又實用的Map函數(shù)總結(jié)
簡介
java8之后,常用的Map接口中添加了一些非常實用的函數(shù),可以大大簡化一些特定場景的代碼編寫,提升代碼可讀性,一起來看看吧。
computeIfAbsent函數(shù)
比如,很多時候我們需要對數(shù)據(jù)進行分組,變成Map<Integer, List<?>>
的形式,在java8之前,一般如下實現(xiàn):
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); }
可以發(fā)現(xiàn)僅僅做一個分組操作,代碼卻需要考慮得比較細致,在Map中無相應(yīng)值時需要先塞一個空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)相應(yīng)的key,則執(zhí)行l(wèi)ambda表達式生成一個默認值并放入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循環(huán)中,需要一個臨時的Cache在循環(huán)中復(fù)用查詢結(jié)果,也可以使用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極有可能相同,使用此方法可以避免大量重復(fù)查詢,但如果不用computeIfAbcent函數(shù),代碼就有點繁瑣晦澀了。
computeIfPresent函數(shù)
computeIfPresent函數(shù)與computeIfAbcent的邏輯是相反的,如果map中存在(Present)相應(yīng)的key,則對其value執(zhí)行l(wèi)ambda表達式生成一個新值并放入map中并返回,否則返回null。
這個函數(shù)一般用在兩個集合做等值關(guān)聯(lián)的時候,可少寫一次判斷邏輯,如下:
@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關(guān)聯(lián)到相關(guān)的order上 for(Payment payment : payments){ orderPaymentMap.computeIfPresent(payment.getOrderId(), (k, orderPayment) -> orderPayment.addPayment(payment)); } }
compute函數(shù)
compute函數(shù),其實和computeIfPresent、computeIfAbcent函數(shù)是類似的,不過它不關(guān)心map中到底有沒有值,都執(zhí)行l(wèi)ambda表達式計算新值并放入map中并返回。
這個函數(shù)適合做分組迭代計算,像分組匯總金額的情況,就適合使用compute函數(shù),如下:
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()) ); }
當(dāng)oldValue是null,表示map中第一次計算相應(yīng)key的值,直接給amount就好,而后面再次累積計算時,直接通過add函數(shù)匯總就好。
merge函數(shù)
可以發(fā)現(xiàn),上面在使用compute匯總金額時,lambda表達式中需要判斷是否是第一次計算key值,稍微麻煩了點,而使用merge函數(shù)的話,可以進一步簡化代碼,如下:
List<Payment> payments = getPayments(); Map<Integer, BigDecimal> amountByTypeMap = new HashMap<>(); for(Payment payment : payments){ amountByTypeMap.merge(payment.getPayTypeId(), payment.getAmount(), BigDecimal::add); }
這個函數(shù)太簡潔了,merge的第一個參數(shù)是key,第二個參數(shù)是value,第三個參數(shù)是值合并函數(shù)。
當(dāng)是第一次計算相應(yīng)key的值時,直接放入value到map中,后面再次計算時,使用值合并函數(shù)BigDecimal::add
計算出新的匯總值,并放入map中即可。
putIfAbsent函數(shù)
putIfAbsent從命名上也能知道作用了,當(dāng)map中沒有相應(yīng)key時才put值到map中,主要用于如下場景:
如將list轉(zhuǎn)換為map時,若list中有重復(fù)值時,put與putIfAbsent的區(qū)別如下:
- put保留最晚插入的數(shù)據(jù)。
- putIfAbsent保留最早插入的數(shù)據(jù)。
forEach函數(shù)
說實話,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))
可以發(fā)現(xiàn),在python中的map遍歷寫法要少寫好幾行代碼呢,不過,雖然java在語法層面上并未支持這種寫法,但使用map的forEach函數(shù),也可以簡化出類似的效果來,如下:
amountByTypeMap.forEach((payTypeId, amount) -> { System.out.printf("payTypeId: %s, amount: %s \n", payTypeId, amount); });
總結(jié)
一直以來,java因代碼編寫太繁瑣而被開發(fā)者們所廣泛詬病,但從java8開始,從Map、Stream、var、multiline-string再到record,java在代碼編寫層面做了大量的簡化,java似乎開竅了
到此這篇關(guān)于Java8中方便又實用的Map函數(shù)總結(jié)的文章就介紹到這了,更多相關(guān)Java8 Map函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解springboot設(shè)置cors跨域請求的兩種方式
這篇文章主要介紹了詳解springboot設(shè)置cors跨域請求的兩種方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11IntelliJ?IDEA快速查詢maven依賴關(guān)系圖文教程
Maven提供了來查看依賴關(guān)系,而IDE往往提供了更加便利的方式,比如Eclipse或者IDEA都有類似的功能,下面這篇文章主要給大家介紹了關(guān)于IntelliJ?IDEA快速查詢maven依賴關(guān)系的相關(guān)資料,需要的朋友可以參考下2023-11-11