從入門到精通:Java中的HashMap實現原理詳解

HashMap是Java語言中最常用的的數據結構之一,它的高效性和易用性使它成為程序員們必備的工具之一。本文將從HashMap的原理入手,詳細闡述它的實現細節和注意事項,幫助讀者掌握HashMap的使用方法和技巧。

一、什麼是HashMap

HashMap是Java語言中的一種散列表,它可以存儲鍵值對的映射關係。HashMap可以快速地進行插入、查找和刪除操作,時間複雜度均為O(1)。HashMap底層實現是通過數組和鏈表相結合的方式來實現散列表。當鏈表長度大於閾值(默認為8)時,鏈表會轉化成紅黑樹,以保證插入、查找和刪除操作的平均時間複雜度為O(1)。

二、HashMap的構造方法

以下是HashMap的幾種構造方法:

1. HashMap() : 創建一個空的HashMap
2. HashMap(int initialCapacity) : 創建指定大小的HashMap
3. HashMap(int initialCapacity, float loadFactor) : 創建指定大小和載入因子的HashMap
4. HashMap(Map m) : 創建指定Map的HashMap

其中,initialCapacity是HashMap的初始容量,loadFactor是負載因子,如果負載因子為0.75,則當HashMap的大小達到容量的75%時,會自動增加容量。如果初始容量和負載因子沒有給出,則默認初始容量為16,負載因子為0.75。

三、HashMap的put()方法

