☕ 第七章 集合
📝 一句话概括

集合 = 会自动伸缩、自带工具箱的对象容器——数组的三大痛点(定长、删插麻烦、没工具)它全包了。本章先看家族地图,后面逐站细逛。

7.1 集合概述

1. 为什么需要集合?——数组的三大痛点

2.7 学的数组很能干,但有三个先天短板:

int[] arr = new int[3];       // ① 长度定死:永远 3 格
arr[0] = 42;
// 来第 4 个数?只能 new 个新数组整体搬家
// 删 arr[1]?自己写循环把后面的整体前挪
// 查 42 在不在?自己写 for 循环挨个比
痛点 数组的表现 集合的解法
长度定死 new int[3] 永远 3 格,扩容要自己搬家 add() 自动伸、remove() 自动缩
删插麻烦 删中间一个,后面全体挪窝,手写循环 remove(下标) 一行,挪窝它来干
没有工具 查找、去重全自己写 contains 查、size 数,Set 自动去重

类比:数组是"定长的格子间",集合是"能扩建的宿舍楼"——住几个人随时改,还配了物业(现成方法)。

2. 集合是什么?

集合(Collection)就是 Java 官方提供的「存对象的容器」——底层也是数组或链表,但把脏活累活全封装好了,你只管调方法。

和数组最大的差别一句话:数组管"存",集合管"存 + 活"。

3. 集合家族地图(本节先认站名)

集合
├── Collection 体系(一次存一个)
│   ├── List:有序、可重复 —— 像排队,有编号
│   │   └── ArrayList ★ 本章主站(用得最多)
│   └── Set:不重复 —— 像点名册
│       └── HashSet(去重看 hashCode,4.5 的钩子在这收网)
└── Map 体系(成对存:键 → 值)
    └── HashMap —— 像字典,查字用页码
体系 特点 类比 代表
List 有序、可重复、有下标 排队 ArrayList
Set 不重复(可能无序) 点名册 HashSet
Map 键值成对,键唯一 字典 HashMap
💡 本节目标:会认地图

三大站点的细节后面每节细讲。现在只需要记住:要"排队存东西"找 List/ArrayList;要"不重复"找 Set;要"按名字查东西"找 Map。

4. 先上手:第一个 ArrayList 程序

import java.util.ArrayList;

public class Demo01 {
    public static void main(String[] args) {
        // 创建一个只能装字符串的集合
        ArrayList<String> list = new ArrayList<>();

        // ① 加
        list.add("波仔");
        list.add("墨池");
        list.add("AI");
        System.out.println(list);            // [波仔, 墨池, AI] —— 直接打印!

        // ② 查
        System.out.println(list.get(0));     // 波仔 —— 下标从 0 开始,和数组一样
        System.out.println(list.size());     // 3
        System.out.println(list.contains("墨池"));  // true

        // ③ 删
        list.remove("AI");
        System.out.println(list.size());     // 2 —— 长度自己缩了
    }
}

五个最常用的方法,先混个脸熟(后面节细讲):

方法 作用 对应数组的……
add(e) 加到末尾 arr[下标] = e(但不用管下标)
get(i) 取第 i 个 arr[i]
size() 数元素个数 arr.length(注意是方法带括号)
remove(i 或 e) 删除 自己写循环挪窝
contains(e) 查在不在 自己写 for 循环

5. 泛型 <String>:先会认

ArrayList<String> 尖括号里的类型叫泛型——意思是"这个集合只装这一类东西":

ArrayList<String> names = new ArrayList<>();   // 只装字符串
names.add("波仔");       // ✓
names.add(42);           // ✗ 编译报错——类型不对,当场拦下

两个规矩:

  1. 泛型只能写引用类型,不能写基本类型:ArrayList<int> ✗ → ArrayList<Integer> ✓
  2. 右边的 <> 可以空着(钻石写法),类型自动推断
💡 泛型是编译器的安检

不写泛型的集合什么都能塞(默认装 Object),取出来还得强转(4.4 的 ClassCastException 风险区)。写了泛型,塞错类型编译就报错——把错误拦在运行前,这是 5 章以来一贯的思想:能编译期发现的问题,绝不留到运行期。

6. 一条铁规矩:集合只能装对象

