Java中HashMap 中的一個坑
前言
最近公司的系統(tǒng)要增加一個新的列表展示功能,功能本身難度并不大,但遇到了一個很“奇怪”的問題。小伙伴在執(zhí)行查詢列表時,明明已經(jīng)使用了 order by 進(jìn)行排序了,但最終查詢出來的數(shù)據(jù)卻還是亂的。
預(yù)期中的(正確)結(jié)果:

現(xiàn)實中的(非預(yù)期)結(jié)果:

那到底是哪里出現(xiàn)了問題呢?
問題展示
為了方便展示,我把復(fù)雜的業(yè)務(wù)程序簡化成了以下代碼:
import java.util.HashMap;
public class App {
public static void main(String[] args) {
HashMap<String, Object> result = getList();
result.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
}
// 查詢方法(簡化版)
public static HashMap<String, Object> getList() {
HashMap<String, Object> result = new HashMap<>(); // 最終返回的結(jié)果集
// 偽代碼:從數(shù)據(jù)庫中查詢出了數(shù)據(jù),然后對數(shù)據(jù)進(jìn)行處理之后,存到了
for (int i = 1; i <= 5; i++) {
result.put("2022-10-" + i, "hello java" + i);
}
return result;
}
}以上程序的執(zhí)行結(jié)果如下:

預(yù)期的結(jié)果應(yīng)該是按時間的先后順序展示的,如下圖所示:

PS:以上示例代碼中,插入元素的順序是有序的(從 1 到 5),相當(dāng)于實際業(yè)務(wù)場景中的 order by。
原因分析
既然原數(shù)據(jù)使用了 order by 排序,那么原數(shù)據(jù)肯定是沒問題的,那問題就只會出現(xiàn)在返回集 HashMap 上,然后我們再把焦點(diǎn)放到 HashMap 上, 瞬間醒悟,哦,原來如此。HashMap 使用的是哈希方式進(jìn)行存儲的,因此存入和讀取的順序可能是不一致的,這也說 HashMap 是無序的集合,所以會導(dǎo)致插入的(或 order by 的)順序,與最終展示的順序不一致。
解決方案
經(jīng)過上面的分析我們順利找到了問題,那接下來就是制定相應(yīng)的解決方案了,我想到的解決方案有兩個:
- 稍微麻煩一點(diǎn)但正確的解決方案:將返回的不確定數(shù)據(jù)類型 HashMap 改為確定的數(shù)據(jù)類型,比如 List;
- 簡單一點(diǎn)但并不是最優(yōu)的解決方案:將無序的 HashMap 改為有序的 LinkedHashMap,此方案的優(yōu)點(diǎn)是,只需要改動一個單詞就可以解決整個問題了。
第一種解決方案大家都懂這里就不演示了,接下來咱們使用第二種解決方案將上面的問題改造一下,
最終的實現(xiàn)代碼如下:
import java.util.HashMap;
import java.util.LinkedHashMap;
public class App {
public static void main(String[] args) {
HashMap<String, Object> result = getList();
result.forEach((k, v) -> {
System.out.println(k + ":" + v);
});
}
// 查詢方法(簡化版)
public static HashMap<String, Object> getList() {
HashMap<String, Object> result = new LinkedHashMap<>(); // 最終返回的結(jié)果集
// 偽代碼:從數(shù)據(jù)庫中查詢出了數(shù)據(jù),然后對數(shù)據(jù)進(jìn)行處理之后,存到了
for (int i = 1; i <= 5; i++) {
result.put("2022-10-" + i, "hello java" + i);
}
return result;
}
}以上程序的執(zhí)行結(jié)果如下:

從上述結(jié)果可以看出,當(dāng)使用 LinkedHashMap 替代了 HashMap 之后,返回的順序就能和插入的順序保持一致了。
LinkedHashMap 的魔力
為什么 HashMap 是無序的,而 LinkedHashMap 卻是有序的呢?
這要從二者的實現(xiàn)說起了,LinkedHashMap 屬于 HashMap 的子類,所以 LinkedHashMap 除了擁有 HashMap 的所有特性之后,還具備自身的一些擴(kuò)展屬性,其中就包括 LinkedHashMap 中額外維護(hù)了一個雙向鏈表,這個雙向鏈表就是用來保存元素的(插入)順序的,這也是為什么 LinkedHashMap 可以實現(xiàn)訪問順序和插入順序一致的原因了。
總結(jié)
本文演示了 HashMap 作為返回類型時隱藏的一個小“坑”,因為 HashMap 本身是無序的,所以它會導(dǎo)致查詢順序和插入順序不一致的問題,對應(yīng)的解決方案有兩種:使用確定的數(shù)據(jù)類型來替代 HashMap,比如 List,或者使用有序的 LinkedHashMap 來替代無序的 HashMap。
到此這篇關(guān)于Java中HashMap 中的一個坑的文章就介紹到這了,更多相關(guān)Java HashMap 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot+Redis實現(xiàn)API接口防刷限流的項目實踐
本文主要介紹了Springboot+Redis實現(xiàn)API接口防刷限流的項目實踐,通過限流可以讓系統(tǒng)維持在一個相對穩(wěn)定的狀態(tài),為更多的客戶提供服務(wù),具有一定的參考價值,感興趣的可以了解一下2024-07-07
Springboot中如何使用過濾器校驗PSOT類型請求參數(shù)內(nèi)容
在Springboot中創(chuàng)建過濾器,用來過濾所有POST類型請求并獲取body中的參數(shù)進(jìn)行校驗內(nèi)容是否合法,該方法僅適用于POST類型請求,本文給大家介紹Springboot中如何使用過濾器校驗PSOT類型請求參數(shù)內(nèi)容,感興趣的朋友一起看看吧2023-08-08
Springboot?內(nèi)部服務(wù)調(diào)用方式
這篇文章主要介紹了Springboot?內(nèi)部服務(wù)調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
SpringBoot字段注入和構(gòu)造函數(shù)注入的區(qū)別及說明
這篇文章主要介紹了SpringBoot字段注入和構(gòu)造函數(shù)注入的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-04-04

