Java 中的HashMap

<h3>寫在開始之前:</h3>

Java為數(shù)據(jù)結(jié)構(gòu)中的映射定義了一個接口java.util.Map,此接口主要有四個常用的實(shí)現(xiàn)類,分別是HashMap、Hashtable、LinkedHashMap和TreeMap。

(1) HashMap:它根據(jù)鍵的hashCode值存儲數(shù)據(jù),大多數(shù)情況下可以直接定位到它的值,因而具有很快的訪問速度,但遍歷順序卻是不確定的。 HashMap最多只允許一條記錄的鍵為null,允許多條記錄的值為null。HashMap非線程安全,即任一時刻可以有多個線程同時寫HashMap,可能會導(dǎo)致數(shù)據(jù)的不一致。如果需要滿足線程安全,可以用 Collections的synchronizedMap方法使HashMap具有線程安全的能力,或者使用ConcurrentHashMap。

(2) Hashtable:Hashtable是遺留類,很多映射的常用功能與HashMap類似,不同的是它承自Dictionary類,并且是線程安全的,任一時間只有一個線程能寫Hashtable,并發(fā)性不如ConcurrentHashMap,因?yàn)镃oncurrentHashMap引入了分段鎖。Hashtable不建議在新代碼中使用,不需要線程安全的場合可以用HashMap替換,需要線程安全的場合可以用ConcurrentHashMap替換。

(3) LinkedHashMap:LinkedHashMap是HashMap的一個子類,保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的,也可以在構(gòu)造時帶參數(shù),按照訪問次序排序。

(4) TreeMap:TreeMap實(shí)現(xiàn)SortedMap接口,能夠把它保存的記錄根據(jù)鍵排序,默認(rèn)是按鍵值的升序排序,也可以指定排序的比較器,當(dāng)用Iterator遍歷TreeMap時,得到的記錄是排過序的。如果使用排序的映射,建議使用TreeMap。在使用TreeMap時,key必須實(shí)現(xiàn)Comparable接口或者在構(gòu)造TreeMap傳入自定義的Comparator,否則會在運(yùn)行時拋出java.lang.ClassCastException類型的異常。

本文介紹的HashMap主要基于JDK 1.7 嘍,JDK 1.8 里對HashMap做了一些優(yōu)化,比如采用使用紅黑樹結(jié)構(gòu)、優(yōu)化擴(kuò)容算法等在本文不做介紹。

<h3>背景介紹</h3>

在做改版過程中,有個地方是5個并排的按鈕,分別跳轉(zhuǎn)到不同的頁面。機(jī)智如我寫下了如下代碼來初始化5個按鈕(Key是按鈕對應(yīng)資源文件ID,Value是按鈕點(diǎn)擊事件):

private HashMap<Integer, View.OnClickListener> resources = new HashMap<>();

結(jié)果發(fā)現(xiàn)5個按鈕的顯示順序與添加順序不一致。才突然間記起HashMap是不能保證順序的。那么為什么它不能保證順序呢?它是如何存儲數(shù)據(jù)的呢?

<h4>簡介</h4>

Java 中HashMap是Map的最常用的實(shí)現(xiàn)類。HashMap 是一個散列表,它存儲的內(nèi)容是鍵值對(key-value)映射。HashMap 繼承于AbstractMap,實(shí)現(xiàn)了Map、Cloneable、java.io.Serializable接口。HashMap 的實(shí)現(xiàn)不是同步的,這意味著它不是線程安全的。它的key\value都可以為null。

<h4>數(shù)據(jù)存取</h4>

HashMap是基于Hashing的原理,我們一般使用put(key, value)存儲對象到HashMap中,使用get(key)從HashMap中獲取對象。

當(dāng)我們給put()方法傳遞鍵和值時,我們先對鍵調(diào)用hashCode()方法,返回的hashCode用于找到Bucket位置來儲存Entry對象。

如果存儲的時候遇到不同的對象有相同的hashCode呢?
需要注意的是,Bucket是一個LinkedList結(jié)構(gòu)。如果多個元素的Key對象的HasCode相同,則存儲于同一個LinkedList。而LinkedList中包含了每個元素的鍵值信息,所以當(dāng)取數(shù)據(jù)遇到Key對應(yīng)的HashCode相同的情況也可以通過Key值的比較來查找到想要的元素。

此處輸入圖片的描述

<h4>加載因子</h4>

加載因子是表示Hsah表中元素的填滿的程度.加載因子越大,填滿的元素越多,空間利用率高了,但沖突的機(jī)會加大了。反之,加載因子越小填滿的元素越少,好處是沖突的機(jī)會減小了,但空間浪費(fèi)多了。沖突的機(jī)會越大,則查找的成本越高.反之,查找的成本越小.因而,查找時間就越小.因此,必須在 "沖突的機(jī)會"與"空間利用率"之間尋找一種平衡與折衷. 這種平衡與折衷本質(zhì)上是數(shù)據(jù)結(jié)構(gòu)中有名的"時-空"矛盾的平衡與折衷.
默認(rèn)情況下HashMap是自增的,它的初始長度是16,加載因子是 0.75 。當(dāng)HashMap中的條目數(shù)超出了加載因子與當(dāng)前容量的乘積時,通過調(diào)用 rehash 方法將容量 <strong> 翻倍 </strong>。

<h4>Android中的HashMap</h4>

因?yàn)镠ashMap內(nèi)存使用不充足。在內(nèi)存相對吃緊的移動客戶端簡直是浪費(fèi)嘛。
所以Google提供了兩個新的數(shù)據(jù)結(jié)構(gòu) SparseArray和ArrayMap來供大家使用。但是這兩個數(shù)據(jù)結(jié)構(gòu)由于各自的局限性,并不能完全取代HashMap。SparseArray只能適用于key為int的數(shù)據(jù)結(jié)構(gòu)。而ArrayMap的問題是在大數(shù)據(jù)量下性能較低。所以,三者之中具體選用哪個還得看情況而定哦。

說了這么多,那5個按鈕到底如何保證順序呢?
答:使用LinkedHashMap!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文轉(zhuǎn)自 https://zhuanlan.zhihu.com/p/21673805 美團(tuán)點(diǎn)評技術(shù)團(tuán)隊(duì)· 3 個月...
    抓兔子的貓閱讀 1,158評論 0 1
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,901評論 11 349
  • Java SE 基礎(chǔ): 封裝、繼承、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨(dú)立的整體,并盡...
    Jayden_Cao閱讀 2,259評論 0 8
  • 接口/抽象類意義規(guī)范、擴(kuò)展、回調(diào)為其子類提供一個公共的類型 封裝子類中得重復(fù)內(nèi)容 定義抽象方法,子類雖然有不同的實(shí)...
    MigrationUK閱讀 2,354評論 1 28
  • 1.An election is a process wherein people decide on who w...
    周艷杰閱讀 342評論 0 0

友情鏈接更多精彩內(nèi)容