github加星 star 艾米莉 2026 Java 集合面试题(高频考点 + 详细解析,最全面总结)

19 阅读11分钟

大家好!本文汇总了 Java 面试核心知识及常用开源框架相关内容,重点聚焦 Java 集合模块的高频面试题,涵盖核心概念、底层原理、实现细节及实战应用,助力大家高效备战面试。因个人知识储备有限,文章若有疏漏或错误,欢迎各位大佬指正!本文将持续更新,敬请关注~

Taimili 艾米莉 ( 一款专业的 GitHub star 管理和github 加星涨星工具taimili.com )

艾米莉 是一款优雅便捷的 GitHub star 管理和github 加星涨星工具,基于 PHP & javascript 构建, 能对github star fork follow watch 刷星管理和提升,最适合github 的深度用户

WX20251021-210346@2x.png

一、Java 面试核心系列文章导航

ID标题地址
1设计模式面试题(总结最全面的面试题)juejin.cn/post/684490
2Java 基础知识面试题(总结最全面的面试题)juejin.cn/post/684490
3Java 集合面试题(总结最全面的面试题)juejin.cn/post/684490
4Java IO、BIO、NIO、AIO、Netty 面试题(总结最全面的面试题)juejin.cn/post/684490
5Java 并发编程面试题(总结最全面的面试题)juejin.cn/post/684490
6Java 异常面试题(总结最全面的面试题)juejin.cn/post/684490
7Java 虚拟机(JVM)面试题(总结最全面的面试题)juejin.cn/post/684490
8Spring 面试题(总结最全面的面试题)juejin.cn/post/684490
9Spring MVC 面试题(总结最全面的面试题)juejin.cn/post/684490
10Spring Boot 面试题(总结最全面的面试题)juejin.cn/post/684490
11Spring Cloud 面试题(总结最全面的面试题)juejin.cn/post/684490
12Redis 面试题(总结最全面的面试题)juejin.cn/post/684490
13MyBatis 面试题(总结最全面的面试题)juejin.cn/post/684490
14MySQL 面试题(总结最全面的面试题)juejin.cn/post/684490
15TCP、UDP、Socket、HTTP 面试题(总结最全面的面试题)juejin.cn/post/684490
16Nginx 面试题(总结最全面的面试题)juejin.cn/post/684490
17ElasticSearch 面试题-
18Kafka 面试题-
19RabbitMQ 面试题(总结最全面的面试题)juejin.cn/post/684490
20Dubbo 面试题(总结最全面的面试题)juejin.cn/post/684490
21ZooKeeper 面试题(总结最全面的面试题)juejin.cn/post/684490
22Netty 面试题(总结最全面的面试题)-
23Tomcat 面试题(总结最全面的面试题)juejin.cn/post/684490
24Linux 面试题(总结最全面的面试题)juejin.cn/post/684490
25互联网相关面试题(总结最全面的面试题)-
26互联网安全面试题(总结最全面的面试题)-

二、集合容器核心概述

1. 什么是集合?

集合是用于存储对象引用的容器,并非存储对象本身。其核心作用是对多个对象进行集中式管理,主要分为三大类型:Set(集)、List(列表)和 Map(映射)。

2. 集合的核心特点

  • 存储对象的容器,对象封装数据,支持多对象统一管理;
  • 可变长度设计,无需提前定义容量,解决了数组固定长度的局限性。

3. 集合与数组的区别

对比维度数组集合
长度特性固定长度,初始化后不可修改可变长度,支持动态扩容 / 缩容
存储数据类型可存储基本数据类型和引用数据类型仅支持存储引用数据类型(基本类型自动装箱)
元素一致性要求必须存储同一数据类型的元素可存储不同数据类型的对象(需手动控制类型安全)

4. 使用集合框架的优势

  • 容量自动增长,无需手动管理容量大小;
  • 内置高性能数据结构与算法(如哈希、红黑树),降低编码复杂度,提升程序效率与质量;
  • 支持灵活扩展与改写,提高代码复用性和可维护性;
  • 依赖 JDK 原生 API,降低学习成本与代码维护难度。

5. 常用集合类体系结构

  • 顶层父接口:Map 接口和 Collection 接口(二者无继承关系);

  • Collection 子接口

    • List 接口:ArrayList、LinkedList、Vector、Stack;
    • Set 接口:HashSet、LinkedHashSet、TreeSet;
  • Map 实现类:HashMap、TreeMap、Hashtable、ConcurrentHashMap、Properties。

6. List、Set、Map 三者核心区别

特性ListSetMap
元素有序性有序(插入顺序 = 取出顺序)无序(HashSet)/ 有序(LinkedHashSet 按插入顺序、TreeSet 按自然顺序)键无序(HashMap)/ 有序(LinkedHashMap 按插入顺序、TreeMap 按键排序)
元素唯一性允许重复元素,可存多个 null不允许重复元素,仅存一个 null键唯一,值可重复;键可为 null(HashMap),Hashtable 不允许 null 键 / 值
数据结构数组(ArrayList/Vector)、双向链表(LinkedList)哈希表(HashSet/LinkedHashSet)、红黑树(TreeSet)数组 + 链表 / 红黑树(HashMap)、红黑树(TreeMap)、哈希表(Hashtable)
索引支持支持索引访问(get (int index))不支持索引访问,需通过迭代器遍历无索引,通过键(key)获取值(value)

