package java.util;
import java.io.*;
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{
/**
* 默认的数组大小,也就是桶数,必须是2次幂,在indexFor方法会降到2次幂的原因
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* 最大的容量,这里1经过左移运算后是个非常大的值
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认装载因子。
* 默认的entry数组的长度为16。装载因子的意义在于使得entry数组有剩余(25%),
* 当HashMap的数据的个数超过12(16*0.75)时
* 即会对entry数组进行第一次扩容,后面的再次扩容依次类推。而且在构造函数可以指定它的值
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 用于存储的表,长度可以调整,且必须是2的n次幂
*/
transient Entry[] table;
/**
* 键值对的数目
*/
transient int size;
/**
* 阀值,也叫下一个容量的扩充值,最大可使用数组大小
*/
int threshold;
/**
* 装载因子(构造函数可以指定)
*/
final float loadFactor;
/**
* map结构被改变的次数
* 结构上的修改是指更改在HashMap中映射的数量或以其他方式修改它的内部结构(例如,刷新)
* 该字段在HashMap使用迭代器遍历的时候判断是否被修改过
*/
transient int modCount;
/**
* 这就是构造函数,参数可以指定桶数组的容量和加载因子,如果初始容量不符合是2次幂的规则,会对它有所调整。
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//初始容量不能超过默认容量
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// 寻找一个2的k次幂capacity恰好大于initialCapacity的最小值
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
/**
* 如果只指定容量,那么就是用默认的加载因子
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 都不指定,那么什么都是默认的
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
/**
* 通过传入的map创建一个HashMap,容量为默认容量(16)和(map.zise()/DEFAULT_LOAD_FACTORY)+1
* 的较大者,装载因子为默认值
*/
public HashMap(Map<? extends K, ? extends V> m) {
//先构造
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
//把Map类型的参数m里面的内容放进去
putAllForCreate(m);
}
/**
* 初始化
*/
void init() {
}
/**
* 这是HashMap里面的hash方法,一般是一个Entry里面的key这个对象属性使用
* 适用补充hash函数,防止质量较差的hash函数。
* 该方法主要作用是防止质量较差的哈希函数带来过多的冲突(碰撞)问题。
* Java中int值占4个字节,即32位。根据这32位值进行移位、异或运算得到一个值。
* 它的参数是int
*/
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**参数为提供的一个hash值和桶这个数组的长度求取Entry在数组的位置
* 由于length是2的n次幂,所以h & (length-1)相当于h % length。
* 对于length,其2进制表示为1000...0,那么length-1为0111...1(其余都是1)。
* 那么对于任何小于length的数h,该式结果都是其本身h。
* 对于h = length,该式结果等于0。
* 对于大于length的数h,则和0111...1位与运算后,只有对应位置为1时,结果的对应位置才有1
* 相当于减去j个length,该式结果是h-j*length,
* 所以相当于h % length。
* 保证结果的最大值为length-1(索引)
* 这也是为什么length只能是2的n次幂的原因
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
/**
* 返回当前键值对数<=当前容量*装载因子
*/
public int size() {
return size;
}
/**
* 判断数组里面的是否有键值对
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 返回制定键的值,如果没有就返回null
*/
public V get(Object key) {
if (key == null)
return getForNullKey();
//对key对象的hashcode再次求得hash值,只有用key的hashCode求索引
int hash = hash(key.hashCode());
//遍历桶,直到指向hash值对应的桶(也就是遍历链表),从最开始利用hash得到的索引位置开始横向遍历(因为有可能hash相同,值不同)
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
//如果Entry的hash值与当前hash值相同并且key也相同(因为Entry的hash值就是key对象的hashCode求出来的,在插入Entry的之后求得)
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
//返回这个桶键对应的值
return e.value;
}
//没有找到
return null;
}
/**
* 如果Key为空,就用这个方法返回对应的值,key为空,那么就从索引为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;
}
/**
* 判断给定一个键是否返回一个映射的判断
*/
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
/**
* 通过key返回对应的Entry
*/
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : 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 != null && key.equals(k))))//hash值相同,带式key的内容不相同,因为HashMap的对象是不重复的。
return e;
}
return null;
}
/**
* 在此映射表中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换,返回旧值, //如果没有就添加一个新的Entry
*/
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);//吧key等于null的value替换
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {//在链表中寻找存在的key
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++;
//如果没有就添加一个新的Entry
addEntry(hash, key, value, i);
return null;
}
/**
* 当key为null是放进table[0]中去,有可能冲突
*/
private V putForNullKey(V 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++;
//如果table[0]这个桶还是空的话,那么就不用解决冲突
addEntry(0, null, value, 0);
return null;
}
/**
* 如果插进一个新的视图,或者节点,这个方法有判断是否需要创建新的桶的必要,也就是键值对数加1
*/
private void putForCreate(K key, V value) {
//规定hash值为key来求得,如果为空就是在0下标处,不然从新用hashCode求得
int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
/**
* 遍历这个下标开始的链表,如果存在相同的key,那么就简单的吧旧值替换新值
*/
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
e.value = value;
return;
}
}
//否则就要创建新的键值对了
createEntry(hash, key, value, i);
}
//将指定map中数据插入到当前map中
private void putAllForCreate(Map<? extends K, ? extends V> m) {
for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<? extends K, ? extends V> e = i.next();
//分别在参数的map里面遍历的每一个视图,都调用上一个方法
putForCreate(e.getKey(), e.getValue());
}
}
/**
* 扩容,有参数(addEntry中是两倍当前数组大小)
*
*/
void resize(int newCapacity) {
//这是旧的HasMap
Entry[] oldTable = table;
//旧键数组大小
int oldCapacity = oldTable.length;
//是否当前容量是否最大
if (oldCapacity == MAXIMUM_CAPACITY) {
// 如果旧的容量已经是系统默认最大容量了,那么将阈值设置成整形的最大值,并退出
threshold = Integer.MAX_VALUE;
return;
}
//先创建一个新的数组
Entry[] newTable = new Entry[newCapacity];
//将所有数据从当前数组复制到新数组
transfer(newTable);
//将新地址放到原来数组的地址
table = newTable;
//设置阀值
threshold = (int)(newCapacity * loadFactor);
}
/**
* 将所有数据从当前数组复制到新数组
*/
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;
//根据新尺寸求出新的hash值
int i = indexFor(e.hash, newCapacity);
//e插入到当前桶的头结点
e.next = newTable[i];
newTable[i] = e;
//节点移动到下一位
e = next;
} while (e != null);
}
}
}
/**
* 复制参数map的映射到当前的map。
* 重复的映射会被替换。(即当前map中的key和指定参数map的key相同,值会被替换)
*
*/
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;
//获得即比targetCapacity大,也是最小值的2的幂,这就是目标数组的新容量
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
}
//迭代将key-value映射放进目标HashMap ,在put里面要么替换旧值,要么使用addEntry添加新视图(在自身数组下)
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());
}
}
/**
* 删除已经存在的键值对,要返回删除视图的值,没有就返回空
*/
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());
//如果hash为0,根据与运算,下标为0
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--;
//如果要删除第一个节点,那么就把第二个节点当做table[i]也就是table[0]
if (prev == e)
table[i] = next;
else
//其他情况就是简单的指针
prev.next = next;
e.recordRemoval(this);
return e;
}
//这两个指针都一项下一项,以便下次操作
prev = e;
e = next;
}
return e;
}
/**
* 上面那个方法是根据key对象,这里还根据键值对删除
*/
final Entry<K,V> removeMapping(Object o) {
if (!(o instanceof Map.Entry))
return null;
Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
Object key = entry.getKey();
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;
if (e.hash == hash && e.equals(entry)) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
/**
* 清空数组
*/
public void clear() {
modCount++;
Entry[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
/**
* 只根据值查找是否存在,遍历整个map
*
*/
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;
}
/**
* 是否包含空值
*/
private boolean containsNullValue() {
Entry[] tab = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value == null)
return true;
return false;
}
/**
* 创建一个副本,键值对象只是简单的插入新创建的HashMap
*
*/
public Object clone() {
HashMap<K,V> result = null;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// assert false;
}
result.table = new Entry[table.length];
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
result.putAllForCreate(this);
return result;
}
/**
* 这就是我么所说的桶,
*/
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
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;
}
//和对象o比较是否相同
public final boolean equals(Object o) {
//如果对象o不是Map.Enter类型的话,那就肯定不相同了
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
//当前的Entry的KEY
Object k1 = getKey();
//相比较的Entry的KEY
Object k2 = e.getKey();
//只有key和value相同的这个两个桶才相等
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() {
//这个对象的hashCode = key的hashCode与value的hashCode的异或结果,例如1011和1111的异或结果为0100(只有在相应位置上一个为1一个为0,结果相应位置才为1)
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
/**
* 使用该方法证明该key已经在该map中,在linkedHashMao实现
*/
void recordAccess(HashMap<K,V> m) {
}
/**
* 该方法记录该key已经被移除了,在linkedHashMao实现
*/
void recordRemoval(HashMap<K,V> m) {
}
}
/**
* 添加一个新桶,bucketIndex为先hash = hash(key.hashCode())然后bucketIndex = indexFor(hash, table.length)求出来这个桶在数组当中的索引
*/
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);
}
/**
* 同上,只是这个方法不需要判断是否超过阀值
*
*/
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
size++;
}
//内部迭代器
private abstract class HashIterator<E> implements Iterator<E> {
Entry<K,V> next; // 下一个节点
int expectedModCount; // 保护HashMap没有变更
int index; // 当前的节点的索引
Entry<K,V> current; // 当前的节点
HashIterator() {
// 保存modCount,因为如果HashMap进行了任何操作modCount都会增加,所以如果发现modCount变化了,就可以抛出失败
expectedModCount = modCount;
if (size > 0) { // 把next指向index位置上的数组的第一项
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
}
//下一个节点是否为空
public final boolean hasNext() {
return next != null;
}
//返回当前节点,相应的指针会有所移动
final Entry<K,V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
//e最开始也指向第一个节点
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
//这是遍历的关键,他会从最开始的索引位置向下遍历,while语句的作用是如果这个索引位置上的链表遍历完了的话,就进入下一行,
//也就是index++,每一次调用e为当前节点,next为下一个节点,next先移动,e后移动,并返回e。
if ((next = e.next) == null) {
Entry[] t = table;
while (index < t.length && (next = t[index++]) == null)
;
}
current = e;
return e;
}
//删除才经过的节点,也就是此时e指向的节点的上一个节点
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
//内部的value的迭代器,调用上面的nextEntry()后再取相应的值
private final class ValueIterator extends HashIterator<V> {
public V next() {
return nextEntry().value;
}
}
//略
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
// 这里的三个Iterator方法返回相应的Iterator对象
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
Iterator<V> newValueIterator() {
return new ValueIterator();
}
Iterator<Map.Entry<K,V>> newEntryIterator() {
return new EntryIterator();
}
// Views
private transient Set<Map.Entry<K,V>> entrySet = null;
/**
* 返回此映射中所包含的键的 Set 视图(Set就像数学的集合一样,不重复是他的特点)。
* 该 set 受映射的支持,所以对映射的更改将反映在该 set 中,
* 反之亦然。如果在对 set 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),
* 则迭代结果是不确定的。该 set 支持元素的移除,通过
* Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作
* 可从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
*/
public Set<K> keySet() {
Set<K> ks = keySet;
//构造一个Set集合,如果他是空的,说明他还没有实例化,需要相应的new一个
return (ks != null ? ks : (keySet = new KeySet()));
}
//内部类keySet
private final class KeySet extends AbstractSet<K> {
//构造键迭代器的方法
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
/**
* 返回此映射所包含的值的 Collection 视图。
* 该 collection 受映射的支持,所以对映射的更改将反映在该 collection 中,
* 反之亦然。如果在对 collection 进行迭代的同时修改了映射(通过迭代器自己的 remove 操作除外),
* 则迭代结果是不确定的。该 collection 支持元素的移除,
* 通过 Iterator.remove、Collection.remove、removeAll、retainAll 和 clear 操作
* 可从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
*/
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null ? vs : (values = new Values()));
}
private final class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
/**
* 返回此映射(HsahMap)所包含的映射关系的 Set 视图。
* 该 set 受映射支持,所以对映射的更改将反映在此 set 中,
* 反之亦然。如果在对 set 进行迭代的同时修改了映射
* (通过迭代器自己的 remove 操作,或者通过在该迭代器返回的映射项上执行 setValue 操作除外),
* 则迭代结果是不确定的。该 set 支持元素的移除,
* 通过 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作
* 可从该映射中移除相应的映射关系。它不支持 add 或 addAll 操作。
*/
public Set<Map.Entry<K,V>> entrySet() {
return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
Set<Map.Entry<K,V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
//如果参数提供的对象o不是Map.Entry的话会报错
if (!(o instanceof Map.Entry))
return false;
Map.Entry<K,V> e = (Map.Entry<K,V>) o;
Entry<K,V> candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
/**
*
*/
private void writeObject(java.io.ObjectOutputStream s)
throws IOException
{
Iterator<Map.Entry<K,V>> i =
(size > 0) ? entrySet0().iterator() : null;
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
// Write out number of buckets
s.writeInt(table.length);
// Write out size (number of Mappings)
s.writeInt(size);
// Write out keys and values (alternating)
if (i != null) {
while (i.hasNext()) {
Map.Entry<K,V> e = i.next();
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
}
}
private static final long serialVersionUID = 362498820763181265L;
/**
* Reconstitute the <tt>HashMap</tt> instance from a stream (i.e.,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the threshold, loadfactor, and any hidden stuff
s.defaultReadObject();
// Read in number of buckets and allocate the bucket array;
int numBuckets = s.readInt();
table = new Entry[numBuckets];
init(); // Give subclass a chance to do its thing.
// Read in size (number of Mappings)
int size = s.readInt();
// Read the keys and values, and put the mappings in the HashMap
for (int i=0; i<size; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
putForCreate(key, value);
}
}
// These methods are used when serializing HashSets
int capacity() { return table.length; }
float loadFactor() { return loadFactor; }
}