目录
hashmap1.8源码大纲
1 HashMap继承与AbstratMap实现了Map、cloneable、serilizable接口。
2 首先有一个静态长整型serialVersionUID。
3 然后是一些常量的定义比如默认初始容量16,最大容量2的30次方,浮点数默认加载因子0.75,链表转树树阈值8,树转链表阈值6,最小转成树的map容量为64。
4 然后是 节点类node继承与Map的Entry,Node类有四个成员变量 hash,key,value,next,构造方法还有getKey,getValue,toString,hashCode,setvalue,equals方法。
5 然后是静态公用工具方法,比如hash(Object),comparableClassFor(Object),compareComparables(),tableSizeFor(),
6 然后是一些成员变量,Node数组table,Entry Set entrySet,size,modCount,int threshold,浮点数loadfactor,
7 然后是一些公共的操作,HashMap的四个构造函数,putMapEntries(),size(),isEmpty(),get(),getNode(),containsKey(),put(),putVal(),resize(),treeifyBin(),putAll(),remove(),removeNode(),clear(),containsValue(),keySet(),类KeySet,values,类Values,entrySet(),类EntrySet,
8 然后是一些覆盖的方法,比如getOrDefault(),putIfAbsent(),remove(),replace(),replace(),compareIfAbsent(),compareIfAbsent(),compute(),merge(),forEach(),replaceAll(),clone(),
9 然后是一些其他的方法loadFactor(),capacity(),writeObject(),readObjet(),
10 然后是迭代器相关,抽象类HashIterator,类keyItetator,valueIterator,EntryIterator,
11 然后是分离器相关,HashMapSpliterator,keySpliterator,ValueSpliterator,EntrySpliterator,
12 然后是链表支持,newNode(),replacementNode(),newTreeNode(),replacementTreeNode(),reinitialize(),afterNodeAccess(),afterNodeInsertion(),afterNodeRemoval()
13 最后是TreeNode类,继承于LinkedHashMap.Entry
那么问题来了?
hashmap的数据结构?
数组 链表 红黑树(java>1.7)
为什么用数组?数组索引下标存取快,O(1),查找的时候也要遍历链表,
红黑树对链表性能做了优化,接近于平衡二叉树,
查找插入删除
为什么扩容长度必须是2的指数次幂也就是2的n次方?
因为hash运算之后存储在数组的那个位置是有hash值对数组长度取模计算完成。
1取模运算的效率低于位运算
2必须是2的n次方取模和位运算才能划等号,h&length-1 == h%length
3Java8以下会rehash,rehash将会非常消耗性能
为什么加载因子是0.75?
牛顿二项式 时间空间的平衡 .net是0.72
为什么数组转链表阈值是8?
泊松分布 8的概念非常小亿分之六,而且链表为8但是数组长度不满64也不会转红黑树。链表为8其实意味着当前链表长度为9,因为是算的表头以外长度为8.
key能否为空?
可以为空,HashMap最多只允许一条记录的键为null,允许多条记录的值为null(hashtable是不允许的,会报异常)。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
} //来自hashmap源码
HashMap mHashMap = new HashMap();
mHashMap.put(null, "11");
mHashMap.put(null, 11);
mHashMap.put(null, 11.9f);
System.out.println(mHashMap.size()); //打印的值为1
HashMap mHashMap = new HashMap();
mHashMap.put(1, null);
mHashMap.put(2, null);
mHashMap.put(3, null);
System.out.println(mHashMap.size());//打印的值为3
hashmap为什么线程不安全?
因为扩容死锁会形成链表死环。
10个线程,每个线程put100万次
为什么会形成链表死环?
扩容,链表调换顺序,第二个线程就会杨过和小龙女互相指向,形成链表死环。