1.ArrayMap 綜述
特點:
1).實現(xiàn)了Map接口,并使用int[]數(shù)來存儲key的hash值,數(shù)組的索引用作index,而使用Object[]數(shù)組來存儲key<->value ,這還是比較新穎的。
2).使用二分查找查找hash值在key數(shù)組中的位置,然后根據(jù)這個位置得到value數(shù)組中對應(yīng)位置的元素。
3).和SparseArray類似,當數(shù)據(jù)有幾百條時,性能會比HashMap低50%,因此ArrayMap適用于數(shù)據(jù)量很小的場景。
2.ArrayMap和HashMap的區(qū)別?
1).ArrayMap的存在是為了解決HashMap占用內(nèi)存大的問題,它內(nèi)部使用了一個int數(shù)組用來存儲元素的hashcode,使用了一個Object數(shù)組用來存儲元素,兩者根據(jù)索引對應(yīng)形成key-value結(jié)構(gòu),這樣就不用像HashMap那樣需要額外的創(chuàng)建Entry對象來存儲,減少了內(nèi)存占用。但是在數(shù)據(jù)量比較大時,ArrayMap的性能就會遠低于HashMap,因為 ArrayMap基于二分查找算法來查找元素的,并且數(shù)組的插入操作如果不是末尾的話需要挪動數(shù)組元素,效率較低。
2).而HashMap內(nèi)部基于數(shù)組+單向鏈表+紅黑樹實現(xiàn),也是key-value結(jié)構(gòu), 正如剛才提到的,HashMap每put一個元素都需要創(chuàng)建一個Entry來存放元素,導(dǎo)致它的內(nèi)存占用會比較大,但是在大數(shù)據(jù)量的時候,因為HashMap中當出現(xiàn)沖突時,沖突的數(shù)據(jù)量大于8,就會從單向鏈表轉(zhuǎn)換成紅黑樹,而紅黑樹的插入、刪除、查找的時間復(fù)雜度為O(logn),相對于ArrayMap的數(shù)組而言在插入和刪除操作上要快不少,所以數(shù)據(jù)量上百的情況下,使用HashMap會有更高的效率。
3.如何解決沖突問題?
在ArrayMap中,假設(shè)存在沖突的話,并不會像HashMap那樣使用單向鏈表或紅黑樹來保留這些沖突的元素,而是全部key、value都存儲到一個數(shù)組當中,然后查找的話通過二分查找進行,這也就是當數(shù)據(jù)量大時不宜用ArrayMap的原因了。
4.HashMap的內(nèi)部數(shù)據(jù)結(jié)構(gòu)
數(shù)組+鏈表/紅黑樹
5.HashMap允許空鍵空值么?
HashMap最多只允許一個鍵為Null(多條會覆蓋),但允許多個值為Null。
6.影響HashMap性能的重要參數(shù)
初始容量:創(chuàng)建哈希表(數(shù)組)時的數(shù)量,默認為16。在不指定capacity情況下,初始化容量是16,但不是初始化的時候就創(chuàng)建了一個16大小的數(shù)組,而是在第一次put的時候去判斷是否需要初始化。太小了就有可能頻繁發(fā)生擴容,影響效率。太大了又浪費空間,不劃算。所以,16就作為一個經(jīng)驗值被采用了。
負載因子(擴容因子,加載因子):哈希表在其容量自動增加之前可以達到多滿的一種尺度,默認為 0.75。擴容因子是用來判斷當前數(shù)組(“哈希桶”)什么時候需要進行擴容,假設(shè)因子為0.5,那么HashMap的初始化容量是16,則16*0.5 = 8個元素的時候,HashMap就會進行擴容。
7.為什么擴容因子是0.75?
擴容因子設(shè)置比較大的時候,相當于擴容的門檻就變高了,發(fā)生擴容的頻率變低了,但此時發(fā)生Hash沖突的幾率就會提升,當沖突的元素過多的時候,變成鏈表或者紅黑樹都會增加了查找成本(hash 沖突增加,鏈表長度變長)。而擴容因子過小的時候,會頻繁觸發(fā)擴容,占用的空間變大,比如重新計算Hash等,使得操作性能會比較高。
8.HashMap的工作原理
HashMap是基于hashing的原理,我們使用put(key, value)存儲對象到HashMap中,使用get(key)從HashMap中獲取對象。
9.new HashMap()
在JDK 8中,在調(diào)用new HashMap()的時候并沒有分配數(shù)組堆內(nèi)存,只是做了一些參數(shù)校驗,初始化了一些常量。
10.HashMap什么時候開辟bucket數(shù)組占用內(nèi)存?
在HashMap第一次put的時候調(diào)用resize方法,無論Java 8還是Java 7都是這樣實現(xiàn)的。
11.HashMap是線程不安全的
在jdk1.7中,在多線程環(huán)境下,擴容時會造成環(huán)形鏈或數(shù)據(jù)丟失,在jdk1.8中,在多線程環(huán)境下,會發(fā)生數(shù)據(jù)覆蓋的情況。
12.HashMap在1.8中做了如下優(yōu)化:
①數(shù)組+鏈表改成了數(shù)組+鏈表或紅黑樹;
②鏈表的插入方式從頭插法改成了尾插法;
③擴容的時候1.7需要對原數(shù)組中的元素進行重新hash定位在新數(shù)組的位置,1.8采用更簡單的判斷邏輯,位置不變或索引+舊容量大小;
④在插入時,1.7先判斷是否需要擴容,再插入,1.8先進行插入,插入完成再判斷是否需要擴容。
13.Java8中為什么要引進紅黑樹,是為了解決什么場景的問題?
引入紅黑樹是為了避免hash性能急劇下降,引起HashMap的讀寫性能急劇下降的場景,正常情況下,一般是不會用到紅黑樹的,在一些極端場景下,假如客戶端實現(xiàn)了一個性能拙劣的hashCode方法,可以保證HashMap的讀寫復(fù)雜度不會低于O(lgN)。
14.HashMap如何處理key為null的鍵值對
放置在桶數(shù)組中下標為0的桶中。
15.桶中的元素鏈表何時轉(zhuǎn)換為紅黑樹,什么時候轉(zhuǎn)回鏈表,為什么要這么設(shè)計?
當同一個桶中的元素數(shù)量大于等于8的時候元素中的鏈表轉(zhuǎn)換為紅黑樹,反之,當桶中的元素數(shù)量小于等于6的時候又會轉(zhuǎn)為鏈表,這樣做的原因是避免紅黑樹和鏈表之間頻繁轉(zhuǎn)換,引起性能損耗。
16.ArrayList介紹
ArrayList是一個數(shù)組隊列,相當于動態(tài)數(shù)組。與Java中的數(shù)組相比,它的容量能動態(tài)增長。它繼承于AbstractList,實現(xiàn)了List,RandomAccess,Cloneable,java.io.Serializable這些接口。
17.ArrayList的線程安全性
對ArrayList進行添加元素的操作的時候是分兩個步驟進行的:
1).即第一步先在object[size]的位置上存放需要添加的元素;
2).第二步將size的值增加1。
由于這個過程在多線程的環(huán)境下是不能保證具有原子性的,因此ArrayList在多線程的環(huán)境下是線程不安全的。
18.ArrayList的數(shù)據(jù)結(jié)構(gòu)
ArrayList的底層數(shù)據(jù)結(jié)構(gòu)就是一個數(shù)組,數(shù)組元素的類型為Object類型,對ArrayList的所有操作底層都是基于數(shù)組的。
19.ArrayList常用優(yōu)化方案
1).如果事先能夠估算ArrayList需要的長度,可在構(gòu)造時指定初始數(shù)組長度,節(jié)省擴容開銷。
2).頻繁調(diào)用void add(int index, E element)函數(shù)且指定下標位置靠前時,考慮轉(zhuǎn)換為LinkedList。
3).調(diào)用boolean contains(Object o) 函數(shù)比較頻繁時,可以考慮把元素放入HashSet里進行查詢。
20.ArrayList優(yōu)缺點
優(yōu)點:
1).ArrayList底層以數(shù)組實現(xiàn),是一種隨機訪問模式,再加上它實現(xiàn)了RandomAccess接口,因此查找也就是get的時候非常快。
2).ArrayList在順序添加一個元素的時候非常方便,只是往數(shù)組里面添加了一個元素而已。
3).根據(jù)下標遍歷元素,效率高。
4).根據(jù)下標訪問元素,效率高。
5).可以自動擴容,默認為每次擴容為原來的1.5倍。
6).修改元素和通過下標查詢元素效率高。
7).集合是有順序的。
缺點:
1).插入和刪除元素的效率不高。
2).根據(jù)元素下標查找元素需要遍歷整個元素數(shù)組,效率不高。
3).線程不安全。
4).刪除元素效率低,因為要通過拷貝數(shù)組來實現(xiàn)。
5).大量新增效率低,因為大量新增的時候要不斷進行擴容和數(shù)組的拷貝。
6)清除集合效率低,因為清除功能是通過循環(huán)數(shù)組進行清除的。
7).移除元素后,容量有大量剩余,需要手動調(diào)用trimToSize進行清理。
21.LinkedList()和ArrayList()
ArrayList() : 代表長度可以改變得數(shù)組。可以對元素進行隨機的訪問,向ArrayList()中插入與刪除元素的速度慢。
LinkedList():在實現(xiàn)中采用鏈表數(shù)據(jù)結(jié)構(gòu)。插入和刪除速度快,訪問速度慢。
22.ArrayMap和HashMap的對比
1).存儲方式不同。
HashMap內(nèi)部有一個HashMapEntry<K, V>[]對象,每一個鍵值對都存儲在這個對象里,當使用put方法添加鍵值對時,就會new一個HashMapEntry對象。
2).添加數(shù)據(jù)時擴容時的處理不一樣,進行了new操作,重新創(chuàng)建對象,開銷很大。ArrayMap用的是copy數(shù)據(jù),所以效率相對要高。
3).ArrayMap提供了數(shù)組收縮的功能,在clear或remove后,會重新收縮數(shù)組,是否空間。
4).ArrayMap采用二分法查找。
23.HashMap和HashTable的區(qū)別?
1).HashMap不是線程安全的,效率高一點、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。
2).hashtable是線程安全,不允許有null的鍵和值,效率稍低,方法是是Synchronize的。有contains方法方法。Hashtable 繼承于Dictionary 類。
24.ArrayList和LinkedList的區(qū)別,以及應(yīng)用場景。
ArrayList是基于數(shù)組實現(xiàn)的,ArrayList線程不安全。
LinkedList是基于雙鏈表實現(xiàn)的。
使用場景:
1).如果應(yīng)用程序?qū)Ω鱾€索引位置的元素進行大量的存取或刪除操作,ArrayList對象要遠優(yōu)于LinkedList對象;
2).如果應(yīng)用程序主要是對列表進行循環(huán),并且循環(huán)時候進行插入或者刪除操作,LinkedList對象要遠優(yōu)于ArrayList對象。
25.HashMap與HashSet的區(qū)別?
hashMap:HashMap實現(xiàn)了Map接口,HashMap儲存鍵值對,使用put()方法將元素放入map中,HashMap中使用鍵對象來計算hashcode值,HashMap比較快,因為是使用唯一的鍵來獲取對象。
HashSet實現(xiàn)了Set接口,HashSet僅僅存儲對象,使用add()方法將元素放入set中,HashSet使用成員對象來計算hashcode值,對于兩個對象來說hashcode可能相同,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話,那么返回false。HashSet較HashMap來說比較慢。
26.數(shù)組和鏈表的區(qū)別?
數(shù)組:是將元素在內(nèi)存中連續(xù)存儲的;它的優(yōu)點:因為數(shù)據(jù)是連續(xù)存儲的,內(nèi)存地址連續(xù),所以在查找數(shù)據(jù)的時候效率比較高;它的缺點:在存儲之前,我們需要申請一塊連續(xù)的內(nèi)存空間,并且在編譯的時候就必須確定好它的空間的大小。在運行的時候空間的大小是無法隨著你的需要進行增加和減少而改變的,當數(shù)據(jù)兩比較大的時候,有可能會出現(xiàn)越界的情況,數(shù)據(jù)比較小的時候,又有可能會浪費掉內(nèi)存空間。在改變數(shù)據(jù)個數(shù)時,增加、插入、刪除數(shù)據(jù)效率比較低。
鏈表:是動態(tài)申請內(nèi)存空間,不需要像數(shù)組需要提前申請好內(nèi)存的大小,鏈表只需在用的時候申請就可以,根據(jù)需要來動態(tài)申請或者刪除內(nèi)存空間,對于數(shù)據(jù)增加和刪除以及插入比數(shù)組靈活。還有就是鏈表中數(shù)據(jù)在內(nèi)存中可以在任意的位置,通過應(yīng)用來關(guān)聯(lián)數(shù)據(jù)(就是通過存在元素的指針來聯(lián)系)。
27.LinkedList
LinkedList本質(zhì)上是一個雙向鏈表的存儲結(jié)構(gòu)。
對于元素查詢來說,ArrayList優(yōu)于LinkedList,因為LinkedList要移動指針。對于新增和刪除操作,LinedList比較占優(yōu)勢,因為ArrayList要移動數(shù)據(jù)。
28.HashMap默認的bucket數(shù)組是多大?
默認是16,即時指定的大小不是2的整數(shù)次冪,HashMap也會找到一個最近的2的整數(shù)次冪來初始化桶數(shù)組。
小伙伴如果有興趣的話,歡迎來閱讀更多文章,搜索并關(guān)注公眾號“Android技術(shù)迷”關(guān)注后即可閱讀更多文章,感謝關(guān)注。