Java集合

2020-05-12  本文已影响0人  Yves_Chen

Java集合

类库关系图

类库关系图

List

ArrayList:Object数组

Vector: Object数组

LinkedList: 双向循环链表

Set

HashSet(无序,唯一)

TreeSet(有序,唯一)

红黑树(自平衡的排序二叉树。)

并发场景使用跳跃表替代红黑树原因:

1.树的增删操作维持平衡时,涉及多个节点的转换,并发场景只能锁上整棵树。

2.基于链表的跳跃表增删操作可基于CAS实现粒度更小的加锁。

Queue

boolean add(E e)

Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and throwing an IllegalStateException if no space is currently available.

E element()

Retrieves, but does not remove, the head of this queue.

boolean offer(E e)

Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions.

E peek()

Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.

E poll()

Retrieves and removes the head of this queue, or returns null if this queue is empty.

E remove()

Retrieves and removes the head of this queue.

关系图

关系图

BlockingQueue

void put(E e)

Inserts the specified element into this queue, waiting if necessary for space to become available.

E take()

Retrieves and removes the head of this queue, waiting if necessary until an element becomes available.

Deque

ConcurrentLinkedQueue

一个适用于高并发场景下的队列,通过无锁的方式(CAS+volatile),实现了高并发下的高性能,通常ConcurrentLinkedQueue的性能好于BlockingQueue。

它是一个基于链接节点的无界线程安全队列,遵循先进先出的原则,头是最先加入的,尾是最近加入的,不允许加入null元素。

注意add()/offer()都是加入元素的方法,这里没有区别;poll()/peek()是取出头元素的方法,区别点在于poll会删除元素,而peek不会。

要特别注意到由于它的非阻塞性,并不像其他普通集合那样,获取队列的SIZE的方法并不是常量时间的花费,而是O(N)的,因此我们应该尽可能避免使用size()方法,可以考虑使用isEmpty()代替。

虽然使用到了CAS+VOLATILE的机制避免了锁,但是我们要明白的是,这只是保证单个操作,如peek()的安全,但是多个操作如果想保证的话,需要使用锁机制来达到同步的效果。

Map

HashMap

非线程安全

JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。

HashMap 的长度为什么是2的幂次方?

为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。

这个算法应该如何设计呢?

我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。

底层数据接口和HashMap一致,1.7之前数组+链表,1.8之后数组+链表+红黑树。

实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

HashTable

线程安全

数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的

TreeMap

红黑树(自平衡的排序二叉树)

LinkedHashMap

在HashMap基础上,引入了链表保存元素加入顺序,方便实现基于最近访问原则的淘汰机制(删除链表尾)。

WeakHashMap

https://www.cnblogs.com/yw-ah/p/5830458.html

http://www.importnew.com/23182.html

上一篇下一篇

猜你喜欢

热点阅读