HashMap源码分析--jdk1.8
jdk1.8
arraylist源码分析--jdk1.8
linkedlist源码分析--jdk1.8
hashmap源码分析--jdk1.8
hashmap概述
1. hashmap是可以动态扩容的数组,基于数组、链表、红黑树实现的集合。
2. hashmap支持键值对取值、克隆、序列化,元素无序,key不可重复value可重复,都可为null。
3. hashmap初始默认长度16,超出扩容2倍,填充因子0.75f。
4.hashmap当链表的长度大于8的且数组大小大于64时,链表结构转变为红黑树结构。
hashmap数据结构
数据结构是集合的精华所在,数据结构往往也限制了集合的作用和侧重点,了解各种数据结构是我们分析源码的必经之路。
hashmap的数据结构如下:数组+链表+红黑树
hashmap源码分析
/* * 用数组+链表+红黑树实现的集合,支持键值对查找 */ public class hashmap<k,v> extends abstractmap<k,v> implements map<k,v>, cloneable, serializable { private static final long serialversionuid = 362498820763181265l; /** * 默认初始容量-必须是2的幂 * 1*2的4次方 默认长度16 */ static final int default_initial_capacity = 1 << 4; /** * 最大容量 * 1*2的30次方 最大容量1073741824 */ static final int maximum_capacity = 1 << 30; /** * 默认的填充因子 0.75 * 负载因子0.75是对空间和时间效率的一个平衡选择,建议大家不要修改,除非在时间和空间比较特殊的情况下, * 如果内存空间很多而又对时间效率要求很高,可以降低负载因子load factor的值; * 相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadfactor的值,这个值可以大于1 */ static final float default_load_factor = 0.75f; /** * 当桶(bucket)上的节点数大于这个值时会转成红黑树 */ static final int treeify_threshold = 8; /** * 当桶(bucket)上的节点数小于这个值时树转链表 */ static final int untreeify_threshold = 6; /** * 桶中结构转化为红黑树对应的table的最小大小 */ static final int min_treeify_capacity = 64; /** * node是单向链表,它实现了map.entry接口 */ static class node<k,v> implements map.entry<k,v> { final int hash; final k key; v value; node<k,v> next; //构造函数hash值 键 值 下一个节点 node(int hash, k key, v value, node<k,v> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final k getkey() { return key; } public final v getvalue() { return value; } public final string tostring() { return key + "=" + value; } public final int hashcode() { return objects.hashcode(key) ^ objects.hashcode(value); } // 实现接口定义的方法,且该方法不可被重写 // 设值,返回旧值 public final v setvalue(v newvalue) { v oldvalue = value; value = newvalue; return oldvalue; } //构造函数hash值 键 值 下一个节点 /* * 重写父类object的equals方法,且该方法不可被自己的子类再重写 * 判断相等的依据是,只要是map.entry的一个实例,并且键键、值值都相等就返回true */ public final boolean equals(object o) { if (o == this) return true; if (o instanceof map.entry) { map.entry<?,?> e = (map.entry<?,?>)o; if (objects.equals(key, e.getkey()) && objects.equals(value, e.getvalue())) return true; } return false; } }
hashmap继承和实现分析
hashmap extends abstractmap
abstractmap extends object
java中所有类都继承object,所以hashmap的继承结构如上图。
1. abstractmap是一个抽象类,实现了map<k,v>接口,map<k,v>定义了一些map(k,v)键值对通用方法,而abstractmap抽象类中可以有抽象方法,还可以有具体的实现方法,abstractmap实现接口中一些通用的方法,实现了基础的/get/remove/containskey/containsvalue/keyset方法,hashmap再继承abstractmap,拿到通用基础的方法,然后自己在实现一些自己特有的方法,这样的好处是:让代码更简洁,继承结构最底层的类中通用的方法,减少重复代码,从上往下,从抽象到具体,越来越丰富,可复用。
2.hashmap实现了map<k,v>、cloneable、serializable接口
1)map<k,v>接口,定义了map键值对通用的方法,1.8中为了加强接口的能力,使得接口可以存在具体的方法,前提是方法需要被default或static关键字所修饰,map中实现了一些通用方法实现,使接口更加抽象。
2)cloneable接口,可以使用object.clone()方法。
3)serializable接口,序列化接口,表明该类可以被序列化,什么是序列化?简单的说,就是能够从类变成字节流传输,反序列化,就是从字节流变成原来的类
hashmap核心方法分析
1. put方法(10种实现)--增/修改
1)v put(k key, v value);//map添加元素
/** * 新增元素 */ public v put(k key, v value) { return putval(hash(key), key, value, false, true); } /** * implements map.put and related methods * @param hash hash for key * @param key the key * @param value the value to put * @param onlyifabsent if true, don't change existing value * onlyifabsent默认传false,覆盖更改现有值 * onlyifabsent传true,不覆盖更改现有值 * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final v putval(int hash, k key, v value, boolean onlyifabsent, boolean evict) { node<k,v>[] tab; node<k,v> p; int n, i; //如果table为空 或者 长度为0 if ((tab = table) == null || (n = tab.length) == 0) //扩容 n = (tab = resize()).length; //计算index,并对null做处理 // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中) if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newnode(hash, key, value, null); // 桶中已经存在元素 else { node<k,v> e; k k; //如果key存在 直接覆盖 value // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) // 将第一个元素赋值给e,用e来记录 e = p; //如果table[i]是红黑树 直接在红黑树中插入 // hash值不相等,即key不相等;为红黑树结点 else if (p instanceof treenode) e = ((treenode<k,v>)p).puttreeval(this, tab, hash, key, value); //如果是链表 则遍历链表 else { // 在链表最末插入结点 for (int bincount = 0; ; ++bincount) { // 到达链表的尾部 if ((e = p.next) == null) { // 在尾部插入新结点 p.next = newnode(hash, key, value, null); // 结点数量达到阈值,转化为红黑树 if (bincount >= treeify_threshold - 1) // -1 for 1st treeifybin(tab, hash); // 跳出循环 break; } // 判断链表中结点的key值与插入的元素的key值是否相等 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) // 相等,跳出循环 break; // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表 p = e; } } // 表示在桶中找到key值、hash值与插入元素相等的结点 if (e != null) { // existing mapping for key // 记录e的value v oldvalue = e.value; // onlyifabsent为false或者旧值为null if (!onlyifabsent || oldvalue == null) //用新值替换旧值 e.value = value; // 访问后回调 afternodeaccess(e); // 返回旧值 return oldvalue; } } // 结构性修改 ++modcount; // 实际大小大于阈值则扩容 if (++size > threshold) resize(); // 插入后回调,用来回调移除最早放入map的对象(linkedhashmap中实现了,hashmap中为空实现) afternodeinsertion(evict); return null; }
2)putall(map<? extends k, ? extends v> m);//添加map全部元素
/** * 添加map全部元素 */ public void putall(map<? extends k, ? extends v> m) { putmapentries(m, true); } /** * implements map.putall and map constructor * @param m the map * @param evict false when initially constructing this map, else * true (relayed to method afternodeinsertion). */ final void putmapentries(map<? extends k, ? extends v> m, boolean evict) { int s = m.size(); if (s > 0) { // 判断table是否已经初始化 if (table == null) { // pre-size // 未初始化,s为m的实际元素个数 float ft = ((float)s / loadfactor) + 1.0f; int t = ((ft < (float)maximum_capacity) ? (int)ft : maximum_capacity); // 计算得到的t大于阈值,则初始化阈值 if (t > threshold) threshold = tablesizefor(t); } // 已初始化,并且m元素个数大于阈值,进行扩容处理 else if (s > threshold) resize(); // 将m中的所有元素添加至hashmap中 for (map.entry<? extends k, ? extends v> e : m.entryset()) { k key = e.getkey(); v value = e.getvalue(); putval(hash(key), key, value, false, evict); } } } /** * 扩容 * ①.在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容; * ②.每次扩展的时候,都是扩展2倍:16、32、64、128... * ③.扩展后node对象的位置要么在原位置,要么移动到原偏移量两倍(2次幂)的位置。 * @return the table */ final node<k,v>[] resize() { node<k,v>[] oldtab = table;//oldtab指向hash桶数组 int oldcap = (oldtab == null) ? 0 : oldtab.length; int oldthr = threshold; int newcap, newthr = 0; if (oldcap > 0) {//如果oldcap大于的话,就是hash桶数组不为空 if (oldcap >= maximum_capacity) {//如果大于最大容量了,就赋值为整数最大的阀值 threshold = integer.max_value; return oldtab; } //如果当前hash桶数组的长度在扩容后仍然小于最大容量 并且oldcap大于默认值16,就扩充为原来的2倍 else if ((newcap = oldcap << 1) < maximum_capacity && oldcap >= default_initial_capacity) newthr = oldthr << 1; // double threshold 双倍扩容阀值threshold } else if (oldthr > 0) // initial capacity was placed in threshold newcap = oldthr; else { // zero initial threshold signifies using defaults newcap = default_initial_capacity; newthr = (int)(default_load_factor * default_initial_capacity); } //计算新的resize上限 if (newthr == 0) { float ft = (float)newcap * loadfactor; newthr = (newcap < maximum_capacity && ft < (float)maximum_capacity ? (int)ft : integer.max_value); } threshold = newthr; @suppresswarnings({"rawtypes","unchecked"}) node<k,v>[] newtab = (node<k,v>[])new node[newcap];//新建hash桶数组 table = newtab;//将新数组的值复制给旧的hash桶数组 if (oldtab != null) {//进行扩容操作,复制node对象值到新的hash桶数组 //把每个bucket都移动到新的buckets中 for (int j = 0; j < oldcap; ++j) { node<k,v> e; if ((e = oldtab[j]) != null) {//如果旧的hash桶数组在j结点处不为空,复制给e oldtab[j] = null;//将旧的hash桶数组在j结点处设置为空,方便gc if (e.next == null)//如果e后面没有node结点 newtab[e.hash & (newcap - 1)] = e;//直接对e的hash值对新的数组长度求模获得存储位置 else if (e instanceof treenode)//如果e是红黑树的类型,那么添加到红黑树中 ((treenode<k,v>)e).split(this, newtab, j, oldcap); else { // preserve order 链表优化重hash的代码块 node<k,v> lohead = null, lotail = null; node<k,v> hihead = null, hitail = null; node<k,v> next; do { next = e.next;//将node结点的next赋值给next if ((e.hash & oldcap) == 0) {//如果结点e的hash值与原hash桶数组的长度作与运算为0 原索引 if (lotail == null)//如果lotail为null lohead = e;//将e结点赋值给lohead else lotail.next = e;//否则将e赋值给lotail.next lotail = e;//然后将e复制给lotail } // 原索引+oldcap else {//如果结点e的hash值与原hash桶数组的长度作与运算不为0 if (hitail == null)//如果hitail为null hihead = e;//将e赋值给hihead else hitail.next = e;//如果hitail不为空,将e复制给hitail.next hitail = e;//将e复制个hitail } } while ((e = next) != null);//直到e为空 //原索引放到bucket里 if (lotail != null) {//如果lotail不为空 lotail.next = null;//将lotail.next设置为空 newtab[j] = lohead;//将lohead赋值给新的hash桶数组[j]处 } //原索引+oldcap放到bucket里 if (hitail != null) {//如果hitail不为空 hitail.next = null;//将hitail.next赋值为空 newtab[j + oldcap] = hihead;//将hihead赋值给新的hash桶数组[j+旧hash桶数组长度] } } } } } return newtab; }
3)v putifabsent(k key, v value);//如果key存在,则跳过,不覆盖value值,onlyifabsent传true,不覆盖更改现有值
/** * 如果key存在则跳过,不覆盖value值,onlyifabsent传true,不覆盖更改现有值 * 如果key不存在则put * @param key * @param value * @return */ @override public v putifabsent(k key, v value) { return putval(hash(key), key, value, true, true); }
4)merge(k key, v value,bifunction<? super v, ? super v, ? extends v> remappingfunction);//用某种方法更新原来的value值
/** * 用某种方法更新原来的value值 * bifunction支持函数式变成,lambda表达式,如:string::concat拼接 * @param key * @param value * @param remappingfunction * @return */ @override public v merge(k key, v value, bifunction<? super v, ? super v, ? extends v> remappingfunction) { if (value == null) throw new nullpointerexception(); if (remappingfunction == null) throw new nullpointerexception(); int hash = hash(key); node<k,v>[] tab; node<k,v> first; int n, i; int bincount = 0; treenode<k,v> t = null; node<k,v> old = null;// 该key原来的节点对象 if (size > threshold || (tab = table) == null || (n = tab.length) == 0)//判断是否需要扩容 n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof treenode)// 取出old node对象 old = (t = (treenode<k,v>)first).gettreenode(hash, key); else { node<k,v> e = first; k k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++bincount; } while ((e = e.next) != null); } } if (old != null) {//如果 old node 存在 v v; if (old.value != null) // 如果old存在,执行lambda,算出新的val并写入old node后返回。 v = remappingfunction.apply(old.value, value); else v = value; if (v != null) { old.value = v; afternodeaccess(old); } else removenode(hash, key, null, false, true); return v; } if (value != null) { //如果old不存在且传入的newval不为null,则put新的kv if (t != null) t.puttreeval(this, tab, hash, key, value); else { tab[i] = newnode(hash, key, value, first); if (bincount >= treeify_threshold - 1) treeifybin(tab, hash); } ++modcount; ++size; afternodeinsertion(true); } return value; }
5)compute(k key,bifunction<? super k, ? super v, ? extends v> remappingfunction);//根据已知的 k v 算出新的v并put
/** * 根据已知的 k v 算出新的v并put。 * 如果根据key获取的oldval为空则lambda中涉及到oldval的计算会报空指针。 * 如:map.compute("a", (key, oldval) -> oldval + 1); 如果oldval为null,则空指针 * 源码和merge类似 * bifunction返回值作为新的value,bifunction有二个参数 * @param key * @param remappingfunction * @return */ @override public v compute(k key, bifunction<? super k, ? super v, ? extends v> remappingfunction) { if (remappingfunction == null) throw new nullpointerexception(); int hash = hash(key); node<k,v>[] tab; node<k,v> first; int n, i; int bincount = 0; treenode<k,v> t = null; node<k,v> old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof treenode) old = (t = (treenode<k,v>)first).gettreenode(hash, key); else { node<k,v> e = first; k k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++bincount; } while ((e = e.next) != null); } } v oldvalue = (old == null) ? null : old.value; v v = remappingfunction.apply(key, oldvalue); if (old != null) { if (v != null) { old.value = v; afternodeaccess(old); } else removenode(hash, key, null, false, true); } else if (v != null) { if (t != null) t.puttreeval(this, tab, hash, key, v); else { tab[i] = newnode(hash, key, v, first); if (bincount >= treeify_threshold - 1) treeifybin(tab, hash); } ++modcount; ++size; afternodeinsertion(true); } return v; }
6)computeifabsent(k key, function<? super k, ? extends v> mappingfunction);//当key不存在时才put,如果key存在则无效
/** * 当key不存在时才put,如果key存在则无效 * 如:computeifabsent(keyc, k -> genvalue(k)); * function返回值作为新的value,function只有一个参数 * @param key * @param mappingfunction * @return */ @override public v computeifabsent(k key, function<? super k, ? extends v> mappingfunction) { if (mappingfunction == null) throw new nullpointerexception(); int hash = hash(key); node<k,v>[] tab; node<k,v> first; int n, i; int bincount = 0; treenode<k,v> t = null; node<k,v> old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof treenode) old = (t = (treenode<k,v>)first).gettreenode(hash, key); else { node<k,v> e = first; k k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++bincount; } while ((e = e.next) != null); } v oldvalue; if (old != null && (oldvalue = old.value) != null) { afternodeaccess(old); return oldvalue; } } v v = mappingfunction.apply(key); if (v == null) { return null; } else if (old != null) { old.value = v; afternodeaccess(old); return v; } else if (t != null) t.puttreeval(this, tab, hash, key, v); else { tab[i] = newnode(hash, key, v, first); if (bincount >= treeify_threshold - 1) treeifybin(tab, hash); } ++modcount; ++size; afternodeinsertion(true); return v; }
7)computeifpresent(k key, bifunction<? super k, ? super v, ? extends v> remappingfunction);//compute方法的补充,如果key存在,则覆盖新的bifunction计算出的value值,如果不存在则跳过
/** * compute方法的补充,如果key存在,则覆盖新的bifunction计算出的value值,如果不存在则跳过 * @param key * @param remappingfunction * @return */ public v computeifpresent(k key, bifunction<? super k, ? super v, ? extends v> remappingfunction) { if (remappingfunction == null) throw new nullpointerexception(); node<k,v> e; v oldvalue; int hash = hash(key); if ((e = getnode(hash, key)) != null && (oldvalue = e.value) != null) { v v = remappingfunction.apply(key, oldvalue); if (v != null) { e.value = v; afternodeaccess(e); return v; } else removenode(hash, key, null, false, true); } return null; }
8)v replace(k key, v value);//替换新值
/** * 如果key存在不为空,则替换新的value值 */ @override public v replace(k key, v value) { node<k,v> e; if ((e = getnode(hash(key), key)) != null) { v oldvalue = e.value; e.value = value; afternodeaccess(e); return oldvalue; } return null; }
9)replace(k key, v oldvalue, v newvalue);//判断oldvalue是否是当前key的值,再替换新值
/** * 如果key存在不为空并且oldvalue等于当前key的值,则替换新的value值 */ @override public boolean replace(k key, v oldvalue, v newvalue) { node<k,v> e; v v; if ((e = getnode(hash(key), key)) != null && ((v = e.value) == oldvalue || (v != null && v.equals(oldvalue)))) { e.value = newvalue; afternodeaccess(e); return true; } return false; }
10)replaceall(bifunction<? super k, ? super v, ? extends v> function);//替换新值
/** * 根据lambda函数替换符合规则的值 */ @override public void replaceall(bifunction<? super k, ? super v, ? extends v> function) { node<k,v>[] tab; if (function == null) throw new nullpointerexception(); if (size > 0 && (tab = table) != null) { int mc = modcount; for (int i = 0; i < tab.length; ++i) { for (node<k,v> e = tab[i]; e != null; e = e.next) { e.value = function.apply(e.key, e.value); } } if (modcount != mc) throw new concurrentmodificationexception(); } }
总结:
正常情况下会扩容2倍,特殊情况下(新扩展数组大小已经达到了最大值)则只取最大值1 << 30。
2.remove方法(2种重载实现)--删
1)v remove(object key); //根据key 删除元素
/** * 根据key 删除元素 */ public v remove(object key) { node<k,v> e; return (e = removenode(hash(key), key, null, false, true)) == null ? null : e.value; } /** * implements map.remove and related methods * * @param hash hash for key * @param key the key * @param value the value to match if matchvalue, else ignored * @param matchvalue if true only remove if value is equal * @param movable if false do not move other nodes while removing * @return the node, or null if none */ final node<k,v> removenode(int hash, object key, object value, boolean matchvalue, boolean movable) { node<k,v>[] tab; node<k,v> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) != null) { node<k,v> node = null, e; k k; v v; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null) { if (p instanceof treenode) node = ((treenode<k,v>)p).gettreenode(hash, key); else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break; } p = e; } while ((e = e.next) != null); } } if (node != null && (!matchvalue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof treenode) ((treenode<k,v>)node).removetreenode(this, tab, movable); else if (node == p) tab[index] = node.next; else p.next = node.next; ++modcount; --size; afternoderemoval(node); return node; } } return null; }
2)remove(object key, object value); //根据key,value 删除元素
/** * 根据key,value 删除元素 */ @override public boolean remove(object key, object value) { return removenode(hash(key), key, value, true, true) != null; }
3.get方法(2种实现)--查
/** * 返回指定的值 */ public v get(object key) { node<k,v> e; return (e = getnode(hash(key), key)) == null ? null : e.value; } /** * @author jiaxiaoxian * @date 2019年2月12日 * 如果key不为空则返回key的值,否则返回默认值 */ @override public v getordefault(object key, v defaultvalue) { node<k,v> e; return (e = getnode(hash(key), key)) == null ? defaultvalue : e.value; }
4.keyset方法--返回所有key,以set结构返回
/** * 返回所有key,以set结构返回 */ public set<k> keyset() { set<k> ks; return (ks = keyset) == null ? (keyset = new keyset()) : ks; }
5.clone方法--克隆
/** * 复制,返回此hashmap 的浅拷贝 */ @suppresswarnings("unchecked") @override public object clone() { hashmap<k,v> result; try { result = (hashmap<k,v>)super.clone(); } catch (clonenotsupportedexception e) { // this shouldn't happen, since we are cloneable throw new internalerror(e); } result.reinitialize(); result.putmapentries(this, false); return result; }
7.clear方法--清空hashmap
/** * 清空hashmap */ public void clear() { node<k,v>[] tab; modcount++; if ((tab = table) != null && size > 0) { size = 0; for (int i = 0; i < tab.length; ++i) tab[i] = null; } }
8.containskey方法--是否存在此key
/** * 是否存在此key */ public boolean containskey(object key) { return getnode(hash(key), key) != null; }
9.containsvalue方法--是否存在此value
/** * 是否存在此value */ public boolean containsvalue(object value) { node<k,v>[] tab; v v; if ((tab = table) != null && size > 0) { for (int i = 0; i < tab.length; ++i) { for (node<k,v> e = tab[i]; e != null; e = e.next) { if ((v = e.value) == value || (value != null && value.equals(v))) return true; } } } return false; }
10.entryset方法--返回key,value的set结果
/** * 返回key,value的set结果 */ public set<map.entry<k,v>> entryset() { set<map.entry<k,v>> es; return (es = entryset) == null ? (entryset = new entryset()) : es; }
hashmap总结
1)hashmap的key、value都可以存放null,key不可重复,value可重复,是数组链表红黑树的数据结构。 2)hashmap区别于数组的地方在于能够自动扩展大小,其中关键的方法就是resize()方法,扩容为2倍。 3)hashmap由于本质是数组,在不冲突的情况下,查询效率很高,hash冲突后会形成链表,查找时多一层遍历,当链表长度到8并且数组长度大于64,转成红黑树存储,提高查询效率。 4)初始化数组时推荐给初始长度,反复扩容会增加时耗,影响性能效率,hashmap需要注意负载因子0.75f,初始16,当长度大于(16*0.75)12的时候会扩容为32,所以初始长度设置需要却别对待。
©著作权归作者所有:来自51cto博客作者jiazhipeng12的原创作品,如需转载,请注明出处,否则将追究法律责任