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