一、Map 是啥?為啥要用它?

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