HashMap底层原理
- jdk1.7的时候是数组+链表;jdk1.8的时候是数组+链表+红黑树
- jdk1.7的时候是无冲突放进数组有冲突放进链表;jdk1.8的时候是无冲突放进数据,有冲突链表长度小于8放进链表,大于8放进红黑树。
- jdk1.7是头插法;jdk1.8是尾插法。
- 在jdk1.6、jdk1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突。同一hash值得链表都存储在一个链表里,但当位于一个桶中的元素较多时,即hash值相同的元素较多时,通过key值依次查找的效率较低。而jdk1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,链表转换为红黑树,这样大大减少了查找时间。
- (jdk1.8中链表长度超过8时为什么使用红黑树)在链表节点中数是8时的概率是千分之一,此时链表的性能已经非常的差了,所以转换为红黑树,来提高性能。
HashMap默认加载为什么选择0.75?
提高空间利用率和减少查询成本的这种,主要是泊松分布,0.75的话碰撞最小。
对比:
- 加载因子过高。例如1,虽然减少了空间开销,提高了空间利用率,但同时增加了查询时间成本。
- 加载因子过低。例如0.5,虽然可以减少查询时间成本,但是空间利用率很低,同时提高了rehash操作的次数。
ArrayList和LinkedList的区别
- ArrayList是基于数组实现的;LinkedList是基于链表实现的。
- ArrayList的查询效率比LinkedList高(因为数组是有序的,LinkedList是无序的,查询后者时需要移动指针)
- LinkedList的插入删除效率比ArrayList要高(因为数组插入删除需要移动下标,对所有的数据下标进行影响,移动数据)
为什么HashMap是线程不安全的?
jdk1.7的时候在多线程的情况下容易出现死循环和丢失数据的情况,主要是因为HashMap冲突时使用的是头插法,也就是链表顺序会翻转,这是形成死循环的关键点。
jdk1.8的时候对HashMap进行了优化,但是HashMap在多线程的情况下任然是线程不安全的,容易出现数据覆盖的情况。比如说线程A和线程B同时进行put操作,线程A还没插入操作就进入挂起状态,这个时候线程B正常执行,获取线程A的CPU时间片,线程A继续进行插入操作,但是不用再进行hash判断,那么线程A就会把线程B的数据给覆盖掉。
解决方法:
Collections中有SynchronizedMap()和SynchronizedSet()方法。还可以使用CopyOnWriteArraySet<>()和ConcurrentHashMap<> ()方法。
HashMap和HashTable有什么区别?
- HashMap是线程不安全的;HashTable是线程安全的。
- 由于HashTable线程安全,所以在效率上,HashTable比不上HashMap。
- HashMap最多只允许一条记录的键为null,允许多条记录的值为null;而HashTable不允许。
- HashMap的初始化数组长度是16,扩容后,扩容两倍;HashTable的初始化数组长度是11,扩容后,扩容两倍+1.
- HashMap需要重新计算hash值;HashTable直接使用对象的hashcode。
为什么HashTable是线程安全的?
HashTable的源码中,remove、get、put方法都加入了synchronized关键字,做成了同步方法,保证了线程安全。
为什么HashTable效率低?
因为HashTable的操作方法都做了同步控制,虽然保证了线程安全,但是带来的结果就是任何一个时刻只能有一个线程控制HashTable,所以HashTable效率比较低。
为什么synchronized可以保证线程安全(synchronized底层原理)?
synchronized代码中来讲就是说,线程1进入后,其他线程就不能进入也就是加锁,必须要等线程1出来后(释放锁),其他线程争抢到线程1的返回值才能进入,以此类推,所以可以保证线程是安全的。
HashMap和ConcurrentHashMap的区别
除了加锁,原理上两者之间无太大的区别。另外,HashMap的键值对允许有null,但是ConcurrentHashMap不允许。
ConcurrentHashMap底层原理(jdk1.7和jdk1.8的区别)
- jdk1.7中ConcurrentHashMap是由Segment(可重入锁)数组结构和HashEntry(存储键值对数据)数组结构组成的。
- jdk1.8中放弃了Segment,取而代之的是Node+CAS+Synchronized来保证并发安全。
HashMap和HashSet的区别
- HashMap实现了Map接口;HashSet实现了Set接口。
- HashMap存储键值对;HashSet仅仅存储对象。
- HashMap使用put()方法将元素放入Map中;HashSet使用add()方法将元素放入Set中。
- HashMap使用键对象来计算hashcode值;HashSet使用成员方法来计算hashcode值(对于两个对象来说,hashcode值可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,返回false)。
- HashMap比较快,因为是使用唯一的键来获取值;HashSet较HashMap来说比较慢。
List去重的五种方式
- 使用LinkedHashSet删除arraylist中的重复数据。
- 使用java8新特性stream进行list去重。
- 利用HashSet不能添加重复数据的特性,由于HashSet不能保证添加顺序,所以只能作为判断条件保证顺序。
- 利用List的contains方法循环遍历,重新排序,只添加一次数据避免重复。
- 双重for循环去重。
Hash解决冲突的方式
开放定址法:
当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探查到开放的地址,则表明表中无待查的关键字,即查找失败。