一文搞懂Map初始化與賦值,碼農(nóng)必備!
2025-01-10 10:01:10
一、Map 是啥?為啥要用它?

在編程的世界里,Map 就像是一個超級收納箱,它是一種用來存儲數(shù)據(jù)的結構。和普通的數(shù)組、列表不一樣,Map 存儲的是一對一對的 “鍵值對”,簡單來說,就是每個數(shù)據(jù)都有一個對應的 “標簽”,通過這個 “標簽”(也就是鍵),我們能快速找到它所對應的 “內(nèi)容”(也就是值)。比如說,咱們要存儲一個班級學生的成績,用數(shù)組的話,可能就是 [90, 85, 92, 78……],但這樣很難直接看出哪個成績對應哪個學生。要是用 Map,就可以像這樣:{"小明": 90, "小紅": 85, "小剛": 92……},一目了然,通過學生的名字(鍵)就能馬上拿到他們的成績(值)。再比如電商平臺管理商品信息,商品名稱作為鍵,商品的價格、庫存、描述等詳情作為值,不管是查詢還是更新某個商品的信息,都能迅速定位,高效便捷。這就是 Map 的強大之處,它讓數(shù)據(jù)的管理和查找變得井井有條,極大地提升了編程處理數(shù)據(jù)的效率,能幫我們輕松應對各種復雜的信息存儲需求。
二、初始化 Map 的幾種常見方式
(一)使用 put () 方法逐個添加鍵值對
這是最基礎、最直觀的一種方式。咱們先創(chuàng)建一個 Map 對象,通常用得比較多的是 HashMap,就像這樣:Map<String, Integer> map = new HashMap<>(); 這里<String, Integer> 表示這個 Map 里的鍵是字符串類型,值是整數(shù)類型。創(chuàng)建好之后,就可以用 put() 方法一個一個地把鍵值對往里塞啦,比如:這樣就創(chuàng)建了一個簡單的存儲水果數(shù)量的 Map,鍵是水果名稱,值是對應的數(shù)量。這種方式雖然簡單直接,容易理解,但是如果要初始化的鍵值對特別多,那代碼就會顯得很冗長,看著眼花繚亂,而且一個個敲也容易出錯。
(二)使用靜態(tài)代碼塊初始化 Map
靜態(tài)代碼塊是在類加載的時候就執(zhí)行的,而且只會執(zhí)行一次,特別適合用來初始化那些固定不變的鍵值對。還是拿剛才水果數(shù)量的例子,代碼可以寫成這樣:對比一下前面用 put() 方法一個個添加的方式,這里是不是簡潔多了?所有的初始化操作都在靜態(tài)代碼塊里一次性搞定,代碼結構清晰,一目了然,別人看你的代碼時也能很快明白這些初始數(shù)據(jù)是干啥的。不過要注意哦,因為靜態(tài)代碼塊只在類加載時執(zhí)行一次,所以如果后續(xù)運行過程中需要動態(tài)地改變 Map 里的數(shù)據(jù),這種方式就不太合適了,它更側重于一開始就固定好的那些配置信息之類的初始化。
(三)使用雙括號初始化(匿名內(nèi)部類)
這種方式有點小 “神奇”,看起來很簡潔。還是以存儲數(shù)據(jù)為例,假設我們要存儲幾個城市的人口數(shù)量,代碼可以這么寫:這里外層的大括號是創(chuàng)建 HashMap 的實例,內(nèi)層的雙括號 {{}} 其實是創(chuàng)建了一個匿名內(nèi)部類,在這個匿名內(nèi)部類的初始化塊里用 put() 方法添加鍵值對。這種寫法讓代碼非常緊湊,在一些簡單場景下看起來很清爽,一眼就能看清初始化的數(shù)據(jù)。但是呢,它也有一些 “坑”。因為匿名內(nèi)部類會持有外部類的引用,如果不小心,當外部類生命周期結束了,而這個匿名內(nèi)部類還被 Map 引用著,就可能導致內(nèi)存泄漏。而且在涉及到序列化、反序列化的時候,也容易出現(xiàn)問題,比如可能會串行化失敗,導致數(shù)據(jù)保存或讀取不正常。所以用這種方式的時候得謹慎,要是對內(nèi)存、序列化這些方面要求高,或者不確定后續(xù)會不會有坑,還是考慮前面更穩(wěn)妥的初始化方法。
三、Java 9 + 帶來的新玩法
Java 9 可是給我們帶來了一些新的 “神器”,讓 Map 的初始化更加得心應手。其中最亮眼的就是 Map.of() 和 Map.ofEntries() 方法。先看看 Map.of(),假設我們要創(chuàng)建一個存儲幾個顏色對應英文單詞的 Map,代碼可以寫成這樣:Map<String, String> colorMap = Map.of("紅色", "red", "綠色", "green", "藍色", "blue"); 短短一行代碼,就把 Map 初始化好了,是不是超級簡潔?它的參數(shù)是有上限的,最多只能 20 個參數(shù),也就是 10 個鍵值對,而且不允許有重復的鍵。這就像是給我們準備了一個便捷的小工具包,適合那些鍵值對數(shù)量固定又不多,并且鍵不能重復的場景,比如一些配置信息、常量映射等,用它來初始化,代碼清爽,一眼就能看清數(shù)據(jù)結構。再說說 Map.ofEntries(),它接受一個 Map.Entry 對象的可變參數(shù)。比如說,我們有這樣一組員工姓名和工號的數(shù)據(jù):這種方式就靈活多了,參數(shù)數(shù)量可以是任意的,雖然也不允許重復鍵,但對于需要一次性初始化較多鍵值對,或者是從其他數(shù)據(jù)結構轉換過來的場景,就特別合適。它就像是一把萬能鑰匙,打開了復雜數(shù)據(jù)初始化的大門,讓我們可以根據(jù)實際情況自由組合鍵值對,輕松構建出符合需求的 Map。和以前的初始化方式相比,這兩個新方法省去了創(chuàng)建對象、調(diào)用 put() 方法等繁瑣步驟,讓代碼更加簡潔高效,是咱們 Java 程序員在處理 Map 初始化時的得力助手。
四、Guava 庫中的 ImmutableMap
除了 Java 自帶的那些初始化 Map 的方法,Guava 庫也為我們提供了超好用的工具,那就是 ImmutableMap。它創(chuàng)建出來的 Map 可是 “鐵打的”,一旦創(chuàng)建,就別想修改里面的數(shù)據(jù)了,絕對的 “只讀” 模式。比如說,我們要存儲一些系統(tǒng)的配置信息,像數(shù)據(jù)庫連接的各種參數(shù):用戶名、密碼、連接地址、端口號等,這些信息在系統(tǒng)運行過程中肯定是不能變的。代碼就可以這么寫:這里用 ImmutableMap.of() 方法輕松創(chuàng)建了一個不可變的 Map 來存儲數(shù)據(jù)庫配置,在程序的任何地方都無法修改它,保證了配置的穩(wěn)定性和一致性,防止誤操作帶來的風險。要是需要初始化的數(shù)據(jù)量比較大,還可以用 ImmutableMap.builder() 來構建,就像這樣:通過 builder() 模式,一個一個地添加鍵值對,最后調(diào)用 build() 方法生成不可變 Map,這種方式在處理大量數(shù)據(jù)時更加靈活、易讀,而且不用擔心數(shù)據(jù)被篡改,為程序的穩(wěn)定性保駕護航,特別適用于那些作為常量、配置信息的 Map 場景,讓代碼更加健壯可靠。
五、不同方式大比拼
這么多種初始化和賦值 Map 的方法,到底啥時候用哪種呢?咱們來詳細對比一下。先看代碼簡潔度,使用 Map.of()(Java 9+)和雙括號初始化(匿名內(nèi)部類)的方式在簡單場景下代碼很簡潔,一行或者幾行就能搞定,看起來清爽。像 Map<String, String> colorMap = Map.of("紅色", "red", "綠色", "green", "藍色", "blue"); ,一目了然。而使用 put() 方法逐個添加就顯得啰嗦,要是鍵值對多,一堆 put() 語句,看著眼花繚亂。靜態(tài)代碼塊比 put() 方法稍好一點,起碼集中在一起,不過也不如前兩者簡潔。Guava 庫的 ImmutableMap.of() 用于少量固定數(shù)據(jù)也很簡潔, ImmutableMap.builder() 在數(shù)據(jù)量較大時,結構清晰,也有不錯的簡潔度。執(zhí)行效率方面,一般來說,簡單的 put() 方法逐個添加和靜態(tài)代碼塊初始化效率相對穩(wěn)定,因為就是常規(guī)的操作。雙括號初始化(匿名內(nèi)部類)由于涉及匿名內(nèi)部類的創(chuàng)建等額外開銷,執(zhí)行效率會稍低一點。有測試在創(chuàng)建大量 Map 對象時,它比普通 put() 方法慢個 10% - 15% 左右。而 Java 9 + 的 Map.of() 和 Map.ofEntries() 方法內(nèi)部做了優(yōu)化,效率挺高,和常規(guī)方式不相上下,甚至在一些場景下還更快,因為減少了不必要的對象創(chuàng)建步驟。Guava 庫的 ImmutableMap 在創(chuàng)建后由于不可變,避免了一些潛在的修改沖突檢查等開銷,對于作為常量使用的 Map,效率也很不錯。內(nèi)存占用上,Map.of() 這種創(chuàng)建不可變小 Map 的方式,因為內(nèi)部優(yōu)化,占用內(nèi)存相對少,尤其是鍵值對不多的時候。雙括號初始化(匿名內(nèi)部類)由于匿名內(nèi)部類的存在,以及可能引發(fā)的外部類引用持有問題,容易造成額外的內(nèi)存占用,在大規(guī)模使用或者對象生命周期復雜時,可能出現(xiàn)內(nèi)存泄漏風險,隱患較大。靜態(tài)代碼塊和普通 put() 方法初始化的 Map,內(nèi)存占用正常,就是常規(guī)對象的開銷。Guava 庫的 ImmutableMap 因為不可變,在內(nèi)存管理上有優(yōu)勢,不會有額外的動態(tài)擴容等內(nèi)存波動,對于配置信息這種長期占用內(nèi)存的場景,很節(jié)省空間。適用場景的話,如果是簡單的臨時數(shù)據(jù)存儲,少量鍵值對且后續(xù)可能修改,用 put() 方法就行,方便靈活。要是固定不變的配置信息,像系統(tǒng)參數(shù)、常量映射,Map.of()(鍵值對少)、Map.ofEntries()(鍵值對可多)或者 Guava 庫的 ImmutableMap 就很合適,保證數(shù)據(jù)不可變,防止誤改。靜態(tài)代碼塊適合初始化那些類加載時就固定的、與類緊密相關的 Map 數(shù)據(jù),比如一些類的靜態(tài)配置。雙括號初始化(匿名內(nèi)部類),只建議在簡單、臨時、對內(nèi)存和序列化沒嚴格要求,追求代碼短期簡潔的小場景下用用,要是涉及復雜業(yè)務、多線程、序列化反序列化,就盡量避開,免得踩坑。咱們今兒個一起深入探討了初始化 Map 并賦值的多種方法,從最基礎的 put() 方法,到靜態(tài)代碼塊、雙括號初始化,再到 Java 9 + 帶來的便捷新特性,還有 Guava 庫的 ImmutableMap,各有千秋。大家在實際編程的時候,一定要根據(jù)項目的具體需求,像是數(shù)據(jù)量大小、是否可變、對執(zhí)行效率和內(nèi)存占用的敏感度等來綜合考量,選出最順手、最能讓代碼 “閃閃發(fā)光” 的那種初始化方式。別小瞧這一點,代碼寫得好,后續(xù)維護、擴展都輕松不少,說不定還能幫你提前發(fā)現(xiàn)潛在的 bug 呢!希望大家都動手試試這些方法,把它們?nèi)谌氲阶约旱拇a “武器庫” 中。之后呢,咱們還會分享更多實用的編程技巧、知識干貨,記得持續(xù)關注,一起在編程的道路上升級打怪,寫出更牛的代碼!