Java集合框架中的List与Deque的区别与用途

不能把Deque当成普通List用,因为其设计目标是双端操作而非随机访问,get(int index)不保证O(1),如LinkedList的get()为O(n),ArrayList实现Deque时首尾操作性能极差。

为什么不能把 Deque 当成普通 List

因为 Deque 的设计目标不是随机访问,而是双端操作。它不保证 get(int index) 是 O(1) —— 比如 LinkedList 实现的 Deque,调用 get(5000) 会从头或尾遍历,实际是 O(n);而 ArrayList 虽然实现了 Deque 接口,但它的 addFirst()removeLast() 都要移动大量元素,性能极差。

常见错误现象:
- 用 ArrayDequelist.get(i) 做遍历,发现越往后越慢
- 把 LinkedList 当作“既能当队列又能当列表”的万能容器,结果在中间插入/查找时卡顿

  • Deque 关键方法是 offerFirst()pollLast()peek(),不是 get()set()
  • 若需要按索引查改 + 频繁首尾增删 → 选 ArrayList(首部操作少)或 ArrayDeque(只做首尾,不索引访问)
  • LinkedList 在 Java 21+ 已被明确标记为“legacy”,官方建议用 ArrayDeque 替代其双端队列用途

ArrayDequeArrayList 在栈/队列场景下的真实开销差异

ArrayDeque 底层是循环数组,扩容时复制数组但只复制有效段;ArrayList 扩容是整块复制,且首部插入需整体位移。实测插入 10 万次到头部:
- ArrayList.add(0, x):约 8 秒(JDK 17)
- ArrayDeque.offerFirst(x):约 8 毫秒

Deque stack = new ArrayDeque<>();
stack.push("a");   // 等价于 offerFirst
stack.pop();       // 等价于 pollFirst

Deque queue = new ArrayDeque<>(); queue.offer("b"); // 等价于 offerLast queue.poll(); // 等价于 pollFirst

  • ArrayDeque 不允许 null 元素,ArrayList 允许 —— 这是运行时才暴露的兼容性坑
  • ArrayDeque 的初始容量是 16,但不会像 ArrayList 那样在 add() 时立即扩容;它更懒,直到真正填满才扩
  • 如果业务需要“带索引的队列”(比如取第 3 个待处理任务),别硬套 Deque,老实用 ArrayList + 明确注释说明访问模式

什么时候该用 List,什么时候必须用 Deque

看操作模式,不是看“要不要存多个值”。List 的语义是“有序序列,支持位置定位”;Deque 的语义是“双端线性结构,支持 LIFO/FIFO 行为”。

  • 需要 subList()indexOf()sort()、或频繁 for (int i=0; i → 必须 List
  • 实现撤销栈(push/pop)、BFS 队列(offer/poll)、滑动窗口(offerLast + pollFirst 当超长)→ 优先 ArrayDeque
  • 需要线程安全的双端队列?不用 synchronized(new LinkedList()),直接用 ConcurrentLinkedDeque —— 它不实现 List,也不支持 get()

Deque 实现类选型的隐藏约束

ArrayDeque 不是万能的:它不能序列化为 JSON 时保持顺序(某些库会把它当 Collection 扁平输出),也不能直接传给期待 List 的 Spring Data JPA 方法(比如 repository.findAllById(list) 会报 ClassCastException)。

  • Spring Boot 3.x 默认 Jackson 会把 ArrayDeque 序列化成数组,但反序列化时若字段声明为 Deque,可能构造出 LinkedList 实例(取决于模块注册)
  • MyBatis 中 可能失败,因为 MyBatis 对 Deque 的反射支持弱于 List
  • 如果你写的工具方法签名是 void process(List items),就别传 ArrayDeque 进去 —— 即使它“恰好”实现了 ListArrayListArrayDeque 都没实现 List!只有 LinkedList 同时实现两者)

最常被忽略的一点:ArrayDequeLinkedList 都实现了 Deque,但只有 LinkedList 实现了 List;而 ArrayList 实现了 List 却没实现 Deque —— 接口实现不是按“功能相似”自动继承的,得看源码里写了几个 implements