Java 容器(Collection)是 JDK 中用于存储、管理和操作一组对象的核心组件,也是日常开发中高频使用的基础工具。本文基于 JDK 8(Java 8)官方文档(Java SE 8 官方 API 文档)和《Java 核心技术 卷 1》(第 10 版),系梳理 Java 容器类的体系

一、Java 容器类的整体体系

Java 容器类主要分为两大顶层接口:Collection(存储单个元素的集合)和 Map(存储键值对的映射),其主要整体继承 / 实现关系如下:

分类简要说明

顶层接口 特征 摘要
Collection 存储一组独立元素,允许重复(List)或不允许(Set),支持有序(List/LinkedHashSet)或无序(HashSet) 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。
Map 存储键值对(Key-Value),Key 唯一,Value 可重复,支持根据 Key 快速查找 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。

二、Collection 接口核心实现类

1. List 接口:有序、可重复的集合

List 接口保证元素的插入顺序,允许通过索引访问元素,支持重复元素。

(1)ArrayList

基于动态扩容的数组(Object [])实现

特性

  • 随机访问快(O (1)),增删慢(需移动元素,O (n))
  • 非线程安全(多线程下需手动同步,如 Collections.synchronizedList
  • 初始容量 10,扩容时默认扩容为原容量的 1.5 倍

使用场景:读多写少、需要快速随机访问的场景(如查询列表数据)

(2)LinkedList

基于双向链表实现

核心特性

  • 随机访问慢(O (n)),增删快(只需修改链表指针,O (1))
  • 同时实现了 Deque 接口,可作为队列 / 双端队列使用
  • 非线程安全

使用场景:写多读少、频繁增删元素的场景(如队列操作、栈操作)

(3)Vector

与 ArrayList 类似(数组),但线程安全(方法加 synchronized 修饰)

缺陷:同步开销大,效率低,已被 ArrayList + 手动同步替代。

Stack:Vector 的子类,基于栈实现(先进后出),但推荐使用 Deque 替代

2. Set 接口:无序、不可重复的集合

Set 接口不允许重复元素(基于 equals()hashCode() 判断),默认无序(HashSet),部分实现支持有序(LinkedHashSet/TreeSet)。

(1)HashSet

基于 HashMap 实现(底层是哈希表),元素存储在 HashMap 的 Key 位置,Value 固定为 PRESENT 常量

核心特性

  • 无序(不保证插入顺序),查询 / 增删效率高(O (1))
  • 不可重复,允许存储 null(但仅一个)
  • 非线程安全

去重原理:添加元素时,先通过 hashCode() 计算哈希值,再通过 equals() 比较,两者都相同则视为重复元素,拒绝添加。

(2)LinkedHashSet

继承 HashSet,基于 LinkedHashMap 实现(哈希表 + 双向链表)

特性:保证插入顺序,其余特性与 HashSet 一致;性能略低于 HashSet,但迭代遍历更快

(3)TreeSet

基于 TreeMap 实现(红黑树)

特性

  • 元素自然排序(实现 Comparable 接口)或自定义排序(传入 Comparator
  • 查询 / 增删效率为 O (log n)
  • 不允许存储 null(会抛出 NullPointerException

3. Queue 接口:队列(先进先出)

Queue 接口用于实现队列(FIFO)操作,核心实现类:

  • LinkedList:实现 Deque 接口,可作为双端队列(Deque)、栈使用
  • PriorityQueue:优先级队列,基于堆实现,元素按优先级排序(默认自然排序),非线程安全

三、Map 接口实现类

Map 接口存储键值对,Key 唯一,主要实现类如下:

1. HashMap

JDK 8 中为数组 + 链表 + 红黑树(当链表长度 ≥8 且数组长度 ≥64 时,链表转为红黑树)

特性

  • 无序,Key 不可重复、Value 可重复,允许 Key/Value 为 null(Key 仅一个 null)
  • 非线程安全,并发场景下可能出现死循环(JDK 7)或数据丢失(JDK 8)
  • 初始容量 16,负载因子 0.75,扩容为原容量的 2 倍

2. LinkedHashMap

继承 HashMap,在哈希表基础上增加双向链表,维护插入顺序或访问顺序

特性:保证键值对的插入顺序(默认)或访问顺序(构造方法传入 accessOrder=true),其余特性与 HashMap 一致。

3. TreeMap

基于红黑树实现

核心特性

  • Key 按自然排序或自定义排序,不允许 Key 为 null
  • 非线程安全,查询 / 增删效率为 O (log n)

4. Hashtable

基于哈希表(数组 + 链表),方法加 synchronized 修饰,线程安全

缺陷:同步粒度大(整个方法加锁),效率低,已被 ConcurrentHashMap 替代;不允许 Key/Value 为 null。

5. ConcurrentHashMap

JUC(java.util.concurrent)包下的线程安全 Map,替代 Hashtable

JDK 8 优化:摒弃分段锁,改用 CAS + synchronized 实现细粒度锁,效率大幅提升。

四、线程安全的容器

除了上述提到的 Vector、Hashtable、ConcurrentHashMap,Java 还提供了以下线程安全容器:

  1. Collections 工具类包装:通过 Collections.synchronizedList(List)Collections.synchronizedSet(Set)Collections.synchronizedMap(Map) 为非线程安全容器添加同步锁,底层是 “装饰器模式”
  2. CopyOnWriteArrayList/CopyOnWriteArraySet:基于 “写时复制” 实现,读操作无锁,写操作复制新数组,适合读多写少的场景
  3. ConcurrentLinkedQueue/ConcurrentLinkedDeque:无锁并发队列,基于 CAS 实现,适合高并发场景。

五、容器类的使用原则

根据访问特性选择

  • 随机访问优先选 ArrayList
  • 频繁增删优先选 LinkedList
  • 去重且无序选 HashSet,去重且有序选 LinkedHashSet/TreeSet
  • 键值对存储:无序选 HashMap,有序选 LinkedHashMap/TreeMap,并发场景选 ConcurrentHashMap

避免线程安全陷阱:非并发场景不要用 Vector/Hashtable,并发场景优先选 JUC 包下的容器(如 ConcurrentHashMap)

重写 equals/hashCode:自定义对象作为 Set 的元素或 Map 的 Key 时,必须重写这两个方法,否则无法保证去重逻辑(来源:《Java 核心技术 卷 1》第 9 章:“如果重新定义 equals 方法,就必须重新定义 hashCode 方法,以便用户可以将对象插入到散列表中”)

六、总结

  1. Java 容器类分为 Collection(单元素集合)和 Map(键值对映射)两大体系,核心实现类的底层数据结构决定了其性能和使用场景
  2. 非线程安全容器(ArrayList/HashMap 等)性能更高,线程安全场景优先选择 JUC 包下的实现(如 ConcurrentHashMap)

更多推荐