/**
* Hash table based implementation of the <tt>Map</tt> interface. This
* implementation provides all of the optional map operations, and permits
* <tt>null</tt> values and the <tt>null</tt> key. (The <tt>HashMap</tt>
* class is roughly equivalent to <tt>Hashtable</tt>, except that it is
* unsynchronized and permits nulls.) This class makes no guarantees as to
* the order of the map; in particular, it does not guarantee that the order
* will remain constant over time.
HashMap允许空值在源码的注释中已经说明了
HashMap与HashTable唯一的不同就是HashMap不是线程安全的而且允许空
HashMap = 数组 + 单向链表
回顾一下数组和链表:
数组:ArrayList ---> Object [ ]
链表:LinkedList(双向链表)
想看懂HashMap的源码的存储方式,首先得知道HashMap存储的数据结构
内部类
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
关键:Node数组 每个节点又可以纵向的挂节点
那么问题来了,数组的大小是多少呢?最大能有多少呢?什么时候需要扩容,怎么实现扩容?每个节点又能再挂多少个节点?
这些问题大概都是面试种经常爱问的。
默认容量大小
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
默认容量大小为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.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
最大容量为2的30次方
负载因子
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
如16 * 0.75 = 12
当数组中的元素增长到12时,就会扩容
如何扩容??? ---------> double扩容 (通过resize方法) ------>为什么double扩容,后面再详细说明
数组都有最大值了,那么链表有最大值吗
链表总不能一直往下挂把。。。
会有一个限制
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;
每个节点最多挂8
如果超过8,就要改变链表结构
链表 ------ > 平衡二叉树
平衡二叉树 红黑树
Node ------------>TreeNode
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6;
如果小于6,平衡二叉树再变回为链表
TreeNode ------------>Node
那么问题又tm来了
16个大小的数组
那么每塞一个Node的位置怎么确定? node内部类有个变量hash就是用来确定位置的
Object --- hashCode( ) ---> 得到32位整型数(二进制)
在这里顺便解读下hashCode( ) 和 equals( )
在Object中
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();
equal返回true hashCode一样
equal返回false hashCode不一定不一样
根据key求hash的源码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
(h = key.hashCode()) ^ (h >>> 16)
高16位和低16位取异或 把32位hashCode充分利用 使得到的hash值尽可能的不同
有了hash值 那么就可以通过hash值确定节点的位置
hash % 16
与运算效率高 hash & (16-1) 即hash & 15
所以在源码中用&运算替代%运算
回到前面为何会到容量阈值时要double扩容
初始是16
&运算时 hash & (16-1) -----> hash & 15 ------>15的2进制是01111
15的二进制后面几位全是1才能保证与运算得到的值完全取决于hash值
double扩容也是这个原理
16*2=32 hash & ( 32-1 ) -------> hash & 31
double扩容
扩容后还需要为其他节点分担
每次扩容,需要重新计算hash,重新打乱,目的是更好的利用节点
假定节点在原,数组中扩容后,原始节点的位置:
可能在两个位置,原位置或( 原下标+原容量)的位置