以下是HashMap的put()方法:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    Node[] tab; Node p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) {
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

put()方法是非常重要的方法,它將鍵值對插入到HashMap中。它的實現細節如下:

1. 首先,通過hash()方法計算鍵的哈希值,判斷table是否為空或長度為0。如果是,通過resize()方法進行初始化,否則直接獲取table的長度n。

2. 然後,通過(n – 1) & hash計算出要插入的位置,p表示該位置的第一個節點。如果該位置為空,則直接插入鍵值對,否則執行第3步。

3. 判斷該位置上的第一個節點的key是否和要插入的key相等,如果相等則執行覆蓋操作。如果不相等,判斷該位置上的第一個節點是否為TreeNode類型。如果是,則執行紅黑樹的put操作,否則遍歷該鏈表,找到最後一個節點,插入新的節點後,如果該鏈表的長度大於等於8,則通過treeifyBin方法將該鏈錶轉化成紅黑樹。

4. 插入完成以後,判斷size是否大於threshold,如果是,則通過resize()方法進行擴容。

四、HashMap的get()方法

以下是HashMap的get()方法:

public V get(Object key) {
    Node e;
    // 通過hash方法計算hash值,然後在table數組中尋找key所在的節點
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

final Node getNode(int hash, Object key) {
    Node[] tab; Node first, e; int n; K k;
    // table不為空,取得table數組的長度n,n>0
    if ((tab = table) != null && (n = tab.length) > 0 &&
        // 通過(n - 1) & hash獲取key在table數組中的位置first
        (first = tab[(n - 1) & hash]) != null) {
        // 如果在table的first位置上就找到相同的key,則返回這個節點first
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        // 如果first位置沒有找到相同的key,則處理next鏈表
        if ((e = first.next) != null) {
            // 如果first位置是TreeNode節點,則使用TreeNode的方式獲取相同的key
            if (first instanceof TreeNode)
                return ((TreeNode)first).getTreeNode(hash, key);
            // 否則,遍歷next鏈表查找相同的key
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null; // 如果table為空或key沒有匹配到返回null
}

get()方法是HashMap中的另一個重要方法,它以鍵為參數,返回與之關聯的值。get()方法的實現細節如下:

1. 首先通過getNode()方法獲取到key所在的節點。getNode()方法也是一個非常重要的方法,它遍歷了HashMap中的所有元素,尋找與key相同的元素,如果找到,則返回與之對應的節點,否則返回null。

2. 如果key所在的節點不為空,則返回該節點對應的值,否則返回null。

五、HashMap的remove()方法

以下是HashMap的remove()方法:

public V remove(Object key) {
    Node e;
    // 通過hash方法計算hash值,然後在table數組中尋找key所在的節點
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}

final Node removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
    Node[] tab; Node p; int n, index;
    // 如果table不為空,且key在table中存在,則執行刪除操作
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node node = null, e; K k; V v;
        // 判斷p節點是不是要被刪除的節點,並找到p鏈表上的最後一個節點node
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        else if ((e = p.next) != null) {
            if (p instanceof TreeNode)
                node = ((TreeNode)p).getTreeNode(hash, key);
            else {
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        // 如果找到要被刪除的節點,則執行刪除操作
        if (node != null && (!matchValue || (v = node.value) == value ||
                              (value != null && value.equals(v)))) {
            if (node instanceof TreeNode)
                ((TreeNode)node).removeTreeNode(this, tab, movable);
            else if (node == p)
                tab[index] = node.next;
            else
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}

remove()方法是HashMap刪除元素的方法,它需要傳入一個key作為參數,並返回被刪除的元素的value值。remove()方法的實現細節如下:

1. 通過hash()方法計算鍵的哈希值,然後獲取該鍵所在的節點p,並獲取在該節點上的最後一個節點node。如果從p節點開始遍歷整個鏈表都沒有找到與key相同的節點,則返回null。

2. 如果找到了與key相同的節點,首先判斷該節點是否是TreeNode類型。如果是,則使用TreeNode的方式刪除節點。否則,判斷要被刪除的節點是否是p節點。如果是,則直接將p節點的下一個節點賦給tab[index]。否則,通過遍歷鏈表,將要被刪除的節點刪除掉。

3. 執行完刪除操作以後,判斷size是否小於threshold的0.25,如果是,則通過resize()方法進行縮容。

六、HashMap的注意事項

1. HashMap的線程安全性問題

HashMap是非線程安全的,如果多個線程同時對一個HashMap進行讀寫操作,可能會出現數據不一致的情況。為了解決這個問題,可以使用線程安全的ConcurrentHashMap。

2. HashMap中鍵對象的注意事項

為了保證HashMap數據的一致性,鍵對象必須實現equals()方法和hashCode()方法,這兩個方法的實現決定了HashMap的查找、插入和刪除操作的正確性和效率。在使用HashMap時,需要特別注意鍵對象的實現細節,盡量避免相同的鍵對象產生哈希碰撞的情況,從而提高HashMap的效率。

3. HashMap的默認容量和負載因子

HashMap的默認容量為16,負載因子為0.75。如果不特別指定初始容量和負載因子,則採用默認值。在實際應用中,建議根據數據量大小和訪問頻率來選擇合適的初始容量和負載因子,以提高HashMap的效率和性能。

七、總結

本文對HashMap的實現原理進行了詳細的闡述,包括構造方法、put()方法、get()方法、remove()方法等。同時,我們也介紹了HashMap的注意事項,如線程安全性問題、鍵對象的實現細節、默認容量和負載因子等。希望通過本文的學習,能夠幫助讀者更好地掌握HashMap的使用方法和技巧。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/233965.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-11 17:12
下一篇 2024-12-11 17:12

相關推薦

  • Python wordcloud入門指南

    如何在Python中使用wordcloud庫生成文字雲? 一、安裝和導入wordcloud庫 在使用wordcloud前,需要保證庫已經安裝並導入: !pip install wo…

    編程 2025-04-29
  • Python小波分解入門指南

    本文將介紹Python小波分解的概念、基本原理和實現方法,幫助初學者掌握相關技能。 一、小波變換概述 小波分解是一種廣泛應用於數字信號處理和圖像處理的方法,可以將信號分解成多個具有…

    編程 2025-04-29
  • Harris角點檢測演算法原理與實現

    本文將從多個方面對Harris角點檢測演算法進行詳細的闡述,包括演算法原理、實現步驟、代碼實現等。 一、Harris角點檢測演算法原理 Harris角點檢測演算法是一種經典的計算機視覺演算法…

    編程 2025-04-29
  • 瘦臉演算法 Python 原理與實現

    本文將從多個方面詳細闡述瘦臉演算法 Python 實現的原理和方法,包括該演算法的意義、流程、代碼實現、優化等內容。 一、演算法意義 隨著科技的發展,瘦臉演算法已經成為了人們修圖中不可缺少…

    編程 2025-04-29
  • Python豎線圖:從入門到精通

    Python豎線圖,即Python的繪圖工具matplotlib中的一種圖形類型,具有直觀、易於理解的特點,適用於各種數據分析和可視化場景。本文從初學者角度出發,介紹Python豎…

    編程 2025-04-29
  • 神經網路BP演算法原理

    本文將從多個方面對神經網路BP演算法原理進行詳細闡述,並給出完整的代碼示例。 一、BP演算法簡介 BP演算法是一種常用的神經網路訓練演算法,其全稱為反向傳播演算法。BP演算法的基本思想是通過正…

    編程 2025-04-29
  • Python爬取數據指南-從入門到精通

    Python爬蟲是指用Python編寫程序,自動化地獲取網路上的信息,並進行處理、分析和存儲。以下是Python爬取數據的指南,從入門到精通。 一、獲取網頁數據 Python爬蟲的…

    編程 2025-04-29
  • Python自學多久能入門?

    Python是一門極具優勢的編程語言,無論在人工智慧、數據分析、Web開發等領域都有廣泛的應用,所以越來越多的人開始學習Python。但是對於初學者來說,Python自學多久能入門…

    編程 2025-04-28
  • Python導出微信群聊天記錄:從入門到實踐

    微信群聊是我們日常生活中與家人、朋友聊天交流的重要平台。但是,當備份和查看微信群聊的聊天記錄時,我們常常會遇到各種問題。這時,我們可以使用Python對微信群聊天記錄進行導出、備份…

    編程 2025-04-28
  • Python熵權法入門指南

    本文將為你介紹Python熵權法的基礎知識以及如何在實際應用中使用熵權法,讓你能夠更好地理解該演算法並將其運用到實際工作中。 一、什麼是Python熵權法? Python熵權法是一種…

    編程 2025-04-28

發表回復

登錄後才能評論