在上一章中,我们对Java集合框架有了整体认识。🌹
现在,让我们深入探索Collection家族中的每位"明星成员",不仅要了解它们的特性,更要掌握一些实用的记忆技巧,帮助你对这些集合类型烂熟于心。🌹
如有描述不准确之处,欢迎大家指出交流。🌹
Java Collection接口家族详解
在Java集合框架中,Collection接口家族就像一个精心设计的工具箱,每个工具都有其独特的用途。本文将深入介绍这些"明星"集合类,带你掌握它们的特点和最佳实践。
Collection框架概览
下面用一张图来表示Collection家族之间的关系,以及里面的一些常见集合的优缺点。

Collection接口家族主要成员
- List: 有序可重复集合
- ArrayList:最常用的List实现,
- LinkedList:适合频繁增删的场景
- Vector:早期的线程安全实现
- Set: 不重复集合
- HashSet:无序唯一集合
- LinkedHashSet:有序唯一集合
- TreeSet:排序唯一集合
- Queue: 队列集合
- PriorityQueue:优先队列
- Deque:双端队列
- Stack:后进先出栈
- ArrayDeque:数组双端队列
- ConcurrentLinkedQueue:并发队列
List详解
由浅:
常见实现类对比
ArrayList
- 基于动态数组实现
- 查询速度快,插入和删除慢
- 需要考虑扩容机制
- 最常用的List实现
最常用的List实现,是基于动态数组实现(之所以叫动态是因为数组容量可以动态变化)、查询速度快、插入和删除慢。这点很容易理解,数组一般需要一开始就定义好容量,如果空间不够用了, 需要扩容,重新定义一个大的空间(怎么定义大空间又涉及到扩容机制,这里浅的时候咱不细谈)。然后将元素全部在放到这个空间中去,这里的操作会有时间成本问题。但是因为是数组实现,可以通过寻址立马查到某一个元素,所以效率高。
LinkedList
- 基于双向链表实现
- 插入删除快,查询慢
- 不存在扩容问题
- 适合频繁增删场景
适合频繁增删的场景,LinkedList可以和ArrayList做一个鲜明的对比:LinkedList采用双向链表实现,由一个个Node节点通过前后指针相连。由于链表结构的特性,它不需要像数组那样预分配连续空间,只要堆内存充足就可以不断添加节点。在插入操作时,只需修改插入位置前后节点的指针指向即可,不需要像ArrayList那样移动大量元素;删除操作也只需要修改待删除节点前后节点的指针指向,操作复杂度都是O(1)。但需要注意的是,虽然增删效率高,但查找元素时需要遍历链表,性能不如ArrayList。
Vector
- 线程安全版ArrayList
- 性能较差(synchronized实现)
- 已被CopyOnWriteArrayList替代
早期的线程安全实现, Vector又可以和ArrayList做一个鲜明的对比:两个都是基于动态数组实现,但是Vector是线程安全的,所以他的方法都加了synchronized关键字,但正是由于这个原因,所以它的效率低。
这里大家要知道synchronized是一个重量级的锁,在并发量大的情况下,性能会下降。所以我们基本上很少使用Vector。而是使用CopyOnWriteArrayList。
源码深入分析
入深:
1. ArrayList源码解析
// 底层数组实现
transient Object[] elementData;
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 核心扩容方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为原来的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList的核心原理主要体现在以下几个方面:
- 扩容机制
- 当容量不足时,会触发grow方法进行扩容
- 默认扩容为原容量的1.5倍(oldCapacity + oldCapacity >> 1)
- 扩容过程中会创建新数组并复制元素,性能开销较大
- 因此在已知元素数量的情况下,建议使用带初始容量的构造方法
- 线程安全性
- ArrayList是非线程安全的
- 多线程并发修改时可能导致数据不一致
- 如需线程安全可以:
- 使用Collections.synchronizedList()包装
- 使用CopyOnWriteArrayList
- 自行加锁控制并发访问
- 删除元素原理
- 删除指定位置元素时,会将该位置后的所有元素向前移动一位
- 元素移动通过System.arraycopy实现
- 删除操作的时间复杂度为O(n)
- 频繁删除场景建议使用LinkedList
- 遍历性能
- 支持随机访问,通过索引访问元素的时间复杂度为O(1)
- for循环遍历性能优于迭代器遍历
- 不要在遍历过程中对List进行结构性修改,会导致ConcurrentModificationException
这些特性决定了ArrayList适合读多写少的场景,不适合频繁增删和并发访问的场景。在使用时需要根据实际情况权衡选择合适的List实现类。
2. LinkedList源码解析
// 节点定义
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
// 头尾指针
transient Node<E> first;
transient Node<E> last;
// 添加元素实现
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null</

最低0.47元/天 解锁文章
1242






