JDK1.8中HashMap的源码分析

关键的属性分析
public class HashMap extends AbstractMapimplements Map, Cloneable, Serializable {//Node类型的数组 , 我们常说的bucket数组 , 其中每个元素为链表或者树形结构transient Node[] table;//HashMap中保存的数据个数transient int size;//HashMap需要resize操作的阈值 , 当table=={}时 , 该值为初始容量(初始容量默认为16) , 当table被//填充了 , 也就是为table分配内存空间后 , threshold=capacity*loadFactory , HashMap在进行//扩容时需要参考threshold//临界值 , 也就是元素数量达到临界值时 , 会进行扩容 。int threshold;//加载因子 , 代表final float loadFactor;//用户快速失败 , 由于HashMap非线程安全 , 在对HashMap进行迭代时 , 如果期间其他线程的参与导致//HashMap的结构发生变化(比如put、remove等操作) , 需要抛出异常//ConcurrentModificationExceptiontransient int modCount; }构造函数的分析,HashMap提供了四个构造函数
//创建一个指定容量和指定负载因子的HashMap public HashMap(int initialCapacity, float loadFactor) {//这三个判断是看初始容量大小的 , 最大不能超过MAXIMUM_CAPACITY=1<<30(2的30次方)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);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}//传入默认的容量大小 , 创造一个指定容量大小和默认负载因子是0.75 HashMap,这个构造函数其实也是调//用的第一个构造函数public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}//此构造函数创建一个空的HashMap , 其中加载因子为默认值0.75 public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}//该构造函数 , 传入一个Map然后把该Map转为HashMap public HashMap(Map m) {this.loadFactor = DEFAULT_LOAD_FACTOR;//将一个老的map添加到这个新的map中putMapEntries(m, false);}final void putMapEntries(Map m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);}else if (s > threshold)resize();for (Map.Entry e : m.entrySet()) {K key = e.getKey();V value = http://kandian.youth.cn/index/e.getValue();putVal(hash(key), key, value, false, evict);}}}添加元素 , 在看添加方法之前 , 我们先看下 hash()方法 , 怎么计算的hash值
static final int hash(Object key) {int h;//先取key的hashCode , 然后进行位运算 , 再进行异或运算 , 这么复杂目的是为了减少hash碰撞return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}下面是put方法
public V put(K key, V value) {//四个参数 , 第一个hash值 , 第四个参数表示如果该key存在值 , 如果为null的话 , 则插入新的//value,最后一个参数在hashMap中没有 , 可以不用管return putVal(hash(key), key, value, false, true);}【JDK1.8中HashMap的源码分析】然后进入putVal方法中
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//定义了四个变量 , tab哈希数组;p该哈希桶的首节点;n hashMap的长度;i计算出数组下标Node[] tab; Node p; int n, i;//获取长度并进行扩容 , 使用的是懒加载 , table一开始是没有加载的 , 等put后才开始加载if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;//如果计算出的该哈希值的位置没有值 , 则把新插入的key-value放到此处 , 此处就算没有插入成功 , 也就是发生哈希冲突时也会把哈希桶的首节点赋予pif ((p = tab[i = (n - 1)//发生哈希冲突的几种情况else {//e 临时节点的作用 , k存放当前节点的keyNode e; K k;//第一种 , 插入当前key-value的hash值 , key都与当前节点的相等 , e=p , 则表示为首节点if (p.hash == hash//第二种 , hash值不等于首节点 , 判断p是否属于红黑树的节点else if (p instanceof TreeNode)//是红黑树的节点 , 则在红黑树中进行添加 , 如果该节点已经存在 , 则返回该节点(不为null) , 该值很重要 , 用来判断put操作是否成功 , 如果添加成功返回nulle = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);//第三种hash值不等于首节点 , 不为红黑树的节点 , 则为链表的节点 。else {//遍历该链表for (int binCount = 0; ; ++binCount) {//如果找到尾部 , 则表明添加的key-value没有重复 , 在尾部进行添加if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);//判断是否要转换为红黑树结构if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}//如果链表中有重复的key , e则为当前重复的节点 , 结束循环if (e.hash == hashp = e;}}//有重复的key , 则用待插入值进行覆盖 , 返回旧值if (e != null) { // existing mapping for keyV oldValue = http://kandian.youth.cn/index/e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}//到了此步骤 , 则表明待插入的key-value是没有key的重复 , 因为插入成功e节点的值为null//修改次数+1++modCount;//实际长度+1 , 判断是否大于临界值 , 大于则扩容if (++size> threshold)resize();afterNodeInsertion(evict);//添加成功return null;}