2025/7/14——java学习总结
集合进阶学习总结:从单列到双列,贯通接口与源码的全维度突破
一、核心知识深耕:集合体系与底层逻辑拆解
(一)Collection 接口:单列集合的行为规范
作为 List
、Set
的顶层接口,定义 增删查遍历 的通用契约:
- 核心方法:
add(E)
、remove(Object)
、iterator()
、size()
、contains(Object)
; - 设计哲学:通过接口解耦 “行为定义” 与 “实现细节”(如
ArrayList
用数组、LinkedList
用链表),支持面向抽象编程。
(二)迭代器(Iterator):遍历的统一范式
- 核心能力:
hasNext()
判空、next()
取值、remove()
删除(需紧跟next()
调用); - 底层机制:
- 快速失败(fail-fast):通过
modCount
(集合修改次数)与expectedModCount
(迭代器记录的修改次数)对比,检测并发修改,抛出ConcurrentModificationException
; - 实现差异:
ArrayList
的Itr
直接操作数组索引,LinkedList
的ListItr
基于链表节点双向遍历。
- 快速失败(fail-fast):通过
(三)增强 for + Lambda:遍历的语法糖进化
- 增强 for:本质是
Iterator
的语法糖(自动创建迭代器),简化代码但 不支持索引操作; - Lambda + forEach:结合
Collection.forEach(Consumer)
,利用函数式接口实现极简遍历(如list.forEach(System.out::println)
),底层仍依赖迭代器。
(四)List 接口:有序集合的精细化操作
- 特有方法:索引操作(
get(int)
、add(int, E)
、remove(int)
)、双向迭代器ListIterator
(支持previous()
反向遍历、中间修改); - 遍历对比:
方式 | 效率(ArrayList) | 效率(LinkedList) | 支持操作 |
---|---|---|---|
普通 for | ✔️ O (1)(随机访问) | ❌ O (n)(遍历) | 索引控制 |
增强 for | ✔️ 语法糖 | ❌ 遍历耗时 | 简单遍历 |
Iterator | ✔️ 通用 | ✔️ 链表友好 | 删除操作 |
ListIterator | ✔️ 双向 | ✔️ 双向 | 中间插入 / 修改 |
Lambda forEach | ✔️ 极简 | ❌ 遍历耗时 | 无状态操作 |
(五)数据结构对比:数组 vs 链表 vs 栈 vs 队列
结构 | 底层实现 | 核心特性 | 典型操作效率 |
---|---|---|---|
数组 | 连续内存 | 随机访问快,增删中间慢 | get() O(1),add(中间) O(n) |
链表 | 离散节点(prev/next) | 增删头尾快,随机访问慢 | addFirst() O(1),get() O(n) |
栈 | 数组 / 链表 | 后进先出(LIFO) | push() /pop() O(1) |
队列 | 链表(如 LinkedList) | 先进先出(FIFO) | offer() /poll() O(1) |
(六)ArrayList 源码:动态数组的扩容艺术
- 初始化:JDK 8+ 无参构造器初始为空数组,首次
add
时扩容为 10; - 扩容策略:默认扩容 1.5 倍(
oldCapacity + (oldCapacity >> 1)
),通过Arrays.copyOf
(native 方法)高效拷贝数组; - 性能陷阱:中间增删触发元素移动(O (n)),批量添加建议提前
ensureCapacity
减少扩容次数。
(七)LinkedList 与迭代器:双向链表的设计
- 结构:内部类
Node<E>
存储元素及前后指针,头尾节点first/last
; - 迭代器:
ListItr
维护当前节点、前一节点,支持双向遍历和中间修改; - 性能短板:遍历效率低于
ArrayList
(链表节点分散,缓存不友好),适合 头尾高频增删 场景。
(八)泛型:类型安全的屏障
- 核心概念:泛型类(
List<E>
)、泛型方法(<T> T func(T t)
)、通配符(? extends E
只读,? super E
可写); - 底层真相:类型擦除(编译期替换为
Object
或边界类型),运行时无泛型信息,需通过反射或@SuppressWarnings
处理类型转换。
(九)Map 接口:双列集合的键值映射
- 核心特点:键唯一(
equals+hashCode
约束),值可重复;支持 键找值、键值对遍历; - 常用 API:
put
、get
、remove
、containsKey
、keySet
、entrySet
; - 实现类差异:
HashMap
:哈希表(数组 + 链表 + 红黑树),无序,允许null
键 / 值;LinkedHashMap
:继承HashMap
,内部维护 双向链表,保持 插入 / 访问顺序;TreeMap
:红黑树实现,按键 自然排序 / 定制排序,不允许null
键。
(十)Map 遍历的三重范式
- 键找值:遍历
keySet()
,通过get(key)
取值; - 键值对:遍历
entrySet()
,获取Map.Entry<K,V>
,同时操作键值; - Lambda 遍历:
forEach(BiConsumer<K,V> action)
,代码极简(如map.forEach((k,v) -> System.out.println(k+":"+v))
)。
(十一)TreeMap 与自定义排序
- 自然排序:键实现
Comparable
接口,重写compareTo
; - 定制排序:创建
TreeMap
时传入Comparator
,优先级高于自然排序(如按字符串长度排序)。
(十二)HashMap 源码核心:哈希与扩容
- 哈希计算:
key.hashCode() ^ (key.hashCode() >>> 16)
,减少高位冲突; - 扩容机制:初始容量 16,负载因子 0.75,扩容时容量 翻倍;链表转红黑树(阈值 8,且数组容量≥64);
- 哈希冲突:链地址法,链表节点超过 8 转为红黑树,查询效率从 O (n) 提升至 O (logn)。
二、实践突破:集合的场景化应用
(一)ArrayList 扩容验证(反射揭秘容量变化)
java
public class ArrayListCapacity {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();for (int i = 0; i < 20; i++) {list.add(i);System.out.println("size: " + list.size() + ", capacity: " + getCapacity(list));}}private static int getCapacity(ArrayList<?> list) {try {Field field = ArrayList.class.getDeclaredField("elementData");field.setAccessible(true);return ((Object[]) field.get(list)).length;} catch (Exception e) {e.printStackTrace();return 0;}}
}
// 输出:首次 add 后容量 10,满后扩容 15(10→15→22...),验证 1.5 倍扩容逻辑
(二)LinkedList 性能对比(头尾 vs 中间操作)
java
public class LinkedListPerfTest {public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();// 头尾操作(O(1),极快)long start = System.currentTimeMillis();for (int i = 0; i < 100000; i++) {list.addFirst(i); // 或 addLast}System.out.println("头尾操作耗时:" + (System.currentTimeMillis() - start) + "ms");// 中间插入(O(n),极慢)start = System.currentTimeMillis();for (int i = 0; i < 10000; i++) {list.add(50000, i); // 中间位置插入}System.out.println("中间插入耗时:" + (System.currentTimeMillis() - start) + "ms");}
}
// 结论:头尾操作高效,中间插入因遍历节点耗时剧增
(三)迭代器 fail-fast 演示(并发修改异常)
java
public class FailFastDemo {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>(List.of("A", "B", "C"));Iterator<String> it = list.iterator();while (it.hasNext()) {String s = it.next();if ("B".equals(s)) {// list.remove(s); // 非迭代器删除,触发 ConcurrentModificationExceptionit.remove(); // 正确方式:通过迭代器删除,避免异常}}System.out.println(list); // 输出:[A, C]}
}
(四)泛型通配符实践(读写边界控制)
java
// 泛型类
class Box<T> { private T value; public T get() { return value; }public void set(T value) { this.value = value; }
}public class GenericWildcard {public static void main(String[] args) {Box<Integer> intBox = new Box<>();intBox.set(10);Box<Double> doubleBox = new Box<>();doubleBox.set(3.14);printBox(intBox); // 上限通配符:接受 Number 及其子类(如 Integer)addNumber(doubleBox); // 下限通配符:接受 Double 及其父类(如 Number、Object)}// 只读:只能 get,不能 set(除 null)public static void printBox(Box<? extends Number> box) {System.out.println(box.get()); // 合法:Number 类型// box.set(20); // 非法:无法确定具体类型}// 可写:只能 set 子类实例(如 Double)public static void addNumber(Box<? super Double> box) {box.set(2.718); // 合法:Double 是 ? super Double 的子类型// box.set(10); // 非法:Integer 不是 Double 的父类}
}
(五)HashMap 存储自定义对象(键唯一验证)
java
class Student {String id;public Student(String id) { this.id = id; }@Overridepublic int hashCode() { return id.hashCode(); }@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return Objects.equals(id, student.id);}
}public class HashMapCustomObject {public static void main(String[] args) {HashMap<Student, String> map = new HashMap<>();map.put(new Student("001"), "Alice");map.put(new Student("001"), "Bob"); // 覆盖,因 id 相同System.out.println(map.size()); // 输出:1}
}
(六)TreeMap 定制排序(按字符串长度排序)
java
public class TreeMapCustomSort {public static void main(String[] args) {TreeMap<String, Integer> map = new TreeMap<>((a, b) -> {int lenCompare = a.length() - b.length();return lenCompare != 0 ? lenCompare : a.compareTo(b);});map.put("Apple", 10);map.put("Banana", 5);map.put("Cherry", 8);map.forEach((k, v) -> System.out.println(k + ": " + v));// 输出:Apple:10, Banana:5, Cherry:8(长度 3、6、6,字典序 Banana < Cherry)}
}
(七)斗地主项目:集合嵌套与发牌逻辑
- 数据结构:
Map<Integer, String>
:序号→牌面(如1→♠3
,54→大王
);List<Integer>
:存储所有牌的序号,用于 洗牌;TreeSet<Integer>
:玩家手牌(自动排序,方便展示)。
- 核心步骤:
- 初始化牌:静态代码块生成 54 张牌的序号和牌面,存入
Map
和List
; - 洗牌:
Collections.shuffle(list)
; - 发牌:遍历
list
,按索引分配给 底牌、玩家 1、2、3(TreeSet
自动排序); - 展示:遍历
TreeSet
,通过Map
获取牌面展示。
- 初始化牌:静态代码块生成 54 张牌的序号和牌面,存入
三、底层原理:集合的运行机制解析
(一)Collection 接口的设计思想
- 依赖倒置:业务代码依赖
Collection
接口,而非具体实现(如ArrayList
),便于切换存储结构(如ArrayList → LinkedList
); - 开闭原则:新增集合类型(如
CopyOnWriteArrayList
)只需实现接口,不影响上层逻辑。
(二)迭代器 fail-fast 的实现
ArrayList
内部维护modCount
(每次增删操作自增);- 迭代器创建时记录
expectedModCount = modCount
; - 每次
next()
/remove()
时校验expectedModCount == modCount
,不等则抛ConcurrentModificationException
,快速暴露并发问题。
(三)ArrayList 扩容的底层逻辑
- 内存拷贝:
Arrays.copyOf
调用 native 方法,直接操作内存块,比 Java 循环拷贝快 3~5 倍; - 扩容触发:当
size == capacity
时触发,空构造器首次add
会初始化容量为 10; - 内存碎片:频繁扩容可能导致内存浪费,建议通过
ensureCapacity
预分配空间。
(四)LinkedList 的链表模型
- 节点内存:每个
Node
占 3 个引用(item
、prev
、next
),内存分散,缓存命中率低(对比ArrayList
的连续数组); - 头尾操作:直接修改
first/last
指针,时间复杂度 O (1);中间操作需遍历节点,时间复杂度 O (n)。
(五)泛型的类型擦除
- 编译期处理:
List<String>
编译后变为List<Object>
,泛型信息仅用于编译期类型检查; - 运行时陷阱:反射获取泛型类型需通过
TypeToken
(如 Gson 反序列化),否则会丢失类型信息。
(六)HashMap 哈希冲突与红黑树转换
- 链地址法:数组每个桶存链表头节点,冲突时链表追加;
- 链表转红黑树:当桶内链表长度≥8,且数组容量≥64 时,链表转为红黑树(
TreeNode
),查询效率从 O (n) 提升至 O (logn); - 扩容时:旧数组元素重新哈希到新数组,链表节点可能拆分到不同桶,红黑树则拆分为链表或保持树结构。
(七)TreeMap 红黑树原理
- 自平衡二叉搜索树,确保左右子树高度差≤1,查询 / 插入 / 删除均为 O (logn);
- 节点颜色:红或黑,通过 旋转和变色 保持平衡,保证有序性。
(八)LinkedHashMap 双向链表
- 继承
HashMap
,内部维护header
双向链表,记录 插入顺序(或访问顺序,开启accessOrder
); - 遍历时按链表顺序,比
HashMap
的无序遍历更高效(如需按插入顺序处理)。
(九)集合嵌套的内存模型(以斗地主为例)
Map
存储序号和牌面的映射,List
存储序号(洗牌的载体),TreeSet
存储玩家手牌(自动排序);- 内存关联:
TreeSet
中的序号指向Map
中的牌面,实现 “序号→牌面” 的关联展示。
四、总结与展望:从 “会用” 到 “精通” 的进阶
今日突破:
- 技术维度:贯通 Collection(单列) 与 Map(双列) 体系,掌握迭代器、泛型、数据结构对比、源码分析(
ArrayList
/LinkedList
/HashMap
/TreeMap
),结合斗地主项目实践 集合嵌套; - 实践细节:规避
ArrayList
扩容陷阱、LinkedList
中间操作低效、迭代器删除异常、泛型通配符读写限制,掌握Map
键唯一性(hashCode/equals
)、TreeMap
排序策略、HashMap
扩容与红黑树转换触发条件; - 底层认知:接口解耦思想、fail-fast 实现、动态数组扩容算法、链表内存模型、泛型类型擦除、哈希冲突解决、红黑树平衡机制、集合嵌套关联逻辑。
后续规划:
技术深化:
- 并发集合:剖析
ConcurrentHashMap
(CAS + synchronized 替代分段锁)、CopyOnWriteArrayList
(读写分离)的线程安全实现; - Map 源码攻坚:
HashMap
扩容全流程、TreeMap
红黑树旋转细节、LinkedHashMap
链表维护; - 集合工具类:
Collections.sort
(归并排序优化)、同步集合(synchronizedMap
)、不可变集合(ImmutableList
); - 泛型高级:
Map<K,V>
的通配符(? extends K
只读键,? super V
可写值)、自定义泛型映射。
设计模式融合:
- 工厂模式:封装集合创建(如根据需求选
HashMap
/TreeMap
); - 策略模式:
TreeMap
的Comparator
作为排序策略,动态切换; - 适配器模式:将
Map
转换为List
(如entrySet
转List
进行排序)。
工程化实践:
- 性能优化:
HashMap
初始容量计算(避免频繁扩容)、TreeMap
排序开销权衡、LinkedHashMap
访问顺序优化; - 线程安全:区分
Hashtable
(全锁)、ConcurrentHashMap
(分段锁)的适用场景; - 单元测试:
Map
键冲突测试、TreeMap
排序异常测试、集合嵌套的空指针防护; - 异常处理:
HashMap
的null
键处理、TreeMap
的null
键异常(非Comparable
时)。
细节攻坚:
- 优化 HashMap:通过
initialCapacity
和loadFactor
减少扩容次数,避免链表转红黑树的性能波动; - 深入红黑树:手动模拟
TreeMap
的插入和旋转过程,理解平衡机制; - 集合嵌套调试:斗地主项目中牌序异常(如
TreeSet
排序不符合预期)的排查方法; - 泛型与 Map:解决 JSON 反序列化时
Map
泛型类型丢失问题(结合TypeToken
)。
持续在 “接口规范 → 实现细节 → 源码分析 → 工程优化” 的闭环中迭代,不仅掌握集合的表层用法,更深入理解 哈希、红黑树、链表 等底层数据结构,以及 并发、泛型、设计模式 的融合应用,为 高并发缓存、有序数据存储、复杂集合嵌套逻辑 等业务场景提供高效解决方案 ✨。