从Java1.6开始:
网络上分析HashMap的版本也主要是JDK1.6,
我的是java1.6.0_45

JAVA6

HashMap有3个构造方法

无参构造
public HashMap() {}

设置the initial capacity的构造方法
public HashMap(int initialCapacity) {}

设置the initial capacity和the load factor的构造方法
public HashMap(int initialCapacity, float loadFactor) {}

通过HashMap构造器放入一个HashMap直接初始化。
public HashMap(Map

if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;

//MAXIMUM_CAPACITY MAXIMUM_CAPACITY = 1 << 30 最大容量 结果

while (capacity < initialCapacity)
capacity <<= 1;

HashMap的本质是 数组+链表

//数组原因 :
//源码是在初始化创建时:

//transient关键字标记的成员变量不参与序列化过程
transient Entry[] table;

table = new Entry[capacity];

//链表原因:
//Entry:
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

HashMap各个版本分析_java

这个就是HashMap的结构

下面分析put方法

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;
}

先分析 key==null的情况,HashMap中允许 key==null

private V putForNullKey(V value) {


//从这里可以了解到 null是存放在 table[0]中的,也就是第一行数组中
//如果存在e.key==null的这种情况
//重写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字面意思就是修改次数
//用modCount记录修改次数
modCount++;
//添加Entry,那么看下addEntry
//了解到key=null的hash值为0,bucketIndex=0
addEntry(0, null, value, 0);
return null;
}

Entry方法:

void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
//对象集合默认初始化为null

//所以从这里可以看出它是前插法
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
//如果大小超过需要resize,这个后面讲.
if (size++ >= threshold)
resize(2

此时再画一张图:

HashMap各个版本分析_java_02

注意空字符串的hashCode为0,所以当key=”“的时候 key.hash也为0,

问当hash为0时.

int indexFor(int h, int length) {
return h & (length-1);
}

可知 hash值为0时,indexFor的返回值为0

他就放在bucketIndex为0,在Entry[0]里。

那么再看看删除的情况吧!
remove(Object)方法

public V remove(Object key) {
//hashMap.remove本质是 removeEntryForKey方法
//那么紧接着看removeEntryForKey
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}

removeEntryForKey:

//没加修饰符就是friendly。friendly只是一种说法,把它认为是defaul
//这里为了方便变色 写成protected
protected 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;
//以上就是 1.先找到hash值 2.通过hash找到bucketIndex 3.通过bucketIndex找到Entry链
while (e != null) {
//其本质是能否找到e,如果能找到e
Entry<K,V> next = e.next;
//指针向后移动
Object k;

if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
//k=e.key
modCount++;
size--;
//删除就是删除当前节点。链表删除。
if (prev == e)
table[i] = next;
else
prev.next = next;
//删除返回
e.recordRemoval(this);
//空方法
return e;
}
prev = e;
e = next;
}
//找不到的话很明显 e=null
return

有些版本上说的是对: h%length 原因如下:
当length总是 2 的n次方时,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。

提高复用性,和解耦的关系

代码的封装是为了提高复用性

解耦是为了提高复用性

Java1.7

table表的建立转到了put方法中,先判断table是否存在,不存在创建

Java1.8

增加putVal方法,在putVal中判断table是否存在,通过resize方法进行初始化表
将新增数据的节点插入到尾部

Resize()

关于HashMap扩容是,Bucket扩容为原来的2倍, bucket<<=1;

HashMap的最大容量

//MAXIMUM_CAPACITY==1<<30
//Integer.MAX_VALUE==1<<31-1
//目的为了防止溢出
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
}

HashMap的容量是不小于输入数的​​1<<N​​;

Capacity指的是桶的大小,即纵向大小,并不是说总的大小。

Hash()作用是

它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分

当计算出来的hash函数h和hashMap的length做了&运算后,会得到[0,length-1]其中的一个值,而散列的均匀也会使这个值分布的均匀,从而达到HashMap高效的一点。

hash对一个对象的hashCode进行重新计算,而IndexFor生成这个对象的index。

hash值重新计算,是为了防止质量低下的hashCode()函数实现。在hashMap数组长度中长度是初始长度的2倍。通过右移造成地位的数据尽量的不同。

而 在计算index上使用的是h&(length-1)的方法。简单而效率高。

看了Java的代码,自己在设计hash函数的时候,就有选择了。尽量使用位运算符,少使用+-*/%的运算符,这样可以提高hash的效率

JAVA1.6的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);
}

JAVA1.7的hash

Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}

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);
}

JAVA1.8的hash

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

聊得时候步骤

1.就table创建区别
2.HashMap1.6底层结构 1.7和1.8底层结构
3.hash的作用,每个版本不同 hash的作用和hash的实际案例 用处
4.具体求桶
5.最大值和初始化值
6.前插和后插
7.putIfAbsent
8.TreeBin的使用
9.涉及到线程安全的包 此时聊到HashTable
10.什么情况下用到hashMap

好好学,好好看

学习还是需要看源码的,被面试打击的不轻