7. 集合框架底层核心数据结构

集合类底层数据结构核心特性
ArrayList动态数组(Object [])随机访问快,增删慢(需移动元素)
LinkedList双向循环链表增删快(仅改指针),随机访问慢(需遍历)
Vector动态数组(Object [])+ synchronized 同步线程安全,效率低,扩容翻倍
HashSet基于 HashMap 实现(键存元素,值为固定虚对象)无序、唯一,查询效率 O (1)(无冲突时)
LinkedHashSet哈希表 + 双向链表有序(插入顺序)、唯一,兼顾查询与顺序遍历
TreeSet红黑树(自平衡排序二叉树)自然排序 / 自定义排序,唯一,查询效率 O (logn)
HashMap(JDK8)数组 + 链表(长度≤8)/ 红黑树(长度 > 8)无序、高效,非线程安全
LinkedHashMap数组 + 链表 / 红黑树 + 双向链表有序(插入顺序),非线程安全
TreeMap红黑树按键排序,非线程安全
Hashtable数组 + 链表线程安全(synchronized),效率低,不支持 null 键 / 值
ConcurrentHashMap(JDK8)数组 + 链表 / 红黑树 + synchronized+CAS高并发安全,效率优于 Hashtable

8. 线程安全的集合类有哪些?

  • Vector:ArrayList 的线程安全版本,所有方法加 synchronized 修饰,效率低,不推荐使用;
  • Hashtable:HashMap 的线程安全版本,方法加 synchronized,不支持 null 键 / 值,效率低,已淘汰;
  • ConcurrentHashMap:Java5 + 推出的高并发集合,JDK7 采用分段锁(Segment),JDK8 采用 synchronized+CAS,锁粒度更细,并发性能优异,推荐使用;
  • Collections.synchronizedXxx() :通过工具类包装非线程安全集合(如 synchronizedList、synchronizedMap),本质是对集合操作加锁,适合低并发场景。

9. 集合的快速失败机制(fail-fast)

  • 定义:Java 集合的一种错误检测机制,当多线程对集合进行结构性修改(如 add/remove/clear)时,迭代器遍历会抛出ConcurrentModificationException异常;

  • 原理:迭代器初始化时记录集合的modCount(修改次数),遍历过程中每次调用hasNext()/next()都会校验modCount是否与初始值一致,不一致则抛出异常;

  • 注意:快速失败仅为调试辅助机制,不保证多线程环境下的线程安全,生产环境需通过同步锁或线程安全集合避免;

  • 解决方案

    1. 遍历期间对结构性修改操作加 synchronized 锁;
    2. 使用 CopyOnWriteArrayList/CopyOnWriteArraySet(写时复制,迭代器遍历旧副本,不抛异常)。

10. 如何创建不可修改的集合?

通过Collections.unmodifiableCollection(Collection c)方法创建只读集合,任何修改操作(add/remove/clear)都会抛出UnsupportedOperationException异常。

示例代码

java

运行

List<String> list = new ArrayList<>();
list.add("x");
// 创建只读集合
Collection<String> unmodifiableList = Collections.unmodifiableCollection(list);
unmodifiableList.add("y"); // 运行时抛出UnsupportedOperationException
System.out.println(list.size()); // 输出1

三、Collection 接口核心考点

1. 迭代器 Iterator 是什么?

Iterator 是遍历 Collection 集合的统一接口,取代了早期的 Enumeration,支持遍历过程中安全移除元素。所有 Collection 子类都实现了Iterable接口,通过iterator()方法获取 Iterator 实例。

2. Iterator 的使用方式与特点

使用代码

java

运行

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String obj = it.next(); // 必须先调用hasNext(),否则可能抛NoSuchElementException
    System.out.println(obj);
}

核心特点

  • 单向遍历(仅支持hasNext()next());
  • 快速失败机制(遍历期间集合结构性修改会抛ConcurrentModificationException);
  • 支持安全移除元素(it.remove()),需在next()之后调用,且每次next()只能调用一次remove()

3. 如何边遍历边移除 Collection 中的元素?

正确方式:使用 Iterator 的remove()方法(唯一安全的遍历删除方式):

java

运行

Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    Integer num = it.next();
    if (num % 2 == 0) {
        it.remove(); // 安全移除当前元素
    }
}

错误方式:foreach 循环中直接调用list.remove()(会触发快速失败):

java

运行

// 错误代码,运行抛ConcurrentModificationException
for (Integer i : list) {
    list.remove(i);
}

原因:foreach 底层依赖 Iterator,集合直接修改会导致modCount变化,与迭代器的expectedModCount不一致,触发异常。

4. Iterator 与 ListIterator 的区别

