Java常用容器总结

在工作中经常会碰到需要各种容器的场景,Java为我们提供了很多的容器,本文将对常用的一些容器做一个总结。

从Java的集合继承关系图中我们可以看到Java的容器一共分为List,Queue,Stack,Set,Map这五种,其中List,Stack,Queue其实在逻辑结构上几乎没有区别,Stack比较特殊,因为在实际开发中几乎不会被用到,不对Stack做相关总结。

一、List

非线程安全的List

LinkedList,ArrayList分别基于链表,数组实现。

线程安全的List

  • Vector
    通过给可能出现并发问题的public方法加上synchronized关键字来同步,性能极低,已被弃用。
  • Collections.synchronizedList(new ArrayList<>())
    这是Collections工具类提供一个方法可以将一个ArrayList变成线程安全的List,和Vector的区别主要是扩容方式不容,加锁方式不同,具体可以参考这篇博客Vector和SynchronizedList区别,其实我个人感觉在线程安全方面的性能差别几乎没有,但是既然Vector已经被弃用了就用这个就可以。
  • Collections.synchronizedList(new LinkedList<>())
    和synchronizedList一样,通过装饰器模式实现的线程安全链表。
  • CopyOnWriteArrayList
    基于COW思想设计的一款线程安全的List,原理是读写分离,读没有影响,写时复制并用ReentrantLock加锁。
    优点是读的效率高,完全不受同步的影响
    缺点是写时复制的会增加GC的次数,并且只保证最终一致性,不保证强一致性。
    所以适用于读多写少的场景。

二、Queue

非线程安全的Queue

ArrayDeque,LinkedList,PriorityQueue分别基于数组,双向链表,堆实现。

线程安全的Queue (队列广泛用于生产者-消费者模式,所以提供的真的多)

阻塞指的的是:当队列满时生产者阻塞,空时消费者阻塞
无界指的是:没有capacity限制,会一直offer,直到OOM

  • ArrayBlockingQueue
    基于数组的,阻塞的,有界的,定长的线程安全队列。
  • LinkedBlockingQueue
    基于链表的,阻塞的,有界的线程安全队列,最大容量为Integer.MAX_VALUE。
  • LinkedBlockingDeque
    基于双向链表的,阻塞的,无界的线程安全队列,最大容量为Integer.MAX_VALUE。
  • PriorityBlockingQueue
    基于堆的,阻塞的,无界的,有优先级的线程安全队列。
  • DelayQueue
    基于PriorityQueue的,阻塞的,无界的,支持延时消费的线程安全队列。
  • SynchronousQueue
    没有容量的,阻塞的线程安全队列。
  • LinkedTransferQueue
    基于链表的,阻塞的,无界的线程安全队列
  • ConcurrentLinkedQueue
    基于链表的,非阻塞的,无界的线程安全队列
  • ConcurrentLinkedDeque
    基于双向链表的,非阻塞的,无界的线程安全队列
  • Collections.synchronizedCollection()
    最后,当然也可以使用Collections提供的方法构造自己需要的线程安全的队列,不过队列很丰富,大部分情况下都应该满足需求了。

三、Set

非线程安全的Set

HashSet,LinkedHashSet,TreeSet,Set和Map的底层实现都比价复杂,HashSet是基于HashMap直接取keySet实现,LinkedHashSet则是在HashSet基础上增加一个链表维护顺序,TreeSet也是基于TreeMap实现的。

线程安全的Set

  • CopyOnWriteArraySet
    类似于CopyOnWriteArrayList,适用于读多写少的场景。
  • ConcurrentSkipListSet
    基于跳表的线程安全Set,是有序的,但是这里的有序和TreeSet的实现不是一样的。
  • Collections.synchronizedSet()
    可以使用Collections方法封装自己需要的线程安全Set

四、Map

非线程安全的Map

HashMapLinkedHashMapTreeMap。TreeMap是基于红黑树实现的来保持顺序的。

线程安全的Map

  • HashTable
    和Vector类似,性能很差,已经被弃用了。
  • ConcurrentHashMap
    自jdk1.8以后放弃了分段锁,改用Node + CAS + Synchronized的方式,同时底层增加红黑树支持,性能相对提升了很多,是目前线程安全Map首要的选择。
  • ConcurrentSkipListMap
    基于跳表的线程安全Map,是有序的,之所以没有基于红黑树,应该是考虑到红黑树在考虑并发安全实现过于复杂的原因。
  • Collections.synchronizedMap()
    可以使用Collections方法封装自己需要的线程安全Map

综上,Java为我们日常编程提供了非常丰富的容器,基本上能够满足所有的场景,底层的数据结构无非是数组,链表,堆,跳表,红黑树等这些我们所了解的,线程安全也无非是乐观(CAS)和悲观(锁)两种。
但是随着容器的复杂化,底层的实现不论在数据结构,还是在线程安全方面都变得越来越复杂,如果要深入的研究透这里所有的容器是要花很大的精力和时间的,尤其是最后的ConcurrentHashMap和ConcurrentSkipListMap源码都比较复杂,所以我认为在必要的时候再去深入研究就好。

你可能感兴趣的:(Java)