Map
HashMap
底层是数组、内部使用数组+链表+红黑树
先看看hashMap在jdk 1.8的结构,用的是数组+链表+红黑树的结构,也叫哈希桶,在jdk 1.8之前都是数组+链表的结构,因为在链表的查询操作都是O(N)的时间复杂度,而且hashMap中查询操作也是占了很大比例的,如果当节点数量多,转换为红黑树结构,那么将会提高很大的效率,因为红黑树结构中,增删改查都是O(log n)。
HashMap是基于哈希表(散列表),实现Map接口的双列集合,数据结构是“链表散列”,也就是数组+链表 ,key唯一的value可以重复,允许存储null 键null 值,元素无序。
为什么默认容量大小为16,加载因子为0.75,主要原因是这两个常量的值都是经过大量的计算和统计得出来的最优解,仅仅是这样而已。
内部节点
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent;
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev;
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
}
注意点容量大小、加载因子、
解决hash冲突的办法
Java中hashmap的解决办法就是采用的链地址法
当哈希表的容量超过默认容量时,必须调整table的大小。当容量已经达到最大可能值时,那么该方法就将容量调整到Integer.MAX_VALUE返回,这时,需要创建一张新表,将原表的映射到新表中。
1、什么是hashmap、为什么要用
2、hashmap工作原理
3、hashmap get()的工作原理
4、两个对象hashcode相同会发生什么
5、两个键的hashcode相同,如何取值对象
6、为什么需要加载因子
7、重新调整hasnmap的大小会产生什么问题
当hashMap中的节点数超过阈值的时候,就会自动扩容,扩容的时候就会调整hashMap的大小,一旦调整了hashMap的大小就会导致之前的hashCode计算出来的hash表中下标无效,所以所有的节点都需要重新hash运算,结果就是带来时间上的浪费。因此我们要尽量避免hashMap调整大小,所以我们使用hashMap的时候要给hashMap设置一个默认值,这个默认值要大于我们hashMap中存放的节点数。
8、为什么string、integer这样的wrapper适合作为键
其内容固定,不可变,稳定
9、我们可以使用自定义的对象作为键吗
可
10、什么是阈值、什么时候扩容
扩容必须满足两个条件:
1、 存放新值的时候当前已有元素的个数必须大于等于阈值
2、 存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)
关于hashmap的思想在threadlocal中的应用
threadlocal包含threadlocalmap。threadlocalmap持有thread的引用,和thread绑定关系,一个thread拥有同一个threadlocalmap。threadlocalmap里有一个entry数组,数组里存储Entry(ThreadLocal<?> k, Object v),以散列的形式来防止冲突。持有threadlocal的引用作为key,实际的值作为value,以此确保同一个thread内entry位置不同,不同thread互不干扰。
LinkedHashMap
可以从上图中看到,LinkedHashMap数据结构相比较于HashMap来说,添加了双向指针,分别指向前一个节点——before和后一个节点——after,从而将所有的节点已链表的形式串联一起来,从名字上来看LinkedHashMap与HashMap有一定的联系,实际上也确实是这样,LinkedHashMap继承了HashMap,重写了HashMap的一部分方法,从而加入了链表的实现
LinkedHashMap是比HashMap多了一个链表的结构。与HashMap相比LinkedHashMap维护的是一个具有双重链表的HashMap,LinkedHashMap支持2中排序一种是插入排序,一种是使用排序,最近使用的会移至尾部例如 M1 M2 M3 M4,使用M3后为 M1 M2 M4 M3了,LinkedHashMap输出时其元素是有顺序的,而HashMap输出时是随机的,如果Map映射比较复杂而又要求高效率的话,最好使用LinkedHashMap,但是多线程访问的话可能会造成不同步,所以要用Collections.synchronizedMap来包装一下,从而实现同步。另外,LinkedHashMap可以实现快速的查询第一个元素(First)跟结尾(Last)。
WeakHashMap
具有弱引用的key。内部是entry数组,触发垃圾回收的时候会把entry的key关联到ReferenceQueue,下次进来调用部分方法会通过key获取entry,然后清理value
(01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
(02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
(03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。
HashTable
hashTable竞争单个锁,效率低,不如CurrentHasnMap效率高,看下面
IdentityHashMap
TreeMap
ConCurrentHashMap
双重hash
JDK1.7版本的ReentrantLock+Segment+HashEntry
JDK1.8版本中synchronized+CAS+HashEntry+红黑树
ConcurrentHashMap原理分析(1.7与1.8)