搜尋
首頁Javajava教程Java提高篇(三三)-----Map總結

        在前面LZ詳細介紹了HashMap、HashTable、TreeMap的實現方法,從資料結構、實現原理、源碼分析三個方面進行闡述,對這個三個類應該有了比較清晰的了解,下面LZ就Map做一個簡單的總結。

        推薦閱讀:

         

java增進篇(二五)—–HashTable

       

Java提高篇(二六)-----hashCode Map

一、Map概述

        先看Map的結構示意圖

「鍵值」對映射的抽象介面。此映射不包括重複的鍵,一個鍵對應一個值。

       

SortedMap:有序的鍵值對接口,繼承Map接口。

        NavigableMap:繼承SortedMap,具有了針對給定搜尋目標傳回給定的導覽方法的介面。

        AbstractMap:實現了Map中大部分的函數介面。它減少了「Map的實作類別」的重複編碼。

        Dictionary:任何可將鍵映射到對應值的類別的抽象父類。目前被Map介面取代。

        TreeMap:有序散列表,並實現SortedMap 介面,底層透過紅黑樹實現。

        HashMap:是基於「拉鍊法」所實現的散列表。底層採用「數組+鍊錶」實作。

        WeakHashMap:基於「拉鍊法」所實現的雜湊列表。

        HashTable:基於「拉鍊法」所實現的雜湊。

       

二、內部雜湊: 雜湊映射技術

        幾乎所有通用Map都使用雜湊映射技術。對於我們程式設計師來說我們必須要對其有所了解。

        哈希映射技術是一種非常簡單的元素映射到陣列的技術。由於雜湊映射採用的是數組結果,那麼必然存在一中用於確定任意鍵存取數組的索引機制,該機制能夠提供一個小於數組大小的整數,我們將該機制稱為雜湊函數。在Java中我們不必為尋找這樣的整數而大傷腦筋,因為每個物件都必定存在一個傳回整數值的hashCode方法,而我們需要做的就是將其轉換為整數,然後再將該值除以數組大小取餘即可。如下:

int hashValue = Maths.abs(obj.hashCode()) % size;

下面是HashMap、HashTable的:

----------HashMap------------
//计算hash值
static int hash(int h) {
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
//计算key的索引位置
static int indexFor(int h, int length) {
        return h & (length-1);
}
-----HashTable--------------
int index = (hash & 0x7FFFFFFF) % tab.length;     //确认该key的索引位置

 位置的索引代表了該節點在數組中的該節點。下圖為雜湊映射的基本原理圖:


         在該圖中1-4步驟是找到此元素在陣列中位置,5-8步驟是將該元素插入陣列中。在插入的過程中會遇到一點小挫折。在眾多肯能存在多個元素他們的hash值是一樣的,這樣就會得到相同的索引位置,也就說多個元素會映射到相同的位置,這個過程我們稱之為「衝突」。解決衝突的辦法是在索引位置處插入連結列表,並簡單地將元素新增至此連結列表。當然也不是簡單的插入,在HashMap中的處理過程如下:取得索引位置的鍊錶,如果該鍊錶為null,則將該元素直接插入,否則透過比較是否存在與該key相同的key,若存在則覆蓋原來key的value並傳回舊值,否則將該元素保存在鏈頭(最先保存的元素放在鏈尾)。以下是HashMap的put方法,該方法詳細展示了計算索引位置,將元素插入到適當的位置的全部過程:

public V put(K key, V value) {
        //当key为null,调用putForNullKey方法,保存null与table第一个位置中,这是HashMap允许为null的原因
        if (key == null)
            return putForNullKey(value);
        //计算key的hash值
        int hash = hash(key.hashCode());                 
        //计算key hash 值在 table 数组中的位置
        int i = indexFor(hash, table.length);            
        //从i出开始迭代 e,判断是否存在相同的key
        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
            Object k;
            //判断该条链上是否有hash值相同的(key相同)
            //若存在相同,则直接覆盖value,返回旧value
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;    //旧值 = 新值
                e.value = value;
                e.recordAccess(this);
                return oldValue;     //返回旧值
            }
        }
        //修改次数增加1
        modCount++;
        //将key、value添加至i位置处
        addEntry(hash, key, value, i);
        return null;
    }

         HashMap
      如果我們查看其它的Map,發現其原理都差不多!