对比维度IteratorListIterator
支持的集合类型Collection(List、Set)仅支持 List 接口
遍历方向单向遍历(仅向后)双向遍历(向前previous()/ 向后next()
额外功能仅支持移除元素(remove()支持添加(add(E e))、替换(set(E e))、获取索引(nextIndex()/previousIndex()

5. List 的三种遍历方式及最佳实践

(1)三种遍历方式

  • for 循环(基于计数器)

    java

    运行

    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
    

    原理:通过索引访问元素,依赖集合的随机访问能力。

  • Iterator 迭代器遍历

    java

    运行

    Iterator<String> it = list.iterator();
    while (it.hasNext()) {
        System.out.println(it.next());
    }
    

    原理:面向接口的遍历模式,屏蔽不同数据结构的差异。

  • foreach 循环遍历

    java

    运行

    for (String s : list) {
        System.out.println(s);
    }
    

    原理:编译后转为 Iterator 遍历,代码简洁,无需手动处理迭代器。

(2)最佳实践

List 是否支持高效随机访问,由RandomAccess接口标记:

  • 支持 RandomAccess(ArrayList、Vector):推荐使用 for 循环,访问效率 O (1);
  • 不支持 RandomAccess(LinkedList):推荐使用 Iterator/foreach,避免索引遍历导致的 O (n²) 时间复杂度。

四、List 接口核心面试题

1. ArrayList 的优缺点

优点:

  • 底层基于数组实现,支持随机访问(实现 RandomAccess 接口),查询效率高(O (1));
  • 顺序添加元素(尾部 add)效率高(O (1)),无需移动元素。

缺点:

  • 插入 / 删除非尾部元素时,需复制移动数组元素(System.arraycopy ()),时间复杂度 O (n),元素越多效率越低;
  • 扩容机制会占用额外内存(默认扩容为原容量的 1.5 倍),可能造成内存浪费。

适用场景:

适合频繁查询、顺序添加元素的场景,不适合频繁插入 / 删除的场景。

2. 数组与 List 的相互转换

(1)数组转 List

使用Arrays.asList(T[] a),注意返回的是固定大小的 List(不可 add/remove),底层依赖原数组:

java

运行

String[] array = new String[]{"123", "456"};
List<String> list = Arrays.asList(array);
// list.add("789"); // 抛UnsupportedOperationException

(2)List 转数组

使用List.toArray()(返回 Object [])或toArray(T[] a)(指定返回数组类型):

java

运行

List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
// 方式1:返回Object[]
Object[] objArray = list.toArray();
// 方式2:指定数组类型(推荐)
String[] strArray = list.toArray(new String[0]);

3. ArrayList 与 LinkedList 的核心区别

对比维度ArrayListLinkedList
底层数据结构动态数组(Object [])双向循环链表
随机访问效率高(O (1)),支持索引访问低(O (n)),需遍历链表
插入 / 删除效率非尾部操作低(O (n),需移动元素)非尾部操作高(O (1),仅改指针)
内存占用占用内存少(仅存元素)占用内存多(每个节点存元素 + 前后指针)
线程安全非线程安全非线程安全
扩容机制默认扩容 1.5 倍(int newCapacity = oldCapacity + (oldCapacity>> 1))无需扩容(链表动态增减节点)

4. ArrayList 与 Vector 的区别

对比维度ArrayListVector
线程安全非线程安全线程安全(所有方法加 synchronized)
性能效率高(无锁开销)效率低(锁竞争导致性能损耗)
扩容机制默认扩容 1.5 倍默认扩容 2 倍
功能支持无枚举器(Enumeration),支持 Iterator/foreach支持 Enumeration 和 Iterator
适用场景单线程环境多线程环境(已被 ConcurrentHashMap 替代)

5. 多线程场景下如何使用 ArrayList?

ArrayList 是非线程安全的,多线程环境下需保证线程安全,推荐方案:

  • 使用Collections.synchronizedList(List<T> list):包装为线程安全集合,本质是对所有操作加锁;

    java

    运行

    List<String> safeList = Collections.synchronizedList(new ArrayList<>());
    safeList.add("aaa");
    
  • 高并发场景推荐使用CopyOnWriteArrayList:写时复制机制,读无锁、写加锁,适合读多写少场景;

    java

    运行

    List<String> cowList = new CopyOnWriteArrayList<>();
    cowList.add("bbb");
    

6. 为什么 ArrayList 的 elementData 数组用 transient 修饰?

  • ArrayList 实现了Serializable接口,支持序列化,但elementData是动态数组,可能存在未使用的空闲容量;
  • transient 修饰表示elementData不参与默认序列化,ArrayList 重写了writeObject()方法,仅序列化实际存储的元素(size 个),而非整个数组;
  • 优势:减少序列化数据体积,提高序列化效率,避免空闲容量占用网络带宽或存储资源。

核心源码

java

运行

private transient Object[] elementData; // 不默认序列化

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
    int expectedModCount = modCount;
    s.defaultWriteObject(); // 序列化非transient字段(如size)
    s.writeInt(elementData.length); // 写入数组容量(用于反序列化扩容)
    for (int i = 0; i < size; i++) {
        s.writeObject(elementData[i]); // 仅序列化实际存储的元素
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}