从 TypeScript 到 Java(7):泛型与集合框架 — 类型安全的数据结构设计

系列导读:
TypeScript 的泛型让我们习惯了灵活的 Array<T>Map<K,V>
而 Java 的泛型与集合框架,则是以“类型安全 + 性能稳定”为核心设计的。

这篇文章将帮你理解:为什么 Java 的集合看似复杂,却能支撑大型系统的稳定性。

☕ 一句话理解

Java 的泛型体系,是一种 编译期类型保护机制
它不影响运行性能,却在编译阶段就能防止类型混乱。
配合集合框架(Collections Framework),
让数据的存储、遍历、查找、转换都有了标准化的方式。

🧱 泛型(Generics)的出现:从 Object 到类型安全

在早期 Java(1.5 之前),集合都是这样的:

List list = new ArrayList();
list.add("hello");
list.add(123); // 合法,但潜藏问题
String s = (String) list.get(1); // 运行时才报错

❌ 问题:类型不安全。任何对象都能放入集合。

从 Java 5 开始,引入泛型:

List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // 编译时报错

✅ 编译期就能检查类型,运行时更安全。

🧩 泛型的基本语法

class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

使用:

Box<Integer> box = new Box<>();
box.set(100);
System.out.println(box.get());

📘 关键点:

  • T 是“类型参数”,在使用时指定;
  • 泛型只在编译期存在,运行时会被擦除(称为“类型擦除”);
  • 因此不能直接写:new T()T.class

🧠 类型擦除(Type Erasure)

Java 的泛型不会在运行时保留类型信息:

List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();

System.out.println(a.getClass() == b.getClass()); // true

输出:

true

这是因为 JVM 不区分泛型类型,泛型仅是编译器的语法糖。
运行时,所有泛型信息都被擦除成 Object

与 TypeScript 对比:

  • TypeScript 的泛型是 编译时约束
  • 编译为 JS 后,同样会被擦除;
  • 因此两者在这点上非常相似。

📦 Java 集合框架(Collections Framework)

Java 提供了一整套接口 + 实现类结构:

Collection
 ├── List
 │    ├── ArrayList
 │    ├── LinkedList
 │    └── Vector
 ├── Set
 │    ├── HashSet
 │    ├── TreeSet
 │    └── LinkedHashSet
 └── Queue
      ├── PriorityQueue
      └── ArrayDeque

Map
 ├── HashMap
 ├── TreeMap
 └── LinkedHashMap

🎫 List:有序集合(类似 TypeScript Array)

List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("cherry");

for (String f : fruits) {
    System.out.println(f);
}

输出:

apple
banana
cherry

特点:

  • 保持插入顺序;

  • 可通过索引访问;

  • 常见实现:

    • ArrayList:基于动态数组;
    • LinkedList:基于双向链表。

🧩 Set:不重复集合(类似 TypeScript Set)

Set<String> names = new HashSet<>();
names.add("Tom");
names.add("Jerry");
names.add("Tom"); // 自动去重

System.out.println(names);

输出:

[Jerry, Tom]

特点:

  • 无序;
  • 不允许重复;
  • 底层使用哈希结构;
  • 若要排序,可用 TreeSet

⚙️ Map:键值对(类似 TypeScript Map)

Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 20);
ages.put("Bob", 25);

System.out.println(ages.get("Bob"));

输出:

25

常见实现:

类名 特点
HashMap 无序,性能高
LinkedHashMap 保留插入顺序
TreeMap 按键排序

💡 Java 没有字面量对象 {},一切键值结构都来自 Map

🧭 遍历方式汇总

1. For-each

for (String f : fruits) {
    System.out.println(f);
}

2. Iterator(手动控制)

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

3. Stream(函数式风格)

fruits.stream()
      .filter(f -> f.startsWith("b"))
      .forEach(System.out::println);

输出:

banana

Stream 是 Java 8 引入的“声明式数据处理”方案,
类似 TypeScript 的 array.filter().map().forEach()

🧮 TypeScript 对比表

功能 TypeScript Java
动态数组 Array<T> ArrayList<T>
键值集合 Map<K,V> HashMap<K,V>
去重集合 Set<T> HashSet<T>
过滤映射 .filter() .map() .stream().filter().map()
类型检查 编译期(宽松) 编译期(严格)
执行期类型 保留 被擦除

⚖️ 工程哲学

  • TypeScript 的集合更轻量、灵活;
  • Java 的集合体系更规范、强类型、可扩展;
  • 泛型与集合结合后,使大型项目的结构稳定且可维护;
  • Stream API 让 Java 逐渐具备了“函数式”的表达力。

总结:

TypeScript 更像“前端的便利贴”;
Java 集合则是“后端的地基钢筋”。

🚀 实战建议

  1. 使用 Map<String, List<String>> 构建一个 “学生-课程” 对照表;

  2. 使用 stream() 过滤出学习 “Java” 的学生;

  3. 尝试封装成一个泛型方法:

    public static <T> List<T> filter(List<T> list, Predicate<T> predicate)
    

    模仿 TypeScript 的 array.filter()

🔮 下一篇预告

第 8 篇:《异常处理与错误机制 —— 让程序稳定地失败》

我们将深入:

  • try...catch...finally
  • throwsthrow
  • 自定义异常类
  • 以及 Java 异常体系的设计哲学
    (Checked vs Unchecked)
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