HashMap : initial capacity :初始容量 load factor:负载因子默认为0.75
Note that this implementation is not synchronized. HashMap中的方法是非同步的,不是线程安全的。
<i>fail-fast</i>: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own。当线程对HashMap进行访问(如iterator进行遍历的时候)另一个线程对其结构进行了修改,就会报出ConcurrentModificationException---“快速失败”。
Java Collections Framework
@param <K> the type of keys maintained by this map
@param <V> the type of mapped values
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable 继承AbstractMap类,实现Map,Cloneable,Serializable 接口
DEFAULT_INITIAL_CAPACITY = 16;初始默认容量
MAXIMUM_CAPACITY = 1 << 30;最大容量
DEFAULT_LOAD_FACTOR = 0.75f;初始默认负载因子
threshold = (int)(capacity * loadFactor);
数组的初始容量不应该很大,否则浪费空间,负载因子也不应该很大,否则效率会很低。
hash算法
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
在数组中的位置,这种方法的效率比取余数快很多,一般数组的长度为2的整数倍
static int indexFor(int h, int length) {
return h & (length-1);
}
get方法,如果key的值为null,调用
getForNullKey()方法,hashmap存储是以数组为基础,如果两个key的hash值相等,即在数组中的位置一样,就会把该位置上的数组变为一个链表,不仅要hash值相等,还要求key的值相等才是唯一的。保存数据的时候是以一个entry对象,即有key也有value。若key不为空,且没有找到该key对应的value,最终返回null。
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;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
在HashMap中允许key和value为空但只能有一个,当key值为空时调用
getForNullKey(),其对应数组的第0个元素。
/**
* Offloaded version of get() to look up null keys. Null keys map
* to index 0. This null case is split out into separate methods
* for the sake of performance in the two most commonly used
* operations (get and put), but incorporated with conditionals in
* others.
*/
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)<pre name="code" class="java"><pre name="code" class="java"> <span style="white-space:pre"> </span>return e.value;
<span style="white-space:pre"> </span> }
<span style="white-space:pre"> </span> return null;
}
判断是否包含以key的entry对象
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
向HashMap中插入数据,如果key已经存在,则覆盖旧的value值。若key为null,调用
putForNullKey方法
<pre name="code" class="java">/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
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;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
若为新的entry调用 addEntry(hash, key, value, i),若数组该位置没有其他元素就直接加入,否则加入该链表的头部。 当数组的长度不够时,扩充为原来的两倍,当扩展容量后,原先数组中的每个元素需要重新计算hash值以及在数组中的位置,重新放入,是一个很浪费时间的。
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
putAll方法
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)
return;
if (numKeysToBeAdded > threshold) {
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
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());
}
}
remove方法
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++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
clear方法
public void clear() {
modCount++;
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
containsValue方法 是否包含某个value值
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value))
return true;
return false;
}
由以上的源码可以看到在put、remove、clear方法中涉及到更改HashMap的结构时,都会有 modCount++;,这是保证fail-fast机制