list.add(42) 明明塞的是数字,为什么能进 ArrayList<Integer>?

自动装箱(6.1 的第一道门在此上岗):42 被悄悄包装成 Integer 对象再入箱。

ArrayList<Integer> nums = new ArrayList<>();
nums.add(42);                              // 42 → Integer.valueOf(42) 自动装箱
int x = nums.get(0);                       // Integer → 42 自动拆箱
System.out.println(nums.get(0) + 1);       // 43 —— 拆箱后能算术
⚠️ 由此继承的坑

6.1 讲过的两个坑在集合里照样成立:缓存池(两个集合里的 127 用 == 是 true,128 是 false——比内容用 equals);null 拆箱(集合里存了 null,取出赋给 int 直接 NPE)。

7. 集合 vs 数组:什么时候用谁?

数组 集合
长度 固定 自动伸缩
能装什么 基本类型 + 对象都行 只能对象
效率 存取最快的裸容器 略有封装开销(可忽略)
工具 无,自己写 add/remove/contains 全套
使用场景 元素个数固定(如扑克 52 张) 个数不定(如用户列表)

经验法则:拿不准就用集合——效率差距在日常场景里感觉不到,灵活性的差距立竿见影。

8. 常见误区

⚠️ 误区 1:`ArrayList` 这样写泛型

编译报错。泛型只能写引用类型,基本类型要用包装类:int→Integer、double→Double(6.1 的 8 大对照表派上用场)。

⚠️ 误区 2:用 `list.length` 取长度

没有 length 属性。数组用 arr.length(属性),集合用 list.size()(方法带括号)——新手最常混的一对。

⚠️ 误区 3:以为集合里存的是"值"

集合存的是对象的引用(地址)——和数组存对象一个道理(3.3 方法传对象)。改了对象,集合里"跟着变",不是复制了多份。

⚠️ 误区 4:remove(1) 以为删的是"值为 1 的元素"

remove(1) 删的是下标 1 的元素;想删"值为 1 的 Integer"要 remove(Integer.valueOf(1))。Integer 重载的这个歧义后面细节节再收拾,先记住有这坑。

⚠️ 误区 5:打印集合写双重循环

System.out.println(list) 直接就能打印出 [a, b, c]——集合重写了 toString(4.5 学的知识),不用自己遍历着拼。

9. 动手试一试(30 秒实验)

新建 Demo01(记得 import java.util.ArrayList;):

实验 1:add 三个名字后直接 System.out.println(list),看 [波仔, 墨池, AI] 格式——对比 4.5 里 类名@乱码,体会 toString 重写。

实验 2:ArrayList<Integer> nums = new ArrayList<>(); 后 nums.add(42),打印——42 是 int,怎么进的 Integer 集合?(自动装箱)

实验 3:写 ArrayList<int> bad = new ArrayList<>();——亲眼看编译报错,把 int 改成 Integer 后通过。

实验 4:remove 中间一个元素再打印——看后面元素自动前移,对比 2.7 手写挪窝的循环。

10. 自测一下

❓ Q1:数组和集合最核心的差别一句话?各举一个适用场景。

数组长度固定、集合自动伸缩。个数固定(扑克 52 张)用数组;个数不定(购物车商品)用集合。集合还自带工具方法。

❓ Q2:`ArrayList` 尖括号里的是什么?不写会怎样?

泛型——声明只装 String。不写则默认装 Object,什么都能塞但取出要强转,容易 ClassCastException;写了泛型塞错编译就报错。

❓ Q3:为什么 `list.add(42)` 能塞进 `ArrayList`?这个过程叫什么?

自动装箱(6.1):int 42 被 Integer.valueOf(42) 包装成对象再入箱。取出时自动拆箱。集合只能装对象,基本类型全靠这道门。

❓ Q4:取长度,数组和集合分别怎么写?

数组 arr.length(属性,不带括号);集合 list.size()(方法,带括号)。

❓ Q5(毕业题):List、Set、Map 各用一句话概括特点,并各配一个生活类比。

List 有序可重复有下标——排队;Set 不重复——点名册;Map 键值对键唯一——字典。选型口诀:排队用 List、去重用 Set、按名查用 Map。

11. 下一步学什么


相关笔记