深度掌握Java集合框架是成为合格Java开发者的必经之路。 本文将全面解析异常处理机制与Collection集合体系的核心知识,助你构建坚实的Java基础。
一、Collection集合框架全景
1. 集合体系架构
集合类型 | 特点 | 实现类 |
---|---|---|
List | 有序、可重复、有索引 | ArrayList, LinkedList |
Set | 无序、不重复、无索引 | HashSet, TreeSet |
Queue | FIFO(先进先出) | LinkedList |
2. Collection核心方法
Collection是集合的祖宗类,它的功能是全部集合都可以继承使用的,所以要学习它。
Collection<String> coll = new ArrayList<>();
coll.add("Java"); // 添加元素
coll.remove("C++"); // 删除元素
coll.contains("Python");// 检查存在
coll.size(); // 获取大小
coll.clear(); // 清空集合
Object[] arr = coll.toArray(); // 转为数组
3. Collection的遍历方式
(1). 迭代器遍历 - 最通用的遍历方式
核心原理:通过Iterator
对象实现解耦访问
Collection集合获取迭代器的方法
Iterator迭代器中的常用方法
Collection<String> coll = new ArrayList<>();
coll.add("Java");
coll.add("Python");
coll.add("Go");
Iterator<String> it = coll.iterator();
while(it.hasNext()) {
String element = it.next();
System.out.println(element);
// 安全删除当前元素
if("Python".equals(element)) {
it.remove(); // ✅ 唯一线程安全的删除方式
}
}
关键特性:
- 🛡️ 并发安全:唯一支持遍历时安全删除元素的方式
- 🔄 单向移动:只能向前遍历,不可回溯
- ⚠️ 注意:每次调用
next()
前必须用hasNext()
检查
(2) 增强for循环 - 语法糖的典范
本质:编译器自动转换为迭代器实现
格式:
for (元素的数据类型 变量名 : 数组或者集合) {
}
快捷键:数组或者集合 . for +回车
for(String language : coll) {
System.out.println(language);
// coll.remove(language); // ❌ 触发ConcurrentModificationException
}
优势与局限:
- ✨ 代码简洁:比传统迭代器减少50%代码量
- ⚡ 编译优化:自动处理迭代器创建和边界检查
- ⛔ 禁止修改:遍历时修改集合会抛出并发修改异常
(3) Lambda表达式 - JDK8+的现代化方案
需要使用Collection的如下方法来完成
函数式编程实践:
技术本质:
- 🎯 基于
Iterable
接口的forEach()
方法 - 🔧 底层仍使用迭代器实现
- 💡 重要特性:同样不支持遍历时修改集合
(4) 普通for循环 - List专属方案
仅适用于List实现类:
if(coll instanceof List) {
List<String> list = (List<String>) coll;
for(int i=0; i<list.size(); i++) {
System.out.println(list.get(i));
// 倒序遍历删除避免索引错位
if(i > 0 && "Java".equals(list.get(i-1))) {
list.remove(i-1);
i--; // 索引回退保持正确位置
}
}
}
适用场景:
- 🔢 需要索引访问的复杂操作
- 🔄 倒序遍历删除元素
- ⚠️ 注意:非List集合无法使用此方式
4. 性能关键数据
遍历方式 | 时间复杂度 | 内存开销 | 修改安全性 |
---|---|---|---|
迭代器 | O(n) | 低 | 高(支持删除) |
增强for | O(n) | 低 | 低 |
Lambda | O(n) | 低 | 低 |
普通for | O(n) | 最低 | 中(需特殊处理) |
最佳实践建议:在JDK8+环境中,优先使用
forEach()
+Lambda的组合,兼顾简洁性与可读性;当需要修改集合时,务必切换为迭代器模式,这是避免并发异常的黄金法则。
二、List集合深度解析
List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
1. List特有方法
List<String> list = new ArrayList<>();
list.add(1, "Spring"); // 指定位置插入
list.remove(0); // 按索引移除
list.set(2, "MySQL"); // 替换元素
String item = list.get(1); // 获取元素
2. 四大遍历方式对比
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("糖宝宝");
list.add("蜘蛛精");
list.add("至尊宝");
//(1)for循环
for (int i = 0; i < list.size(); i++) {
// i = 0 1 2
String s = list.get(i);
System.out.println(s);
}
//(2)迭代器。
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
//(3)增强for循环(foreach遍历)
for (String s : list) {
System.out.println(s);
}
//(4)JDK 1.8开始之后的Lambda表达式
list.forEach(s -> {
System.out.println(s);
});
}
3. ArrayList底层原理
- 数组结构:初始容量10
- 扩容机制:1.5倍动态扩容
- 特点:
- 查询速度快
- 尾部插入高效
- 中间插入/删除慢(需移动元素)
4. LinkedList底层原理
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
- 双向链表:节点结构
prev|data|next
- 特有方法:
- 应用场景:
- 队列实现:FIFO(先进先出)
Queue<String> queue = new LinkedList<>(); // 入队 queue.addLast("第1号人"); queue.addLast("第2号人"); // 出队 System.out.println(queue.removeFirst()); System.out.println(queue.removeFirst()) queue.offer("A"); // 入队 queue.poll(); // 出队
- 栈实现:LIFO(后进先出)
Deque<String> stack = new LinkedList<>(); stack.push("A"); // 压栈 push() = addFirst() stack.pop(); // 弹栈 pop() = removeFirst()
注意:ArrayList和LinkedList 底层采用的数据结构不同,应用场景不同
三、Set集合核心探秘
Set系列集合特点: 无序:添加数据的顺序和获取出的数据顺序不一致; 不重复; 无索引;
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:可排序、不重复、无索引。
注意:
Set要用到的常用方法,基本上就是Collection提供的!!
自己几乎没有额外新增一些常用功能!
1. HashSet实现机制
哈希表结构:数组+链表+红黑树(JDK8+)
红黑树,就是可以自平衡的二叉树
红黑树是一种增删改查数据性能相对都较好的结构。去重原理:
如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法- 比较
hashCode()
- 若哈希冲突,再比较
equals()
// 自定义对象去重要例 @Override public int hashCode() { return Objects.hash(name, age); } @Override public boolean equals(Object o) { // 属性值比较逻辑 }
- 比较
2. LinkedHashSet特性
依然是基于哈希表(数组、链表、红黑树)实现的。但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。
- 双向链表:维护元素插入顺序
- 性能平衡:在HashSet基础上增加顺序维护
3. TreeSet排序控制
红黑树结构:自动排序,对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
对于自定义类型如Student对象,TreeSet默认是无法直接排序的。
排序规则:
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。- 方式一:
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。 - 方式二:
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则。
- 实现
Comparable
接口
public class Student implements Comparable<Student> { @Override public int compareTo(Student s) { return this.age - s.age; } }
- 自定义
Comparator
TreeSet<Student> students = new TreeSet<>( (s1, s2) -> s1.getName().compareTo(s2.getName()) );
- 方式一:
四、关键问题解决方案
1. 并发修改异常处理
// 正确删除方式(迭代器)
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String item = it.next();
if("removeMe".equals(item)) {
it.remove(); // 使用迭代器删除,当前遍历到的数据,每删除一个数据后,相当于也在底层做了i--
}
}
// 逆向遍历删除
for(int i=list.size()-1; i>=0; i--) {
if("removeMe".equals(list.get(i))) {
list.remove(i);
}
}
2. 集合选型黄金法则
场景需求 | 推荐集合 | 原因剖析 |
---|---|---|
高频随机访问 | ArrayList | 数组结构O(1)访问 |
频繁首尾操作 | LinkedList | 双向链表高效增删 |
去重且无需排序 | HashSet | 哈希表最佳查询性能 |
去重且保留插入顺序 | LinkedHashSet | 哈希表+链表维护顺序 |
去重且需要自动排序 | TreeSet | 红黑树保证有序性 |
结语:技术本质思考
Java集合框架的精髓在于根据数据结构特性解决特定场景问题。理解这三点至关重要:
- 时间复杂度决定性能:ArrayList的O(1)随机访问 vs LinkedList的O(1)头尾操作
- 空间与时间的平衡:HashSet以空间换时间,TreeSet以排序换性能
- 设计模式应用:迭代器模式实现统一遍历接口,装饰者模式扩展功能(如LinkedHashSet)
高效编程的本质不是记住所有API,而是掌握数据结构的灵魂。 当你深刻理解每种集合的底层实现原理时,技术选型将不再是选择题,而是基于场景的最优解推导。