Map:存储key-value
HashMap<String, Integer> map = new HashMap<>();//底层执行Entry[] table = new Entry[16];
map.put("name", 20);//将name和20封装到Entry对象中,并考虑将此对象添加到table数组中
public class HashMap<K,V> implements Map.Entry<K,V>{
transient Entry<K,V>[] table;
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
int hash;//使用key得到的哈希值2进行赋值
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
//略
}
}
//table数组的默认初始化长度
static final int DEFAULT_INITIAL_CAPACITY = 16;
//哈希表
transient Entry<K,V>[] table;
//哈希表中key-value的个数
transient int size;
//临界值、阈值(扩容的临界值)
int threshold;
//加载因子
final float loadFactor;
//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
//DEFAULT_INITIAL_CAPACITY:默认初始容量16
//DEFAULT_LOAD_FACTOR:默认加载因子0.75
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);//调用HashMap(int initialCapacity, float loadFactor)方法
}
// 初始容量16,loadFactor加载因子0.75
public HashMap(int initialCapacity, float loadFactor) {
...
//循环计算得到table数组的最终长度(保证capacity是2的整次幂)
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
//加载因子,初始化为0.75
this.loadFactor = loadFactor;
// 确定了临界值为12
threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
//初始化table数组,容量为capacity
table = new Entry[capacity];
...
}
public V put(K key, V value) {
// HashMap允许添加key为null的值,调用putForNullKey()方法将此(key,value)放到table[0]位置处。
if (key == null)
return putForNullKey(value);//该方法看下一段代码
// 阿静key传入hash(),内部使用了key的哈希值1,此方法执行结束后,返回哈希值2.
int hash = hash(key);//该方法看下一段代码
// 确定当前(key,value)在数组中的存放位置i。
int i = indexFor(hash, table.length);//该方法看下一段代码
//检查table[i]下面有没有key与新的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;//如果put()时修改操作,会返回原有旧的value值
}
}
addEntry(hash, key, value, i);//将(key,value)封装为一个Entry对象,并保存在此索引i位置处。
return null;//如果put()是添加操作,会返回null。
}
//如果key是null,直接存入[0]的位置
private V putForNullKey(V value) {
//判断是否有重复的key,如果有重复的,就替换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++;
//把新的映射关系存入[0]的位置,而且key的hash值用0表示
addEntry(0, null, value, 0);
return null;
}
final int hash(Object k) {
int h = 0;
if (useAltHashing) {
if (k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h = hashSeed;
}
h ^= k.hashCode();
// 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);
}
static int indexFor(int h, int length) {
return h & (length-1);//没有使用取模运算计算索引位置,该方式效率更高
}
void addEntry(int hash, K key, V value, int bucketIndex) {
//判断是否需要扩容
//扩容:(1)size达到阈值(2)table[i]正好非空
if ((size >= threshold) && (null != table[bucketIndex])) {
//table扩容为原来的2倍,并且扩容后,会重新调整所有key-value的存储位置
resize(2 * table.length);
//新的key-value的hash和index也会重新计算
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
//存入table中
createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//原来table[i]下面的映射关系作为新的映射关系next
table[bucketIndex] = new Entry<>(hash, key, value, e);
//个数增加
size++;
}
将Entry更名为Node,因为在链表或数中名为Node节点更合适
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;
}
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认的初始容量 16
static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量 1 << 30
static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认加载因子
static final int TREEIFY_THRESHOLD = 8; //默认树化阈值8,当链表的长度达到这个值后,要考虑树化
static final int UNTREEIFY_THRESHOLD = 6;//默认反树化阈值6,当树中结点的个数达到此阈值后,要考虑变为链表
//当单个的链表的结点个数达到8,并且table的长度达到64,才会树化。
//当单个的链表的结点个数达到8,但是table的长度未达到64,会先扩容
static final int MIN_TREEIFY_CAPACITY = 64; //最小树化容量64
transient Node<K,V>[] table; //数组
transient int size; //记录有效映射关系的对数,也是Entry对象的个数
int threshold; //阈值,当size达到阈值时,考虑扩容
final float loadFactor; //加载因子,影响扩容的频率
LinkedHashMap复用了HashMap判断元素添加位置的逻辑,但是,在真正执行添加操作的时候重写了HashMap的newNode()方法,以为LinkedHashMap多了一个双向链表记录元素添加时的顺序。源码如下:
//重写newNode()
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);//连接元素添加的先后顺序
return p;
}
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//增加的一对双向链表
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
添加、修改使用的方法一样