文章参考:https://www.cnblogs.com/skywang12345/p/3310835.html
1. HashMap概述
HashMap是存储键值对内容的一个集合,继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口,HashMap的实现不是线程同步的,即非线程安全的,key和value都是支持null的。
HashMap根据键的hashCode值获取存储位置,存储数据,大部分情况可以直接定位到它的值,访问速度快,但是其遍历的顺序确实不确定的(无序)。
2. HashMap存储结构
数组:
数组的存储区是连续的,占用内存严重,故空间复杂度很大。但数组的二分查找时间度小;
数组的特点:寻址容易,插入和删除困难。
链表:
链表的储存区离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度大;
链表的特点:寻址困难,插入和删除容易。
哈希表
Jdk1.7 : 数组+链表 Jdk1.8 数组+链表+红黑树 (寻址容易,插入和删除也容易)
结构图
3 HashMap成员概述
3.1 重要的成员变量
transient Entry[] table:是一个Entry[]数组类型,而Entry实际上是一个单向链表,哈希表中的“key-value键值对”都存储在Entry数组中,
transient int size:是HashMap的大小,是保存HashMap中保存键值对的数量
final float loadFactor:加载因子
int threshold:是HashMap的阈值,用来判断是否需要调整HashMap的容量,threshold = 容量*加载因子(最大可承受装载的容量),达到最大threshold值时,就需要扩容
transient volatile int modCount:fail-fast机制的需要。
trasient
就是让某些被修饰的成员属性变量不被序列化
volatile
是一个类型修饰符(type specifier)。volatile的作用是作为指令关键字, 确保本条指令不会因编译器的优化而省略, 且要求每次直接读值。 volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
final
变量不可变,被修饰后就是一个常量
Entry[] table 源码详解
static class Entry<K,V> implements Map.Entry<K,V> {
final K key; // 成员变量
V value;
Entry<K,V> next; // 指向下一个节点
final int hash;
构造函数。 初始化操作,输入参数包括哈希值(h), 键(k), 值(v), 下一节点(n)
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;
}
判断两个Entry是否相等
先判断两个对象类型是否相等,不相同直接返回false
相同然后判断两个Entry的key和value是否相等,相同返回true 否则false
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry)) //判断类型是否相同
return false;
Map.Entry e = (Map.Entry //获取key和value用于判断
Object k1 = getKey();
Object k2 = e.getKey();
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;
}
实现hashCode()
public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^(value==null ? 0 : value.hashCode());
}
public final String toString() {
return getKey() + "=" + getValue();
}
当向HashMap中添加元素时,绘调用recordAccess(),这里不做任何处理
void recordAccess(HashMap<K,V> m) { }
当从HashMap中删除元素时,绘调用recordRemoval(),这里不做任何处理
void recordRemoval(HashMap<K,V> m) { }
}
3.2 重要的成员方法
Object clone()
boolean containsKey(Object key)
boolean containsValue(Object value)
boolean isEmpty()
int size()
void clear()
void putAll(Map<? extends K, ? extends V> map)
V put(K key, V value)
V get(Object key)
V remove(Object key)
Collection<V> values()
Set<K> keySet()
Set<Entry<K, V>> entrySet()
3.3 构造函数
HashMap()默认构造函数。
HashMap(int capacity) 指定“容量大小”的构造函数
HashMap(int capacity, float loadFactor) 指定“容量大小”和“加载因子”的构造函数
HashMap(Map<? extends K, ? extends V> map) 包含“子Map”的构造函数
4 HashMap成员源码详解 (大量代码袭来)注释符号多了,看着不舒服就去了,嘿嘿
package java.util;
import java.io.*;
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
{
默认的初始容量是16,必须是2的幂。
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
static final int MAXIMUM_CAPACITY = 1 << 30;
默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
存储数据的Entry数组,长度是2的幂, HashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表
transient Entry[] table;
HashMap的大小,它是HashMap保存的键值对的数量
transient int size;
HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)
int threshold;
加载因子实际大小
final float loadFactor;
HashMap被改变的次数 (fail-fast机制)
transient volatile int modCount;
默认构造函数。
public HashMap() {
设置加载因子
this.loadFactor = DEFAULT_LOAD_FACTOR;
设置HashMap阈值,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
创建Entry数组,用来保存数据
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
指定“容量大小”的构造函数
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
指定“容量大小”和“加载因子”的构造函数 ,先判断参数合法性后初始化
public HashMap(int initialCapacity, float loadFactor) {
参数不合法抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;