Java 基础 02:面向对象、集合框架与泛型(正确用上接口与算法复杂度)
初级必读:类/接口/抽象的分工、equals 与 hashCode 契约、List/Set/Map 选型、迭代与并发修改;泛型如何避免强制转换地狱。
- Java
- 集合
- 泛型
- 基础
本篇目标:你能为业务对象正确实现
equals/hashCode;能说明ArrayList/HashMap常见操作复杂度;能用泛型避免强转;能解释 fail-fast 与「可变对象作 key」的坑。
1. 知识点:接口 / 抽象类 / 组合#
| 机制 | 何时用 | 反模式 |
|---|---|---|
interface | 多种实现、对外契约 | 接口膨胀成「上帝接口」 |
abstract class | 复用骨架,保留一步抽象 | 深层继承树 |
| 组合 | 「有一个」能力委托 | 仅为复用代码而继承 |
学以致用:价格策略 interface Pricing { Money quote(Order o); },促销/会员各实现一类,上层只依赖接口。
2. 知识点:equals 与 hashCode 契约(逐项)#
对用作 HashMap/HashSet 元素的类型:
- 自反:
x.equals(x)。 - 对称:
x.equals(y)⇒y.equals(x)。 - 传递:相等关系传递。
- 一致:不变对象多次比较结果不变。
- 对 null 返回 false。
- hashCode 一致:若相等,则
hashCode必须相同;反之不必。
实践:用 IDE 生成或 Java 16+ record(自动生成 equals/hashCode),并对值字段设置为 final。
反例(必须能讲清):
import java.util.*;
public class BadKey {
String name;
BadKey(String name) { this.name = name; }
@Override public boolean equals(Object o) {
return o instanceof BadKey b && Objects.equals(name, b.name);
}
@Override public int hashCode() { return Objects.hash(name); }
public static void main(String[] args) {
Map<BadKey, String> map = new HashMap<>();
BadKey k = new BadKey("a");
map.put(k, "v");
k.name = "b";
System.out.println(map.get(k)); // 可能 null:破坏了 hash 桶假设
}
}3. 知识点:List 选型#
| 实现 | get(i) | 尾部 add | 头插/中间插 | 备注 |
|---|---|---|---|---|
ArrayList | O(1) 摊销 | 均摊 O(1) | O(n) | 默认首选;内部数组扩容(通常 1.5×) |
LinkedList | O(n) | O(1) | O(1) 节点 | 随机访问差;多数业务不如 ArrayList |
学以致用:大量随机下标访问 → ArrayList;实现 LRU 常配合 LinkedHashMap 而非盲目 LinkedList。
4. 知识点:Map / Set#
| 类型 | 有序性 | 底层直觉 |
|---|---|---|
HashMap | 无序 | 数组 + 链表/树 |
LinkedHashMap | 插入顺序 | 额外双向链表 |
TreeMap | 键排序 | 红黑树,O(log n) |
HashMap.get 平均 O(1);TreeMap 需要 Comparable 或 Comparator。
5. 知识点:迭代与 fail-fast#
List<Integer> xs = new ArrayList<>(List.of(1, 2, 3));
for (Integer x : xs) {
if (x == 2) xs.remove(x); // ConcurrentModificationException
}正确姿势:用 Iterator.remove()、removeIf,或收集后再删。
6. 知识点:泛型与 PECS#
PECS:Producer Extends, Consumer Super。
- 只读出
T:List<? extends T> - 只写入
T:List<? super T>
学以致用:工具方法拷贝:
public static <T> void copy(List<? extends T> src, List<? super T> dst) {
for (T t : src) dst.add(t);
}7. 知识点:Stream 初用(注意副作用)#
Map<String, Long> freq = words.stream()
.collect(Collectors.groupingBy(w -> w, Collectors.counting()));避免在 forEach 里修改外部可变集合 unless 清楚并发语义。
8. Lab:购物车#
record LineItem(ProductId id, int qty, Money price) {}Map<ProductId, LineItem> lines;合并同款商品数量。- 覆盖
ProductId的 equals/hashCode(或 record)。
9. 自测清单#
- 解释
ArrayList扩容触发点与摊销意义。 - 为何「可变对象作 HashMap 键」危险?
- 写
static <T extends Comparable<T>> T max(List<T> xs)空表抛NoSuchElementException。 - fail-fast 与设计并发修改的关系?
下一篇:异常、NIO.2 与线程池入门。
相关阅读
基于内容相似度 + 标签/系列加权
- 2 分钟Java 从基础到精通(初级 · 高级 · 专家路线)
Java 基础 01:平台、JDK、字节码与可维护的最小项目骨架
写给初级工程师:JVM/JRE/JDK 分工、源码到字节码的路径、classpath 与模块的基础心智;用 Maven 或 Gradle 搭一个最小可测试骨架。
- Java
- JVM
- 基础
- 2 分钟Java 从基础到精通(初级 · 高级 · 专家路线)
Java 基础 03:异常体系、try-with-resources、IO/NIO.2 入门与并发初探
初级收尾篇:受检/非受检异常取舍、异常链与日志;现代 IO 用法;线程与 synchronized 的最小正确用法,为进阶 JUC 铺路。
- Java
- 异常
- IO
- 2 分钟Java 从基础到精通(初级 · 高级 · 专家路线)
Java 进阶 01:JUC、线程池、CompletableFuture 与异步组合实战
写给高级工程师:happens-before 直觉、Lock 与原子类、ExecutorService 参数含义、超时/取消/组合异步;常见踩坑与背压入门。
- Java
- 并发
- JUC
- 2 分钟Java 从基础到精通(初级 · 高级 · 专家路线)
Java 路线 00:从初级到专家的三层能力模型、学习顺序与全系列索引
面向初级、高级与资深/专家 Java 工程师的分层路线:每层该学什么、如何验收、与本系列各篇的对应关系;附统一练习模板与进阶节奏。
- Java
- 学习路线
- 方法论