跳至主要內容
ConcurrentHashMap

ConcurrentHashMap是如何保证线程安全的?

在JDK 1.7中,ConcurrentHashMap使用了分段锁技术,即将哈希表分成多个段,每个段拥有一个独立的锁。这样可以在多个线程同时访问哈希表时,只需要锁住需要操作的那个段,而不是整个哈希表,从而提高了并发性能。

虽然JDK 1.7的这种方式可以减少锁竞争,但是在高并发场景下,仍然会出现锁竞争,从而导致性能下降。

在JDK 1.8中,ConcurrentHashMap的实现方式进行了改进,使用分段锁和“CAS+Synchronized”的机制来保证线程安全。 在JDK 1.8中,ConcurrentHashMap会在添加或删除元素时,首先使用CAS操作来尝试修改元素,如果CAS操作失败,则使用Synchronized锁住当前槽,再次尝试put或者delete。这样可以避免分段锁机制下的锁粒度太大,以及在高并发场景下,由于线程数量过多导致的锁竞争问题,提高了并发性能。


Znyoung大约 8 分钟Java集合线程
HashMap(二)

为什么在JDK8中HashMap要转成红黑树

为什么不继续使用链表

我们知道,HashMap解决hash冲突是通过拉链法完成的,在JDK8之前,如果产生冲突,就会把新增的元素增加到当前桶所在的链表中。
这样就会产生一个问题,当某个bucket冲突过多的时候,其指向的链表就会变得很长,这样如果put或者get该bucket上的元素时,复杂度就无限接近于O(N),这样显然是不可以接受的。
所以在JDK1.7的时候,在元素put之前做hash的时候,就会充分利用扰动函数,将不同KEY的hash尽可能的分散开。不过这样做起来效果还不是太好,所以当链表过长的时候,我们就要对其数据结构进行修改。


Znyoung大约 5 分钟Java集合HashMap
fail-fast和fail-safe

fail-fast

在系统设计中,快速失效(fail-fast)系统一种可以立即报告任何可能表明故障的情况的系统。快速失效系统通常设计用于停止正常操作,而不是试图继续可能存在缺陷的过程。

其实,这是一种理念,说白了就是在做系统设计的时候先考虑异常情况,一旦发生异常,直接停止并上报。

public int divide(int dividend,int divisor){
    if(divisor == 0){
        throw new RuntimeException("divisor can't be zero");
    }
    return dividend/divisor;
}

Znyoung大约 3 分钟Java集合基础
ArrayList、LinkedList与Vector的区别

区别

特性 ArrayList LinkedList Vector
线程安全性 非线程安全 线程安全 线程安全
底层实现 数组 双向链表 数组
动态扩容 支持 不支持 支持
访问元素速度 相对较慢
插入/删除速度 相对较慢
接口实现 List List, Deque List
允许存放重复元素 默认不允许
扩容大小 当前容量的一半 无扩容概念 当前容量的一半
访问元素方式 索引 遍历链表 索引

Znyoung大约 2 分钟Java集合
HashMap(一)

HashMap的数据结构

在Java中,保存数据有两种比较简单的数据结构:数组和链表。

数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。

常用的哈希函数的冲突解决办法中有一种方法叫做链地址法,其实就是将数组和链表组合在一起,发挥了两者的优势,我们可以将其理解为链表的数组。在JDK 1.8之前,HashMap就是通过这种结构来存储数据的。


Znyoung大约 10 分钟Java集合HashMap
哈希冲突怎么解决

下面假设hash=(x) -> x/2,hash表的长度为10,x可取5,6等,此时hash(5)=hash(6)=3,产生冲突

开放定址法

开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。

也被称为再散列法,当出现hash冲突的时候,会将上一次hash的结果和增量序列相加,然后对hash表的长度取模,进行再散列;如当3=hash(5) =hash(6)时,此时hash(6)就需要利用开放定址法解决冲突:hash(6)=(3+d)%length = p1 (d为增量序列)
其中,增量序列的获取方式有三种:


Znyoung大约 2 分钟Java集合哈希
Java的集合框架

Java的集合框架有哪些数据结构?

  1. List(列表):有序的集合,可以包含重复元素。常用的实现类有ArrayList、LinkedList和Vector。
  2. Set(集):不允许包含重复元素的集合。常用的实现类有HashSet和TreeSet。
  3. Queue(队列):按照一定规则进行插入和删除操作的集合。常用的实现类有LinkedList和PriorityQueue。
  4. Map(映射):存储键值对的集合,每个键只能出现一次。常用的实现类有HashMap、TreeMap和LinkedHashMap。
  5. Stack(栈):后进先出(LIFO)的集合,常用的实现类是Stack。
  6. Deque(双端队列):可以在两端进行插入和删除操作的队列。常用的实现类有ArrayDeque和LinkedList。

Znyoung大约 2 分钟Java集合
Set是如何保证元素不重复的

Set是如何保证元素不重复的

在Java的Set体系中,根据实现方式不同主要分为两大类。HashSet和TreeSet。

  • HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束;底层基于HashMap

在HashSet中,基本的操作都是有HashMap底层实现的,因为HashSet底层是用HashMap存储数据的。当向HashSet中添加元素的时候,首先计算元素的hashCode值,然后通过扰动计算和按位与的方式计算出这个元素的存储位置,如果这个位置为空,就将元素添加进去;如果不为空,则用equals方法比较元素是否相等,相等就不添加,否则找一个空位添加。


Znyoung大约 2 分钟Java集合