Java集合框架是日常开发使用率最高、面试必考、工程核心的知识点。相比于固定长度、功能单一的数组,集合可动态扩容、支持海量数据操作、内置丰富增删改查、排序、去重、队列特性,是存储业务数据、实现业务逻辑的核心容器。
本文全覆盖核心考点:List(ArrayList/LinkedList)、Set(HashSet/TreeSet)、Map(HashMap/TreeMap)、Queue队列、迭代器Iterator、Comparable与Comparator比较器,搭配底层原理、完整实战代码、性能对比、高频坑点、工程规范、面试标准答案。
一、Java集合整体体系概述
Java集合分为两大顶层根接口,所有集合实现类均基于以下体系拓展,也是面试底层认知基础:
1.1 两大核心根体系
-
Collection(单列集合):存储单个元素,包含三大子接口
-
List:有序、可重复、有索引
-
Set:无序、不可重复、无索引
-
Queue:队列集合,先进先出、阻塞队列特性
-
-
Map(双列集合):存储键值对(Key-Value),Key唯一、Value可重复
1.2 常用实现类速览(工程高频)
-
List:ArrayList、LinkedList
-
Set:HashSet、TreeSet
-
Map:HashMap、TreeMap
-
Queue:LinkedList、ArrayDeque、BlockingQueue
二、List 有序可重复集合
List特性:元素有序(存入顺序与取出顺序一致)、可重复、自带索引、支持根据下标精准操作元素。核心两大实现类:ArrayList、LinkedList。
2.1 ArrayList(数组实现)
2.1.1 底层原理
底层基于动态扩容数组实现,默认初始化容量10,扩容机制:原容量1.5倍扩容,每次扩容需要数组拷贝。
2.1.2 核心优缺点
-
优点:支持下标随机访问,查询速度极快,遍历效率高
-
缺点:中间/头部增删元素需要移动数组元素,增删效率低,扩容产生性能损耗
2.1.3 基础实战代码
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("Java");
list.add("MySQL");
list.add("Java"); // 允许重复元素
// 遍历、查询
System.out.println("根据下标取值:" + list.get(0));
System.out.println("集合所有元素:" + list);
// 删除、修改
list.remove(1);
list.set(0, "Java进阶");
System.out.println("操作后元素:" + list);
}
}
2.2 LinkedList(双向链表实现)
2.2.1 底层原理
底层基于双向链表实现,每个节点存储前驱、后继指针和数据,无需连续内存,不支持扩容,按需创建节点。
2.2.2 核心优缺点
-
优点:首尾增删元素无需移动数据,增删效率极高,无扩容损耗
-
缺点:无下标随机访问,查询需要遍历链表,查询效率低
2.2.3 基础实战代码
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(10);
list.add(20);
// 首尾快速操作(LinkedList专属优势)
list.addFirst(5);
list.addLast(30);
System.out.println("首尾操作后:" + list);
list.removeFirst();
list.removeLast();
System.out.println("删除首尾后:" + list);
}
}
2.3 ArrayList vs LinkedList 面试终极对比
| 对比维度 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 查询效率 | 快(随机访问O(1)) | 慢(遍历查询O(n)) |
| 增删效率 | 慢(需移动元素、扩容) | 快(仅修改指针) |
| 内存占用 | 连续内存,利用率高 | 节点存指针,内存冗余大 |
| 使用场景 | 查询多、遍历多、增删少 | 首尾频繁增删、队列场景 |
三、Set 无序去重集合
Set特性:元素无序、不可重复、无索引,核心用于数据去重、自动排序场景,主流实现:HashSet、TreeSet。
3.1 HashSet(哈希去重)
3.1.1 底层原理
底层基于 HashMap 实现,利用哈希算法去重,通过 hashCode\(\) \+ equals\(\) 双重校验判断元素是否重复。
3.1.2 去重规则(面试必考)
-
先调用
hashCode\(\)获取哈希值,哈希值不同:直接判定不重复,存入集合 -
哈希值相同:再调用
equals\(\)比较内容,返回true判定重复,拒绝存入
核心坑点:自定义对象存入HashSet,必须重写hashCode和equals方法,否则无法去重!
3.1.3 实战代码(自定义对象去重)
import java.util.HashSet;
import java.util.Objects;
// 自定义实体类,必须重写hashCode+equals
class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
// 重写:根据业务字段判断重复
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(name, user.name) && Objects.equals(age, user.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class HashSetDemo {
public static void main(String[] args) {
HashSet<User> set = new HashSet<>();
set.add(new User("张三", 20));
set.add(new User("张三", 20)); // 重复元素,自动去重
set.add(new User("李四", 22));
System.out.println("去重后集合:" + set);
}
}
3.2 TreeSet(有序去重)
3.2.1 底层原理
底层基于 TreeMap 红黑树实现,自动升序排序+去重,支持自然排序、自定义比较器排序。
3.2.2 核心特性
-
默认对数值、字符串自然排序
-
自定义对象必须指定排序规则(Comparable/Comparator),否则直接报错
-
有序、去重、查询效率稳定
3.2.3 基础排序实战
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
// 自动自然升序排序+去重
TreeSet<Integer> set = new TreeSet<>();
set.add(5);
set.add(2);
set.add(9);
set.add(2); // 重复自动去除
// 输出有序结果:[2,5,9]
System.out.println("TreeSet排序结果:" + set);
}
}
四、Map 键值对集合
Map特性:双列键值对存储,Key唯一不可重复、Value可重复,Key重复会覆盖原有Value,是业务数据映射、缓存存储核心容器。
4.1 HashMap(无序键值对)
4.1.1 底层核心原理(JDK1.8 面试重中之重)
底层结构:数组 + 链表 + 红黑树,解决哈希冲突,兼顾查询与增删效率
-
默认容量16,负载因子0.75,扩容阈值12,扩容2倍
-
链表长度>8 且 数组长度>64:链表转为红黑树
-
红黑树节点数<6:退化为链表
4.1.2 核心特性
无序存储、查询速度极快、允许null键null值(仅一个null key)、线程不安全。
4.1.3 遍历实战(三种主流方式)
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapDemo {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
map.put("Java", 100);
map.put("MySQL", 90);
map.put("Java", 99); // key重复,覆盖value
// 方式1:遍历key
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key + "=" + map.get(key));
}
// 方式2:遍历键值对(推荐,效率最高)
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
4.2 TreeMap(有序键值对)
4.2.1 底层原理
底层基于红黑树实现,默认根据Key自然升序排序,支持自定义排序规则,Key不可为null。
4.2.2 核心特性
-
Key自动排序、唯一去重
-
有序存储,适合排序映射场景
-
增删查效率稳定,略低于HashMap
五、Queue 队列集合
Queue队列特性:遵循FIFO先进先出规则,多用于任务队列、消息队列、限流排队、线程池任务存储。
5.1 常用队列实现类
-
LinkedList:普通非阻塞队列,基于链表实现
-
ArrayDeque:数组双端队列,效率高于LinkedList,推荐优先使用
-
BlockingQueue:阻塞队列,多线程并发核心,满阻塞、空阻塞
5.2 队列基础实战
import java.util.Queue;
import java.util.ArrayDeque;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new ArrayDeque<>();
// 入队
queue.offer("任务1");
queue.offer("任务2");
queue.offer("任务3");
// 出队(先进先出)
while (!queue.isEmpty()) {
System.out.println("执行:" + queue.poll());
}
}
}
六、迭代器 Iterator(集合通用遍历)
Iterator是所有单列集合的通用遍历方式,也是集合底层遍历原理,解决普通for循环遍历删除元素的并发修改异常问题。
6.1 核心方法
-
hasNext\(\):判断是否有下一个元素 -
next\(\):获取下一个元素 -
remove\(\):安全删除当前遍历元素
6.2 迭代器安全删除实战(解决并发修改异常)
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
// 迭代器安全遍历删除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String name = it.next();
if ("李四".equals(name)) {
it.remove(); // 迭代器自带删除,无并发异常
}
}
System.out.println(list);
}
}
6.3 高频坑点:并发修改异常
问题场景:增强for循环/普通for循环遍历集合时,直接调用集合remove()删除元素,触发 ConcurrentModificationException。
解决方案:遍历删除元素必须使用 迭代器remove() 或 JDK8 removeIf。
七、比较器:Comparable vs Comparator(排序核心)
Java集合排序仅有两种方式:自然排序Comparable、自定义排序Comparator,是TreeSet/TreeMap/集合工具类排序的底层核心,面试高频对比考点。
7.1 Comparable(内部自然排序)
特点:实体类内部实现接口,定义默认排序规则,属于侵入式排序。
实战代码
// 实体类实现Comparable,重写默认排序规则
class Student implements Comparable<Student>{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
// 自然排序:年龄升序
@Override
public int compareTo(Student s) {
return this.age - s.age;
}
@Override
public String toString() {
return name + "-" + age;
}
}
7.2 Comparator(外部比较器自定义排序)
特点:外部定义排序规则,不修改实体类源码,非侵入式,灵活适配多场景排序,工程开发首选。
实战代码(Lambda简化写法)
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class ComparatorDemo {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("张三", 20));
list.add(new Student("李四", 18));
list.add(new Student("王五", 22));
// 外部自定义排序:年龄降序(灵活修改,不改动实体类)
Collections.sort(list, (s1, s2) -> s2.getAge() - s1.getAge());
System.out.println("自定义排序:" + list);
}
}
7.3 两大比较器终极面试对比
| 对比维度 | Comparable(内部比较器) | Comparator(外部比较器) |
|---|---|---|
| 位置 | 实体类内部实现 | 外部单独定义 |
| 侵入性 | 侵入源码,修改需改实体类 | 无侵入,不改动原有代码 |
| 排序规则 | 固定默认排序 | 灵活多变,支持多场景排序 |
| 使用场景 | 全局统一默认排序 | 临时排序、多规则差异化排序 |
八、集合工程最佳实践 & 避坑指南
-
优先使用ArrayList:90%业务场景查询遍历居多,性能最优
-
HashMap初始化容量:预估数据量,避免频繁扩容(初始容量=数据量/0.75+1)
-
自定义对象存Set/Map:必须重写hashCode+equals,否则去重失效
-
遍历删除元素:禁止普通for/增强for删除,使用迭代器或removeIf
-
排序优先Comparator:外部排序解耦,符合开闭原则
-
线程安全:普通集合线程不安全,并发场景使用ConcurrentHashMap、CopyOnWriteArrayList
九、全文核心总结(面试必背)
-
List:有序可重复有索引,ArrayList查快增删慢,LinkedList首尾增删快
-
Set:无序不可重复无索引,HashSet哈希去重,TreeSet有序排序去重
-
Map:键值对存储,HashMap无序高效,TreeMap按键有序排序
-
Queue:先进先出队列,适合任务排队、消息队列场景
-
迭代器:集合通用遍历,唯一安全遍历删除方式,解决并发修改异常
-
比较器:Comparable内部默认排序,Comparator外部灵活自定义排序,工程优先用后者
十、高频面试简答题
-
ArrayList和LinkedList区别? 基于数组和双向链表实现,ArrayList查询快增删慢,LinkedList首尾增删快查询慢。
-
HashSet去重原理? 重写hashCode+equals,先比哈希值、再比内容,双重校验去重。
-
HashMap底层结构? JDK1.8采用数组+链表+红黑树,链表过长树化,优化查询性能。
-
为什么遍历删除会报并发修改异常? 增强for循环基于迭代器实现,遍历中修改集合长度会触发修改次数校验失败。
-
Comparable和Comparator区别? 前者内部侵入式默认排序,后者外部非侵入式灵活排序,业务开发优先使用Comparator。
-
TreeSet为什么能排序去重? 底层红黑树,基于比较器规则判断大小与重复,自动排序去重。
-
ArrayDeque和LinkedList队列区别? ArrayDeque基于数组,效率更高、无指针冗余,优先作为队列使用。