Java之HashMap案例詳解
概述
這篇文章,我們打算探索一下Java集合(Collections)框架中Map接口中HashMap的實(shí)現(xiàn)。Map雖然是Collctions框架的一部分,但是Map并沒有實(shí)現(xiàn)Collection接口,而Set和List是實(shí)現(xiàn)Collection接口的。
簡單來說,HashMap主要通過key存儲value值,并且提供了添加,獲取和操作存儲value的方法。HashMap的實(shí)現(xiàn)基于HashTable。
HashMap內(nèi)部呈現(xiàn)
Key-value對在內(nèi)部是以buckets的方式存儲在一起,最終成為一個表。存儲和檢索操作的時間是固定的,也就是時間復(fù)雜度為O(1)。
這篇文章暫時不過于涉及HashMap的底層,我們先對HashMap有個整體認(rèn)知。
put方法
Map中通過put方法來存儲一個value。
/** * 建立鍵值對應(yīng)關(guān)系,如果之前已經(jīng)存在對應(yīng)的key, * 返回之前存儲的value,之前如果不存在對應(yīng)的key,返回null */ public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
知識點(diǎn)一: 當(dāng)Map的調(diào)用put方法的時候,key對象被調(diào)用hashCode()方法,獲得一個hash值供hashmap使用。
我們創(chuàng)建一個對象來證實(shí)一下。
public class MyKey { private int id; @Override public int hashCode() { System.out.println("調(diào)用 hashCode()"); return id; } // constructor, setters and getters } @Test public void mapKeyTest(){ HashMap<MyKey,String> map = new HashMap<MyKey, String>(); String retV = map.put(new MyKey(1),"value1"); }
可以看到控制臺的輸出信息
調(diào)用 hashCode()
知識點(diǎn)二: hash()方法計(jì)算出的hash值可以標(biāo)識它在buckets數(shù)組中的索引位置。
HashMap的hash()方法如下:可以與put方法進(jìn)行關(guān)聯(lián)。
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
HashMap有一個特點(diǎn),它可以存儲null的key和null的value。當(dāng)key時null的時候,執(zhí)行put方法,它會自動分配hash為0. 這也意味著key為null的時候沒有hash操作,這樣就避免了空指針異常。
get() 方法
為了獲取存儲在hashMap中的對象,我們需要知道與它對應(yīng)的key。然后通過get方法把對應(yīng)的key傳到參數(shù)里。調(diào)用HashMap的get方法的時候,也會調(diào)用key對象的hashCode方法。
@Test public void mapKeyTest(){ HashMap<MyKey,String> map = new HashMap<MyKey, String>(); MyKey key1 = new MyKey(1); map.put(key1,"value1"); String retV = map.get(key1); }
控制臺上可以看到兩行輸出
調(diào)用 hashCode() 調(diào)用 hashCode()
HashMap中的集合視圖
HashMap提供了三種方式,讓我們可以把key和value作為其它集合來使用。
Set<K> keys = map.keySet() Collection<V> values = map.values() Set<Entry<K, V>> entries = map.entrySet();
注意: 在iteators創(chuàng)建完畢后,對map的任何結(jié)構(gòu)修改,都會拋出一個異常。
@Test public void givenIterator_whenFailsFastOnModification_thenCorrect() { Map<String, String> map = new HashMap<>(); map.put("name", "baeldung"); map.put("type", "blog"); Set<String> keys = map.keySet(); Iterator<String> it = keys.iterator(); map.remove("type"); while (it.hasNext()) { String key = it.next(); } } // 會拋出java.util.ConcurrentModificationException異常
HashMap中唯一允許的修改是在iterator中移除元素。
public void givenIterator_whenRemoveWorks_thenCorrect() { Map<String, String> map = new HashMap<>(); map.put("name", "baeldung"); map.put("type", "blog"); Set<String> keys = map.keySet(); Iterator<String> it = keys.iterator(); while (it.hasNext()) { it.next(); it.remove(); } assertEquals(0, map.size()); }
HashMap在iterator上的性能相比于LinkedHashMap和treeMap,性能非常糟糕。最差情況下為O(n),n為hashmap中條目的個數(shù)。
HashMap性能
HashMap的性能主要有兩個參數(shù)影響,初始容量和負(fù)載因子。初始容量為Map底層桶數(shù)組的長度,負(fù)載因子為當(dāng)桶容量的長度為多大的時候,重新開辟新的空間。
int threshold; final float loadFactor;
默認(rèn)的初始容量為16,默認(rèn)的負(fù)載因子為0.75. 我們也可以自定義它們的值。
Map<String,String> hashMapWithCapacity=new HashMap<>(32); Map<String,String> hashMapWithCapacityAndLF=new HashMap<>(32, 0.5f);
初始容量:
大的初始容量用于條目數(shù)較多,但是少量迭代(iteration)
小的初始容量用于條目數(shù)較少,但是多次迭代(iteration)
負(fù)載因子:
0.75是一個很折衷的方案了。在我們初始化HashMap的時候,初始容量和負(fù)載因子都應(yīng)該考慮在內(nèi),比如為了減少重新hash的操作,初始容量乘以負(fù)載因子應(yīng)該大于能存儲的最大條目數(shù),這樣就不會發(fā)生重新hash的操作。
最后
HashMap內(nèi)部有很多東西值得探索,這篇僅僅對HashMap做了一層表面的分析。接下來會深入分析。
到此這篇關(guān)于Java之HashMap案例詳解的文章就介紹到這了,更多相關(guān)Java之HashMap內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java編程用棧來求解漢諾塔問題的代碼實(shí)例(非遞歸)
這篇文章主要介紹了Java編程用棧來求解漢諾塔問題的代碼實(shí)例(非遞歸),具有一定參考價(jià)值,這里給大家分享下,供朋友們參考。2017-10-10詳解java動態(tài)代理的2種實(shí)現(xiàn)方式
目前Java開發(fā)包中包含了對動態(tài)代理的支持,但是其實(shí)現(xiàn)只支持對接口的的實(shí)現(xiàn)。這篇文章主要介紹了詳解java動態(tài)代理的2種實(shí)現(xiàn)方式 ,有興趣的可以了解一下。2016-11-11Mybatis通過攔截器實(shí)現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換
這篇文章主要為大家詳細(xì)介紹了Mybatis如何通過攔截器實(shí)現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12Java 將PPT幻燈片轉(zhuǎn)為HTML文件的實(shí)現(xiàn)思路
本文以Java程序代碼為例展示如何通過格式轉(zhuǎn)換的方式將PPT幻燈片文檔轉(zhuǎn)為HTML文件,本文通過實(shí)例代碼圖文相結(jié)合給大家分享實(shí)現(xiàn)思路,需要的朋友參考下吧2021-06-06Java使用抽象工廠模式實(shí)現(xiàn)的肯德基消費(fèi)案例詳解
這篇文章主要介紹了Java使用抽象工廠模式實(shí)現(xiàn)的肯德基消費(fèi)案例,較為詳細(xì)的分析了抽象工廠模式的定義、原理并結(jié)合實(shí)例形式分析了Java使用抽象工廠模式實(shí)現(xiàn)肯德基消費(fèi)案例的步驟與相關(guān)操作技巧,需要的朋友可以參考下2018-05-05