HashMap 中的一個“坑”!

最近公司新來了一個小夥伴,問了磊哥一個比較“奇怪”的問題,這個問題本身的難度並不大,但比較“隱蔽”,那究竟是什麼問題呢?接下來我們一起來看。

起因

最近公司的系統要增加一個新的列表展示功能,功能本身難度並不大,但遇到了一個很“可怪”的問題。小夥伴在執行查詢列表時,明明已經使用了 order by 進行排序了,但最終查詢出來的數據卻還是亂的。

預期中的(正確)結果:
image.png
現實中的(非預期)結果:
image.png
那到底是哪裏出現了問題呢?

問題展示

爲了方便展示,我把複雜的業務程序簡化成了以下代碼:

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<>(); // 最終返回的結果集
        // 僞代碼:從數據庫中查詢出了數據,然後對數據進行處理之後,存到了
        for (int i = 1; i <= 5; i++) {
            result.put("2022-10-" + i, "hello java" + i);
        }
        return result;
    }
}

以上程序的執行結果如下:
image.png

預期的結果應該是按時間的先後順序展示的,如下圖所示:
image.png

PS:以上示例代碼中,插入元素的順序是有序的(從 1 到 5),相當於實際業務場景中的 order by。

原因分析

既然原數據使用了 order by 排序,那麼原數據肯定是沒問題的,那問題就只會出現在返回集 HashMap 上,然後我們再把焦點放到 HashMap 上, 瞬間醒悟,哦,原來如此。HashMap 使用的是哈希方式進行存儲的,因此存入和讀取的順序可能是不一致的,這也說 HashMap 是無序的集合,所以會導致插入的(或 order by 的)順序,與最終展示的順序不一致。

解決方案

經過上面的分析我們順利找到了問題,那接下來就是制定相應的解決方案了,我想到的解決方案有兩個:

  1. 稍微麻煩一點但正確的解決方案:將返回的不確定數據類型 HashMap 改爲確定的數據類型,比如 List
  2. 簡單一點但並不是最優的解決方案:將無序的 HashMap 改爲有序的 LinkedHashMap,此方案的優點是,只需要改動一個單詞就可以解決整個問題了。

第一種解決方案大家都懂這裏就不演示了,接下來咱們使用第二種解決方案將上面的問題改造一下,最終的實現代碼如下:

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<>(); // 最終返回的結果集
        // 僞代碼:從數據庫中查詢出了數據,然後對數據進行處理之後,存到了
        for (int i = 1; i <= 5; i++) {
            result.put("2022-10-" + i, "hello java" + i);
        }
        return result;
    }
}

以上程序的執行結果如下:
image.png
從上述結果可以看出,當使用 LinkedHashMap 替代了 HashMap 之後,返回的順序就能和插入的順序保持一致了。

LinkedHashMap 的魔力

爲什麼 HashMap 是無序的,而 LinkedHashMap 卻是有序的呢?

這要從二者的實現說起了,LinkedHashMap 屬於 HashMap 的子類,所以 LinkedHashMap 除了擁有 HashMap 的所有特性之後,還具備自身的一些擴展屬性,其中就包括 LinkedHashMap 中額外維護了一個雙向鏈表,這個雙向鏈表就是用來保存元素的(插入)順序的,這也是爲什麼 LinkedHashMap 可以實現訪問順序和插入順序一致的原因了。

總結

本文演示了 HashMap 作爲返回類型時隱藏的一個小“坑”,因爲 HashMap 本身是無序的,所以它會導致查詢順序和插入順序不一致的問題,對應的解決方案有兩種:使用確定的數據類型來替代 HashMap,比如 List,或者使用有序的 LinkedHashMap 來替代無序的 HashMap。

關注公衆號「Java中文社羣」查看更多 Java 總結性系列文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章