------------------------------------------------------
重要的数据结构
------------------------------------------------------
// ******* 这个类是HashMap的一个内部类
// ******* 这个entry本质上就是一个单向链表
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next; // 正因为有这个成员变量,才使entry成为一个单向链表
final int hash;
// 构造方法需要传入的参数有4个:hash值h、Key值k、Value值v、下一个entry节点n
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;
}
// 判断两个entry是否相等,当它们的Key和Value都相等时返回true
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();
}
/**
* This method is invoked whenever the value in an entry is
* overwritten by an invocation of put(k,v) for a key k that's already
* in the HashMap.
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* This method is invoked whenever the entry is
* removed from the table.
*/
void recordRemoval(HashMap<K,V> m) {
}
}
--------------------
// HashMap的结构(这里只写了它的属性,省略了它的方法)
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
// The default initial capacity - MUST be a power of two.
// 默认的初始容量(entry数组的长度)是16,并且值必须是2的整数次幂
static final int DEFAULT_INITIAL_CAPACITY = 16;
//The maximum capacity, used if a higher value is implicitly specified by either of the constructors with arguments - MUST be a power of two <= 1<<30.
// 最大的容量,当传入的容量过大时将使用这个值,并且必须是2的整数次幂且小于2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
The load factor used when none specified in constructor.
// 默认的加载因子是0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// The table, resized as necessary. Length MUST Always be a power of two.
// (储存数据的)entry数组,长度必须是2的整数次幂
transient Entry[] table;
// The number of key-value mappings contained in this map.
// HashMap(即:entry数组)中已存在的键值对的数量
transient int size;
// 阀值,当size的值大于或等于阀值时,需要调整HashMap的容量(threshold = capacity * load factor)
int threshold;
// The load factor for the hash table.
// 加载因子的大小
// 说明:加载因子越大,对空间的利用越充分,但是查找的效率会降低(链表长度会越来越长);
// 加载因子太小,那么表中的数据将过于稀疏(很多空间还没用,就开始扩容了),对空间造成严重浪费。
// 系统默认加载因子为0.75,这是一个比较理想的值,一般情况下我们是无需修改的。
final float loadFactor;
// HashMap被改变的次数
transient int modCount;
}
------------------------------------------------------
存储对象相关的方法:
------------------------------------------------------
// 将key-value对添加到HashMap中,如果已存在,则新值替旧值,并返回旧值
public V put(K key, V value) {
// 1 若key为null,则将该键值对添加到table[0]中。
if (key == null)
return putForNullKey(value);
// 2 若key不为null,则计算该key的哈希值,然后将key-value对添加到该哈希值对应的链表(Entry)中。
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
// 2.1 若该key对应的键值对已经存在,则用新的value取代旧的value;返回了旧的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;
}
}
// 2.2若该key对应的键值对不存在,则将key-value对添加到table中
//(注:transient Entry[] table;// table是一个Entry数组,查看Entry的源码,会发现:Entry实质上是一个链表)
modCount++;
//将key-value添加到table[i]处,返回null后,退出。
addEntry(hash, key, value, i);
return null;
}
--------------------
// putForNullKey()的作用是将key为null的键值对添加到table[0]位置
private V putForNullKey(V value) {
// 若已经存在key为null的键值对,则替换并返回旧值
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;
}
}
// 如果不存在key为null的键值对,则直接添加到table[0]处!
modCount++;
addEntry(0, null, value, 0);
return null;
}
--------------------
// 根据传入的hash值算出entry数组对应的下标,即:hash算法的实现
static int indexFor(int h, int length) {
return h & (length-1);
}
说明:
1, 除留余数法(取模法):用hash值除以关键字后得到的余数为哈希地址,即用hash值对关键字取模(MOD)的结果为哈希地址。
这是一种最简单,也是最常用的构造哈希函数的方法,Hashtable就是采用这种方法实现的(用hash值对entry数组的length取模),这种方法基本能保证元素在哈希表中散列的比较均匀,但取模会用到除法运算,效率很低。
除留余数法对关键字的选取很重要,由经验得知:一般情况下,选关键字为质数或不包含小于20的质因数的合数。
2, HashMap中则通过h&(length-1)的方法来代替取模,同样实现了均匀的散列,但效率要高很多。
哈希表的容量(entry数组的length)必须是2的整数次幂的原因:
首先,length为2的整数次幂的话,h&(length-1)就相当于对length取模,这样便保证了散列的均匀,同时也提升了效率;
其次,length为2的整数次幂的话,为偶数,这样length-1为奇数,奇数的最后一位是1,这样便保证了h&(length-1)的最后一位可能为0,也可能为1(这取决于hash值),即:与后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性。
如果length为奇数的话,很明显length-1为偶数,它的最后一位是0,这样h&(length-1)的最后一位肯定为0,即只能为偶数,这样任何hash值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间。
--------------------
// 添加一个entry
void addEntry(int hash, K key, V value, int bucketIndex) {
// table[bucketIndex]即put(K key, V value)方法中的table[i],这里只是把下标的变量名由i换成bucketIndex罢了
// ******* 把已有的链表备份一下
Entry<K,V> e = table[bucketIndex];
// 把新添加进来的entry放到链表的头部,即:把旧的链表(Entry)赋给新添加进来的Entry的 Entry<K,V> next;字段
table[bucketIndex] = new Entry<>(hash, key, value, e);
// 如果entry数组的实际大小(已放入entry的数量) 大于或等于阈值threshold,则
// 将会创建一个是原来entry数组(即table)大小两倍的entry数组,并将原来的entry对象放入新的entry数组中。
if (size++ >= threshold)
resize(2 * table.length);
}
--------------------
// 重新调整entry数组的length,并将原来的entry对象放入新的entry数组中
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
// 新建一个entry数组
Entry[] newTable = new Entry[newCapacity];
// 将原来的entry对象放入新的entry数组中
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
说明: 扩容是一个相当耗时的操作,因为它需要重新计算这些元素在新的数组中的位置并进行复制处理。
因此,我们在用HashMap的时,最好能提前预估下HashMap中元素的个数,这样有助于提高HashMap的性能。
--------------------
// 将原来的entry对象放入新的entry数组中
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
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);
}
}
}
------------------------------------------------------
获取对象相关的方法:
------------------------------------------------------
// 获取key对应的对象的值
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
Object k;
// 判断key是否相等。注:如果hash值相同,就调用key.equals()方法比较
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
// 没有找的则返回null
return null;
}
--------------------
// 获取key为null的对象的值
// HashMap将key为null的元素存储在table[0]位置,但不一定是该链表的第一个位置,因为有可能用其他key的hash值算出的bucket位置也为0!
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
}
原文:http://blog.csdn.net/wodewutai17quiet/article/details/46044311