您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“JDK的HashMap怎么使用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“JDK的HashMap怎么使用”吧!
首先先來分析一下HashMap
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量2的15次方+1;
static final int MAXIMUM_CAPACITY = 1 << 30;
默認(rèn)加載因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
內(nèi)部實(shí)現(xiàn)的機(jī)制是用具有鍵值對(duì)格式的單個(gè)的entry數(shù)組實(shí)現(xiàn):
transient Entry[] table;
static class Entry<K,V> implements Map.Entry<K,V> {
final K key; //鍵
V value; // 值
Entry<K,V> next; //下一個(gè)
final int hash; //哈西值
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
public final K getKey() {
return key;
}
public final V getValue() {
return value;
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
void recordAccess(HashMap<K,V> m) {
}
void recordRemoval(HashMap<K,V> m) {
}
}
當(dāng)前的數(shù)組大小:
transient int size;
構(gòu)造函數(shù)初始化:
設(shè)置初始化的容積和加載因子
public HashMap(int initialCapacity, float loadFactor) {
//初始化容積必須大于0
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//超過最大容積的時(shí)候,那么等于最大容積
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//初始化加載因子;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
//找出一個(gè)數(shù)的平方大于當(dāng)前給出的初始化容積,比如是初始化是10的話,那么最終的容積就是16
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
//用容積來初始化數(shù)組大??;size當(dāng)前還是為0;
table = new Entry[capacity];
//初始化動(dòng)作,可以留給子類實(shí)現(xiàn),模板模式的應(yīng)用
init();
}
這是一個(gè)hashcode的轉(zhuǎn)換算法;他能夠把對(duì)象的hashcode轉(zhuǎn)換成為小于length-1的整數(shù)作為數(shù)組的下標(biāo);那么勢(shì)必會(huì)出現(xiàn)重合
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
我們先看增加方法:
public V put(K key, V value) {
//判斷鍵值是否為空;
if (key == null)
return putForNullKey(value);
//得到hashcode
int hash = hash(key.hashCode());
//得到一個(gè)小于數(shù)組長度(取的是與操作)的下標(biāo)
int i = indexFor(hash, table.length);
//在數(shù)組中找到該下標(biāo)的entry值;
//事實(shí)上entry也是一個(gè)鏈表,相對(duì)于LinkedList來說,他的entry是單向的
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果存在鍵值是同一對(duì)象的entry,那么用新值覆蓋舊值,不存在則再往下找,知道末尾
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//增加修改次數(shù)
modCount++;
//增加這個(gè)entry到該下標(biāo)列表的首部
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//可見是放在該下標(biāo)鏈表的第一位的
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
//在這里增加了size;
if (size++ >= threshold)
resize(2 * table.length);
}
如果key是null的話:
那么他可以不用通過hashcode定位數(shù)組中的隊(duì)列下標(biāo)-_-||事實(shí)上他也沒有hashcode;規(guī)定在0位存放這個(gè)鏈表的頭;可見在HashMap中是可以
存放null的key的;但是正因?yàn)槠錄]有hashcode那么就只能存放一個(gè)元素,而不是像其他一樣能存放多個(gè);但是另外我們可見,你可以考慮把使
用的最多的值的鍵設(shè)置成為null,因?yàn)樗钦业降淖羁斓模?/p>
private V putForNullKey(V value) {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(0, null, value, 0);
return null;
}
上面說了存放了,下面我們?cè)賮砜纯慈绾稳〕鰜戆眩?/p>
final Entry<K,V> getEntry(Object key) {
//經(jīng)過算法得到這個(gè)key對(duì)應(yīng)的hashcode,可見hashcode是固定的對(duì)應(yīng),而不是隨機(jī)的;如果是null的話為0,經(jīng)過與操作還是0,直接
//定位到table[0]否則查找出下標(biāo)
int hash = (key == null) ? 0 : hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
//匹配這個(gè)key的hashcode和數(shù)值,可見不是同一的值其hashcode是可能一樣的,否則如果是一一對(duì)應(yīng)則沒必要匹配key了
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
//沒有找到,為空;
return null;
我們?cè)賮砜纯雌淙萘渴侨绾卧鲩L的:
public void putAll(Map<? extends K, ? extends V> m) {
//得到新增加進(jìn)來的元素的個(gè)數(shù)
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)
return;
//如果新增加的元素個(gè)數(shù)大于原來容積*加載因子;可見我們必須要擴(kuò)充容量了
if (numKeysToBeAdded > threshold) {
//那么我們?cè)黾拥娜萘繉⑹窃撔略鲈貍€(gè)數(shù)+預(yù)留空間的總數(shù)
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
//如果我們現(xiàn)在的表的容量小于新增加的容量,那么我們擴(kuò)展兩倍,如果還小,再擴(kuò)大兩倍,直到他的大于當(dāng)前需要的容量
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
//添置新的"家具"
for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<? extends K, ? extends V> e = i.next();
put(e.getKey(), e.getValue());
}
}
用新的容積開辟了一塊足夠大的存儲(chǔ)空間,
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
并且把家具從以前的“小房子”幫到了“大房子”
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
//得到各個(gè)數(shù)組元素的鏈表的頭
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null; //一定要把原來的“小房子”收拾好,方便垃圾回收;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
新增,查找,都看過了,我們來看一下刪除:
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
final Entry<K,V> removeEntryForKey(Object key) {
//定位查找到鏈表表頭;
int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
Entry<K,V> prev = table[i];
Entry<K,V> e = prev;
while (e != null) {
Entry<K,V> next = e.next;
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
//若能找到,修改前后的鏈表節(jié)點(diǎn)的指向,從中刪除;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
//方便鏈表往下傳遞;保存prev是因?yàn)槭菃蜗蜴湵恚砸坏┱业降哪繕?biāo)節(jié)點(diǎn),無法通過該節(jié)點(diǎn)得到錢一個(gè)節(jié)點(diǎn),就無法更改前
一個(gè)節(jié)點(diǎn)的指//向,所以要保存prev
prev = e;
e = next;
}
return e;
}
現(xiàn)在我們?cè)倏匆幌氯绾螁为?dú)取到他的鍵值集合或者值集合:
values實(shí)現(xiàn)了一個(gè)內(nèi)部私有類;
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
//AbstractCollection<V> 把一些基本的curd委托給了iteartor,所以只需重載其獲取iteartor的方法;
private final class Values extends AbstractCollection<V> {
//重載了iteartor
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
下面是該iteartor的獲?。浩渲兄饕氖强纯次覀儷@取的value的collection是如何排序的;
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // next entry to return
int expectedModCount; // For fast-fail
int index; // current slot
Entry<K,V> current; // current entry
HashIterator() {
expectedModCount = modCount;
//如果該HashMap有元素
if (size > 0) { // advance to first entry
Entry[] t = table;
然后把next和index都調(diào)至該table數(shù)組的鏈表頭中不為空的地方;
while (index < t.length && (next = t[index++]) == null)
;
}
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
//代表這個(gè)鏈表遍歷完成了,需要開始遍歷下一個(gè)table下標(biāo)的鏈表了
if ((next = e.next) == null) {
Entry[] t = table;
//找到下一個(gè)不為空的table元素
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
ValueIterator :
private final class ValueIterator extends HashIterator<V> {
public V next() {
return nextEntry().value;
}
}
這是我們會(huì)問加入我在這個(gè)value的collection中增加元素會(huì)出現(xiàn)什么樣的情況呢,次序會(huì)不會(huì)打亂,對(duì)不起,AbstractCollection不存在add
的,iteartor也是不允許增加元素的;
對(duì)于來說keySet來說是同理的,不過因?yàn)閗ey是不存在一樣的,所以,我們不會(huì)返回collection而返回set,避免了重復(fù)key的出現(xiàn)
到此,相信大家對(duì)“JDK的HashMap怎么使用”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。