三、Map最佳化

         首先被映射,假設哈希映射的內部數組的大小只有1,所有的元素都將只有1,所有的元素都構成一個較大的位(從而一個長的大小(從而較大的大小)(從而較大地1,所有的元素都只有1,所有的元素都構成一個較大(從而一個長的)鍊錶。由於我們更新、訪問都要對這條鍊錶進行線性搜索,這樣勢必會降低效率。我們假設,如果存在一個非常大數組,每個位置鍊錶處都只有一個元素,在進行訪問時計算其 index 值就會獲得該對象,這樣做雖然會提高我們搜索的效率,但是它浪費了控制項。誠然,雖然這兩種方式都是極端的,但是它給我們提供了一種優化思路:使用一個較大的數組讓元素能夠均勻分佈。

在Map有兩個會影響到其效率,一是容器的初始化大小、二是負載因子。

3.1、調整實現大小

         在哈希映射表中,以內部數組中的每個位置稱為「儲存桶」(bucket),而可用桶數的儲存空間(即可用桶的數組”大小)稱作容量(capacity),我們為了讓Map物件能夠有效地處理任意數的元素,將Map設計成可以調整自身的大小。我們知道當Map中的元素達到一定量的時候就會調整容器本身的大小,但是這個調整大小的過程其開銷是非常大的。調整大小需要將原來所有的元素插入到新數組中。我們知道index = hash(key) % length。這樣可能會導致原先衝突的鍵不在衝突,不衝突的鍵現在衝突的,重新計算、調整、插入的過程開銷是非常大的,效率也比較低。所以,如果我們開始知道Map的預期大小值,將Map調整的足夠大,則可以大幅減少甚至不需要重新調整大小,這很有可能會提高速度。

下面是HashMap調整容器大小的過程,透過下面的程式碼我們可以看到其擴容過程的複雜性:

🎜🎜
void resize(int newCapacity) {
            Entry[] oldTable = table;    //原始容器
            int oldCapacity = oldTable.length;    //原始容器大小
            if (oldCapacity == MAXIMUM_CAPACITY) {     //是否超过最大值:1073741824
                threshold = Integer.MAX_VALUE;
                return;
            }

            //新的数组:大小为 oldCapacity * 2
            Entry[] newTable = new Entry[newCapacity];    
            transfer(newTable, initHashSeedAsNeeded(newCapacity));
            table = newTable;
           /*
            * 重新计算阀值 =  newCapacity * loadFactor >  MAXIMUM_CAPACITY + 1 ? 
            *                         newCapacity * loadFactor :MAXIMUM_CAPACITY + 1
            */
            threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);   
        }
        
        //将元素插入到新数组中
        void transfer(Entry[] newTable, boolean rehash) {
            int newCapacity = newTable.length;
            for (Entry<K,V> e : table) {
                while(null != e) {
                    Entry<K,V> next = e.next;
                    if (rehash) {
                        e.hash = null == e.key ? 0 : hash(e.key);
                    }
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                }
            }
        }

3.2、负载因子

         为了确认何时需要调整Map容器,Map使用了一个额外的参数并且粗略计算存储容器的密度。在Map调整大小之前,使用”负载因子”来指示Map将会承担的“负载量”,也就是它的负载程度,当容器中元素的数量达到了这个“负载量”,则Map将会进行扩容操作。负载因子、容量、Map大小之间的关系如下:负载因子 * 容量 > map大小  ----->调整Map大小。

         例如:如果负载因子大小为0.75(HashMap的默认值),默认容量为11,则 11 * 0.75 = 8.25 = 8,所以当我们容器中插入第八个元素的时候,Map就会调整大小。

        负载因子本身就是在控件和时间之间的折衷。当我使用较小的负载因子时,虽然降低了冲突的可能性,使得单个链表的长度减小了,加快了访问和更新的速度,但是它占用了更多的控件,使得数组中的大部分控件没有得到利用,元素分布比较稀疏,同时由于Map频繁的调整大小,可能会降低性能。但是如果负载因子过大,会使得元素分布比较紧凑,导致产生冲突的可能性加大,从而访问、更新速度较慢。所以我们一般推荐不更改负载因子的值,采用默认值0.75.

最后

        推荐阅读:

        java提高篇(二三)—–HashMap

        java提高篇(二五)—–HashTable

        Java提高篇(二六)-----hashCode

        Java提高篇(二七)—–TreeMap

 


以上就是Java提高篇(三三)-----Map总结的内容,更多相关内容请关注PHP中文网(www.php.cn)!


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?Mar 17, 2025 pm 05:46 PM

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?Mar 17, 2025 pm 05:44 PM

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?Mar 17, 2025 pm 05:43 PM

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Mar 17, 2025 pm 05:35 PM

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器