HashMap的工作原理
2014-03-08 14:48
323 查看
1. HashMap概述:
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。不保证映射的顺序,特别是它不保证该顺序恒久不变。
2. HashMap的数据结构:
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
数组中存放的键值对:
static class
Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
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;
}
}
Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的
4000
引用,这就构成了链表
3. HashMap的存取实现:
1.存储
public V put(K key, V value) {
//键值为null,采取特别处理
if (key == null)
return putForNullKey(value);
//计算键值的hash值
int hash = hash(key.hashCode());
//计算 h & (length-1),获得在数组中的序号
int i = indexFor(hash, table.length);
//如果该位置上已经有键值对了,遍历这个位置上的链表,如果找到相同的key,则对其value进行更新
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
{
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//该位置上没有存储键值对或者不存在相同的key,在该位置上存储键值对
modCount++;
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//创建键值对,存储在该位置上,其next指向的原来的键值对
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
//实际容量超过预定负载后,把长度扩展一倍
//threshold = (int)(capacity * loadFactor)
//capacity必须为2的幂
//loadFactor默认为0.75,可以在构造函数中制定
if (size++ >= threshold)
resize(2 * table.length);
}
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++;
//null键总是放在数组第一位置上的链表中
addEntry(0, null, value, 0);
return null;
}
2.获取
public V get(Object key) {
//键值为null,采取特别处理
if (key == null)
return getForNullKey();
//计算键值的hash值
int hash = hash(key.hashCode());
//通过hash,计算出其存储位置的索引,在该位置上遍历链表,取得键值对,返回value
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
//在数组的第一个位置上遍历链表,查找key为null的键值对
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
按位取并,作用上相当于取模mod或者取余%。
这意味着数组下标相同,并不表示hashCode相同。
HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。不保证映射的顺序,特别是它不保证该顺序恒久不变。
2. HashMap的数据结构:
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
数组中存放的键值对:
static class
Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
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;
}
}
Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的
4000
引用,这就构成了链表
3. HashMap的存取实现:
1.存储
public V put(K key, V value) {
//键值为null,采取特别处理
if (key == null)
return putForNullKey(value);
//计算键值的hash值
int hash = hash(key.hashCode());
//计算 h & (length-1),获得在数组中的序号
int i = indexFor(hash, table.length);
//如果该位置上已经有键值对了,遍历这个位置上的链表,如果找到相同的key,则对其value进行更新
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
{
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//该位置上没有存储键值对或者不存在相同的key,在该位置上存储键值对
modCount++;
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//创建键值对,存储在该位置上,其next指向的原来的键值对
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
//实际容量超过预定负载后,把长度扩展一倍
//threshold = (int)(capacity * loadFactor)
//capacity必须为2的幂
//loadFactor默认为0.75,可以在构造函数中制定
if (size++ >= threshold)
resize(2 * table.length);
}
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++;
//null键总是放在数组第一位置上的链表中
addEntry(0, null, value, 0);
return null;
}
2.获取
public V get(Object key) {
//键值为null,采取特别处理
if (key == null)
return getForNullKey();
//计算键值的hash值
int hash = hash(key.hashCode());
//通过hash,计算出其存储位置的索引,在该位置上遍历链表,取得键值对,返回value
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
//在数组的第一个位置上遍历链表,查找key为null的键值对
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
4.确定数组index:hashcode % table.length取模
HashMap存取时,都需要计算当前key应该对应Entry[]数组哪个元素,即计算数组下标/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
按位取并,作用上相当于取模mod或者取余%。
这意味着数组下标相同,并不表示hashCode相同。
相关文章推荐
- HashMap的工作原理
- HashMap的工作原理
- HashMap的工作原理
- HashMap的工作原理
- Java HashMap工作原理及实现
- Java HashMap的工作原理
- HashMap的工作原理
- 【转】hashmap的工作原理
- HashMap的工作原理
- HashMap的工作原理--重点----数据结构示意图的理解
- HashMap的工作原理
- Java HashMap的工作原理
- HashMap的工作原理
- HashMap的工作原理
- HashMap的工作原理
- java HashMap 的工作原理详解
- HashMap的工作原理
- Java HashMap的工作原理
- 从头认识java-15.7 Map(6)-介绍HashMap的工作原理-装载因子与性能
- hashMap 的工作原理