HashSet的存储原理
HashSet的存储原理或者工作原理,主要是从如何保证唯一性来说起。
这里面主要有3个问题,需要回答?
第一,为什么要采用Hash算法?有什么优势,解决了什么问题?
第二,所谓哈希表是一张什么表?
第三,HashSet如何保证保存对象的唯一性?会经历一个什么样的运算过程?
大家可以先思考,晚些再补充答案!
首先,我们要明确一点,HashSet底层采用的是HashMap来实现存储,其值作为HashMap的key
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
具体关于hashmap的细节再说
第一,为什么要采用Hash算法?有什么优势,解决了什么问题?
解决的问题是唯一性
存储数据,底层采用的是数组
当我们往数组放数据的时候,你如何判断是否唯一?
可以采用遍历的方式,逐个比较,但是这种效率低,尤其是数据很多的情况下
所以,为了解决这个效率低的问题,我们采用新的方式
采用hash算法,通过计算存储对象的hashcode,然后再跟数组长度-1做位运算,得到我们要存储在数组的哪个下标下,如果此时计算的位置没有其他元素,直接存储,不用比较。
此处,我们只会用到hashCode
但是随着元素的不断添加,就可能出现“哈希冲突”,不同的对象计算出来的hash值是相同的,这个时候,我们就需要比较,才需要用到equals方法
如果equals相同,则不插入,不相等,则形成链表
第二,所谓哈希表是一张什么表?
本质是一个数组,而且数组的元素是链表
JDK1.7的版本实现
JDK1.8做了优化
随着元素不断添加,链表可能会越来越长,会优化红黑树
LinkedHashMap和HashMap的区别(重点)
此处,我们好好谈谈HashMap
主要关注几个点:
1,初始化大小是16,如果事先知道数据量的大小,建议修改默认初始化大小。 减少扩容次数,提高性能 ,这是我一直会强调的点
2,最大的装载因子默认是0.75,当HashMap中元素个数达到容量的0.75时,就会扩容。 容量是原先的两倍
3,HashMap底层采用链表法来解决冲突。 但是存在一个问题,就是链表也可能会过长,影响性能
于是JDK1.8,对HashMap做了进一步的优化,引入了红黑树。
当链表长度超过8,且数组容量大于64时,链表就会转换为红黑树
当红黑树的节点数量小于6时,会将红黑树转换为链表。
因为在数据量较小的情况下,红黑树要维护自身平衡,比链表性能没有优势。
这3点非常重要!
其次,LinkedHashMap就是链表+散列表的结构,其底层采用了Linked双向链表来保存节点的访问顺序,所以保证了有序性。
ConcurrentHashMap,HashMap,Hashtable的区别
1,首先,来看看其他几个相关的类
Hashtable是线程安全的,但效率低
HashMap是线程不安全的,但效率高
Collections.synchronizedMap(),工具类提供了同步包装器的方法,来返回具有线程安全的集合对象
性能依然有问题
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<>(m);
}
//在这个类的内部方法实现上,也只是单纯加上了锁
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
为解决这样的矛盾问题,所以JDK提供了并发包,来平衡这样的问题(java.util.concurrent)
2,ConcurrentHashMap(重点)
兼顾了线程安全和效率的问题
分析:HashTable锁了整段数据(用户操作是不同的数据段,依然需要等待)
解决方案:把数据分段,执行分段锁(分离锁),核心把锁的范围变小,这样出现并发冲突的概率就变小
在保存的时候,计算所存储的数据是属于哪一段,只锁当前这一段
注意:分段锁(分离锁)是JDK1.8之前的一种的方案,JDK1.8之后做了优化。
JDK1.7跟JDK1.8在ConcurrentHashMap的实现上存在以下区别:
1,数据结构
JDK1.7采用链表的方式,而JDK1.8则采用链表+红黑树的方式
2,发生hash碰撞之后
JDK1.7发生碰撞之后,会采用链表的方式来解决
JDK1.8发生碰撞之后,默认采用链表,但当链表的长度超过8,且数组容量超过64时,会转换为红黑树存储
3,保证并发安全
JDK1.7采用分段锁的方式,而JDK1.8采用CAS和synchronized的组合模式
4,查询复杂度
JDK1.7采用链表的方式,时间复杂度为O(n),而JDK1.8在采用红黑树的方式时,时间复杂度为O(log(n))
题外话:
不过红黑树其实是一种兜底方案,因为当链表数量达到8个的时候,其发生的概率是千万分之几,所以作者考虑到这种极端情况下,需要用红黑树的方式来优化
ArrayList vs Vector
说句实话,对这种古老的Vector,之所以你在笔试题会遇到,我感觉是面试官偷懒了。
来吧,我们看看
ArrayList:线程不安全,效率高,常用
Vector:线程安全的,效率低
我们看Vector的源码:是被synchronize关键字上锁的