|
|
@@ -0,0 +1,557 @@
|
|
|
+# 2026-07-31 Iterator 迭代器与 Map 集合专项练习(课堂练习用)
|
|
|
+
|
|
|
+> **说明**:以下习题围绕今日授课核心内容——**Iterator 迭代器** 与 **Map 集合(双列集合)** 展开,涵盖 for 循环与迭代器遍历对比、Map 键值对基本操作、Map 两种遍历方式(keySet+get / entrySet)、自定义类型作为 HashMap 的 Key(重写 hashCode/equals)、HashMap 底层原理应用等知识点。
|
|
|
+>
|
|
|
+> 题目分为 **基础练习**(迭代器与 Map 基本操作,3 题)和 **进阶挑战**(Map 综合应用,2 题)。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第一部分:基础练习
|
|
|
+
|
|
|
+> 以下练习围绕今日授课核心内容展开,由浅入深。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 1:Iterator 迭代器遍历(for 循环 vs 迭代器)
|
|
|
+
|
|
|
+**难度**:⭐
|
|
|
+**知识点**:`Collection` 继承 `Iterable`、`iterator()` / `hasNext()` / `next()`、for+get 遍历与迭代器遍历对比
|
|
|
+
|
|
|
+**场景描述**:所有单列集合都继承自 `Iterable` 接口,都具备迭代能力。通过迭代器,可以**统一**地遍历 ArrayList 和 LinkedList,且与集合的内部结构无关。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+在 `IteratorDemo` 类的 `main` 方法中,完成以下操作:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.LinkedList;
|
|
|
+
|
|
|
+public class IteratorDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建一个 ArrayList<String>,添加 5 个元素:"A", "B", "C", "D", "E"
|
|
|
+
|
|
|
+ // 2. 方式一:使用 for + size() + get(i) 遍历 ArrayList,并输出
|
|
|
+ // 提示:for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }
|
|
|
+
|
|
|
+ // 3. 方式二:通过 list.iterator() 获取迭代器对象
|
|
|
+ // 使用 while + hasNext() + next() 遍历,并输出
|
|
|
+ // 提示:Iterator<String> iterator = list.iterator();
|
|
|
+ // while (iterator.hasNext()) { String element = iterator.next(); ... }
|
|
|
+
|
|
|
+ // 4. 再创建一个 LinkedList<String>,添加同样的 5 个元素
|
|
|
+
|
|
|
+ // 5. 使用迭代器遍历 LinkedList(同样的模板代码)
|
|
|
+ // 观察:LinkedList 没有 get(i) 高效随机访问,迭代器是它的最佳遍历方式
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+========== for 循环遍历 ArrayList ==========
|
|
|
+A
|
|
|
+B
|
|
|
+C
|
|
|
+D
|
|
|
+E
|
|
|
+========== 迭代器遍历 ArrayList ==========
|
|
|
+A
|
|
|
+B
|
|
|
+C
|
|
|
+D
|
|
|
+E
|
|
|
+========== 迭代器遍历 LinkedList ==========
|
|
|
+A
|
|
|
+B
|
|
|
+C
|
|
|
+D
|
|
|
+E
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**:
|
|
|
+1. `hasNext()` 和 `next()` 分别做什么?`next()` 调用后迭代器指针会发生什么变化?
|
|
|
+2. 为什么 LinkedList 用 `for + get(i)` 遍历效率低,而迭代器遍历效率高?(提示:`get(i)` 每次从头遍历,O(n²))
|
|
|
+3. 增强 for(foreach)的底层实现是什么?它和迭代器有什么关系?
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 2:Map 集合基本操作(HashMap 键值对)
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:`HashMap` 创建、`put()` / `get()` / `containsKey()` / `containsValue()` / `size()` / `isEmpty()` / `remove()` / `clear()`、键不可重复
|
|
|
+
|
|
|
+**场景描述**:Map 是**双列集合**,以「键值对」为单位存储。键(Key)不可重复,值(Value)可以重复。模拟一个"学生信息登记表"——用学号作为键,姓名作为值。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+在 `MapDemo` 类的 `main` 方法中,完成以下操作:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+public class MapDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建一个 HashMap<String, String>,学号作为 Key,姓名作为 Value
|
|
|
+ // 提示:Map<String, String> map = new HashMap<>();
|
|
|
+
|
|
|
+ // 2. 登记 4 名学生:x001-张三, x002-李四, x003-王五, x004-赵六
|
|
|
+ // 使用 put() 方法
|
|
|
+
|
|
|
+ // 3. 输出集合大小(size()):"学生人数:4"
|
|
|
+
|
|
|
+ // 4. 根据学号查询姓名(get("x002")):"学号 x002 的学生:李四"
|
|
|
+
|
|
|
+ // 5. 判断是否包含某个键(containsKey("x001")):true
|
|
|
+ // 判断是否包含某个值(containsValue("王五")):true
|
|
|
+ // 判断是否包含不存在的键(containsKey("x999")):false
|
|
|
+
|
|
|
+ // 6. 重复键测试:put("x001", "张三丰")
|
|
|
+ // 再次输出"学号 x001 的学生":观察发生了什么?
|
|
|
+ // —— 提示:同一个 Key 再次 put 会【覆盖】旧值,键不可重复
|
|
|
+
|
|
|
+ // 7. 移除一个学生(remove("x004")),输出移除后的集合大小
|
|
|
+
|
|
|
+ // 8. 判断集合是否为空(isEmpty()):false
|
|
|
+
|
|
|
+ // 9. 清空集合(clear()),再次判断 isEmpty():true
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+学生人数:4
|
|
|
+学号 x002 的学生:李四
|
|
|
+包含键 x001:true
|
|
|
+包含值 王五:true
|
|
|
+包含键 x999:false
|
|
|
+重复键 put 后,学号 x001 的学生:张三丰 ← 旧值"张三"被覆盖
|
|
|
+移除 x004 后,学生人数:3
|
|
|
+集合为空?false
|
|
|
+清空后,集合为空?true
|
|
|
+```
|
|
|
+
|
|
|
+**思考题**:
|
|
|
+1. 为什么 Map 的 Key 不能重复?重复 put 会怎样?(提示:覆盖旧值)
|
|
|
+2. `containsKey()` 和 `containsValue()` 的底层实现有什么区别?(提示:containsKey 走哈希定位,containsValue 需遍历)
|
|
|
+3. 为什么 `keySet()` 返回的是 Set 而不是 List?(提示:键不可重复)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 练习 3:Map 集合的遍历(keySet + get / entrySet)
|
|
|
+
|
|
|
+**难度**:⭐⭐
|
|
|
+**知识点**:`keySet()` + `get(key)`(方式一)、`entrySet()` + `getKey()` / `getValue()`(方式二)、`Map.Entry` 内部类型
|
|
|
+
|
|
|
+**场景描述**:Map 没有索引,不能像 List 那样用 `for + get(i)` 直接遍历。有两种标准遍历方式:**keySet + get**(先取键再反查值)和 **entrySet**(直接取键值对)。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+在 `MapTraversalDemo` 类的 `main` 方法中,完成以下操作:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+public class MapTraversalDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 创建一个 HashMap<String, Double>,存储商品价格
|
|
|
+ // "苹果"-3.5, "香蕉"-2.0, "西瓜"-6.8, "葡萄"-12.5
|
|
|
+
|
|
|
+ // 2. 方式一:keySet() + foreach + get(key) 遍历
|
|
|
+ // 输出格式:"key:苹果,value:3.5"
|
|
|
+ // 步骤:
|
|
|
+ // a. Set<String> keys = map.keySet();
|
|
|
+ // b. for (String key : keys) { Double value = map.get(key); ... }
|
|
|
+
|
|
|
+ // 3. 方式二:entrySet() + foreach + getKey()/getValue() 遍历
|
|
|
+ // 输出格式:"key:苹果,value:3.5"
|
|
|
+ // 步骤:
|
|
|
+ // a. Set<Map.Entry<String, Double>> entries = map.entrySet();
|
|
|
+ // b. for (Map.Entry<String, Double> me : entries) {
|
|
|
+ // String key = me.getKey(); Double value = me.getValue(); ... }
|
|
|
+
|
|
|
+ // 4. 统计商品总价(遍历两种方式均可),输出:"商品总价:24.8"
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**(顺序因 HashMap 无序而不定):
|
|
|
+```
|
|
|
+========== 方式一:keySet + get ==========
|
|
|
+key:香蕉,value:2.0
|
|
|
+key:苹果,value:3.5
|
|
|
+key:葡萄,value:12.5
|
|
|
+key:西瓜,value:6.8
|
|
|
+========== 方式二:entrySet ==========
|
|
|
+key:香蕉,value:2.0
|
|
|
+key:苹果,value:3.5
|
|
|
+key:葡萄,value:12.5
|
|
|
+key:西瓜,value:6.8
|
|
|
+========== 总价统计 ==========
|
|
|
+商品总价:24.8
|
|
|
+```
|
|
|
+
|
|
|
+**关键对比**:
|
|
|
+
|
|
|
+| 遍历方式 | 步骤 | 特点 |
|
|
|
+|----------|------|------|
|
|
|
+| 方式一:`keySet()` + `get(key)` | 取键集合 → 遍历键 → 反查值 | **间接**:每次 get 都是一次哈希查找 |
|
|
|
+| 方式二:`entrySet()` | 取键值对集合 → 遍历 → getKey/getValue | **直接**:一次拿到键和值,效率更高 |
|
|
|
+
|
|
|
+**思考题**:
|
|
|
+1. 方式二为什么比方式一高效?(提示:方式一每次 `get(key)` 都要重新哈希查找)
|
|
|
+2. `Map.Entry<String, Double>` 是什么类型?它和 Map 是什么关系?(提示:Map 的内部类型)
|
|
|
+3. 两次遍历的输出顺序是否与 put 的顺序一致?为什么?(提示:HashMap 无序)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 第二部分:进阶挑战
|
|
|
+
|
|
|
+> 以下练习综合运用 Map 集合、哈希表原理、自定义类型等知识。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 进阶 1:自定义类作为 HashMap 的 Key(重写 hashCode / equals)
|
|
|
+
|
|
|
+**难度**:⭐⭐⭐
|
|
|
+**知识点**:HashMap 依赖 `hashCode()` 和 `equals()` 保证键唯一、自定义类型作为 Key、哈希表结构(数组+链表)
|
|
|
+
|
|
|
+**场景描述**:HashMap 的键 Key 的唯一性依赖 `hashCode()` 和 `equals()` 两个方法。**自定义类型**想存入 HashMap 的 Key 中,**必须重写**这两个方法,否则两个属性相同的对象会被当作不同的键。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+定义 `Student` 类(学号 + 姓名),作为 HashMap 的 Key,并重写 `hashCode()` 和 `equals()`:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+public class Student {
|
|
|
+ private String id; // 学号
|
|
|
+ private String name; // 姓名
|
|
|
+
|
|
|
+ public Student(String id, String name) {
|
|
|
+ this.id = id;
|
|
|
+ this.name = name;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: 补全 getter / setter 方法
|
|
|
+
|
|
|
+ // TODO: 重写 hashCode()
|
|
|
+ // 规则:属性值完全相同时,必须返回相同的哈希值
|
|
|
+ // 提示:return Objects.hash(id, name);
|
|
|
+
|
|
|
+ // TODO: 重写 equals(Object o)
|
|
|
+ // 规则:id 和 name 都相同时返回 true
|
|
|
+ // 提示:使用 Objects.equals(id, student.id) 判断
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return "Student{id='" + id + "', name='" + name + "'}";
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+编写 `StudentKeyTest` 测试类:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+public class StudentKeyTest {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ Map<Student, String> map = new HashMap<>();
|
|
|
+
|
|
|
+ // 1. 创建两个属性完全相同的 Student 对象
|
|
|
+ Student s1 = new Student("x001", "张三");
|
|
|
+ Student s2 = new Student("x001", "张三");
|
|
|
+
|
|
|
+ // 2. 判断 s1 和 s2 是否相等(不重写 equals 时是 false)
|
|
|
+ System.out.println("s1.equals(s2):" + s1.equals(s2)); // 期望 true
|
|
|
+
|
|
|
+ // 3. 判断 s1 和 s2 的哈希值是否相同
|
|
|
+ System.out.println("s1.hashCode():" + s1.hashCode());
|
|
|
+ System.out.println("s2.hashCode():" + s2.hashCode()); // 期望相同
|
|
|
+
|
|
|
+ // 4. 分别用 s1、s2 作为 Key 存入
|
|
|
+ map.put(s1, "学员A");
|
|
|
+ map.put(s2, "学员B");
|
|
|
+
|
|
|
+ // 5. 思考:如果不重写 hashCode/equals,size 是几?重写后 size 是几?
|
|
|
+ System.out.println("map 大小:" + map.size()); // 期望 1(s1、s2 视为同一个键)
|
|
|
+
|
|
|
+ // 6. 用 get 取出:new 一个属性相同的对象 s3 去查找
|
|
|
+ Student s3 = new Student("x001", "张三");
|
|
|
+ System.out.println("用 s3 查找:" + map.get(s3)); // 期望 "学员B"
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+s1.equals(s2):true
|
|
|
+s1.hashCode():1416729608 ← 具体数值不重要,关键是 s2 相同
|
|
|
+s2.hashCode():1416729608
|
|
|
+map 大小:1
|
|
|
+用 s3 查找:学员B
|
|
|
+```
|
|
|
+
|
|
|
+**原理分析(必答)**:
|
|
|
+1. 如果不重写 `hashCode()` 和 `equals()`,`map.size()` 会是多少?为什么?
|
|
|
+ - 答:会是 2。因为默认 `hashCode()` 基于对象地址,两个 new 出来的对象地址不同 → 哈希值不同 → 存到不同位置;`equals()` 默认用 `==` 比较地址,也判定不相等
|
|
|
+2. HashMap 在存 Key 时,先调用哪个方法、后调用哪个方法?
|
|
|
+ - 答:先计算 `hashCode()` 定位存储位置(数组下标),若位置已有元素,再用 `equals()` 比较是否相同——相同则不存(覆盖),不同则挂到链表上
|
|
|
+3. 为什么重写 `equals()` 时必须同步重写 `hashCode()`?(提示:哈希表的一致性契约)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 进阶 2:Map 综合应用 —— 单词词频统计器
|
|
|
+
|
|
|
+**难度**:⭐⭐⭐⭐
|
|
|
+**知识点**:Map 键值对建模、`put` / `get` / `containsKey` / 遍历、HashMap 统计思想、综合应用
|
|
|
+
|
|
|
+**场景描述**:词频统计是 Map 最典型的应用场景——**单词作为 Key,出现次数作为 Value**。遍历文本时:单词没出现过就 `put(word, 1)`,出现过就 `put(word, 次数+1)`。
|
|
|
+
|
|
|
+**题目要求**:
|
|
|
+
|
|
|
+编写 `WordCountDemo` 类,完成单词词频统计:
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Scanner;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+public class WordCountDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ // 1. 定义一个字符串数组,模拟一段文本中的单词(含重复)
|
|
|
+ // "apple", "banana", "apple", "orange", "banana", "apple", "grape"
|
|
|
+
|
|
|
+ // 2. 创建一个 HashMap<String, Integer>,单词 → 出现次数
|
|
|
+
|
|
|
+ // 3. 遍历单词数组,统计每个单词出现的次数
|
|
|
+ // 核心逻辑:
|
|
|
+ // for (String word : words) {
|
|
|
+ // if (map.containsKey(word)) {
|
|
|
+ // map.put(word, map.get(word) + 1); // 已出现过,次数 +1
|
|
|
+ // } else {
|
|
|
+ // map.put(word, 1); // 第一次出现
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 4. 使用 entrySet() 遍历输出统计结果
|
|
|
+ // 输出格式:"单词:apple,出现次数:3"
|
|
|
+
|
|
|
+ // 5. 进阶:找出出现次数最多的单词
|
|
|
+ // 提示:遍历 entrySet,用变量记录 maxCount 和对应单词
|
|
|
+
|
|
|
+ // 6. 拓展:使用 Scanner 从键盘输入一句话(用空格分隔),统计每个单词的出现次数
|
|
|
+ // 提示:String[] inputWords = scanner.nextLine().split(" ");
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**预期输出**:
|
|
|
+```
|
|
|
+========== 词频统计结果 ==========
|
|
|
+单词:grape,出现次数:1
|
|
|
+单词:banana,出现次数:2
|
|
|
+单词:orange,出现次数:1
|
|
|
+单词:apple,出现次数:3
|
|
|
+出现次数最多的单词:apple(3 次)
|
|
|
+```
|
|
|
+
|
|
|
+**测试用例**(Scanner 输入部分):
|
|
|
+```
|
|
|
+请输入一段英文(单词用空格分隔):I love java and I love coding
|
|
|
+I:2
|
|
|
+love:2
|
|
|
+java:1
|
|
|
+and:1
|
|
|
+coding:1
|
|
|
+```
|
|
|
+
|
|
|
+**拓展思考**:
|
|
|
+1. 如果把统计逻辑换成 `map.put(word, map.getOrDefault(word, 0) + 1)`,能简化代码吗?`getOrDefault` 是什么意思?
|
|
|
+2. 如果想按出现次数从高到低排序输出,可以怎么做?(提示:把 entrySet 转为 List 后用 Comparator 排序)
|
|
|
+3. 如果文本有 100 万个单词,HashMap 相比数组统计的优势在哪里?(提示:键是字符串不是数字索引,HashMap 用哈希直接定位)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 参考答案要点
|
|
|
+
|
|
|
+## 练习 1 参考
|
|
|
+
|
|
|
+```java
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.LinkedList;
|
|
|
+
|
|
|
+public class IteratorDemo {
|
|
|
+ public static void main(String[] args) {
|
|
|
+ ArrayList<String> list = new ArrayList<>();
|
|
|
+ list.add("A");
|
|
|
+ list.add("B");
|
|
|
+ list.add("C");
|
|
|
+ list.add("D");
|
|
|
+ list.add("E");
|
|
|
+
|
|
|
+ System.out.println("========== for 循环遍历 ArrayList ==========");
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
+ System.out.println(list.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("========== 迭代器遍历 ArrayList ==========");
|
|
|
+ Iterator<String> iterator = list.iterator();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ System.out.println(iterator.next());
|
|
|
+ }
|
|
|
+
|
|
|
+ LinkedList<String> linkedList = new LinkedList<>();
|
|
|
+ linkedList.add("A");
|
|
|
+ linkedList.add("B");
|
|
|
+ linkedList.add("C");
|
|
|
+ linkedList.add("D");
|
|
|
+ linkedList.add("E");
|
|
|
+
|
|
|
+ System.out.println("========== 迭代器遍历 LinkedList ==========");
|
|
|
+ Iterator<String> iterator1 = linkedList.iterator();
|
|
|
+ while (iterator1.hasNext()) {
|
|
|
+ System.out.println(iterator1.next());
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 练习 2 参考
|
|
|
+
|
|
|
+```java
|
|
|
+Map<String, String> map = new HashMap<>();
|
|
|
+map.put("x001", "张三");
|
|
|
+map.put("x002", "李四");
|
|
|
+map.put("x003", "王五");
|
|
|
+map.put("x004", "赵六");
|
|
|
+
|
|
|
+System.out.println("学生人数:" + map.size());
|
|
|
+System.out.println("学号 x002 的学生:" + map.get("x002"));
|
|
|
+
|
|
|
+System.out.println("包含键 x001:" + map.containsKey("x001"));
|
|
|
+System.out.println("包含值 王五:" + map.containsValue("王五"));
|
|
|
+System.out.println("包含键 x999:" + map.containsKey("x999"));
|
|
|
+
|
|
|
+map.put("x001", "张三丰"); // 键重复 → 覆盖旧值
|
|
|
+System.out.println("重复键 put 后,学号 x001 的学生:" + map.get("x001"));
|
|
|
+
|
|
|
+map.remove("x004");
|
|
|
+System.out.println("移除 x004 后,学生人数:" + map.size());
|
|
|
+
|
|
|
+System.out.println("集合为空?" + map.isEmpty());
|
|
|
+map.clear();
|
|
|
+System.out.println("清空后,集合为空?" + map.isEmpty());
|
|
|
+```
|
|
|
+
|
|
|
+## 练习 3 参考
|
|
|
+
|
|
|
+```java
|
|
|
+Map<String, Double> map = new HashMap<>();
|
|
|
+map.put("苹果", 3.5);
|
|
|
+map.put("香蕉", 2.0);
|
|
|
+map.put("西瓜", 6.8);
|
|
|
+map.put("葡萄", 12.5);
|
|
|
+
|
|
|
+System.out.println("========== 方式一:keySet + get ==========");
|
|
|
+Set<String> keys = map.keySet();
|
|
|
+for (String key : keys) {
|
|
|
+ Double value = map.get(key);
|
|
|
+ System.out.println("key:" + key + ",value:" + value);
|
|
|
+}
|
|
|
+
|
|
|
+System.out.println("========== 方式二:entrySet ==========");
|
|
|
+Set<Map.Entry<String, Double>> entries = map.entrySet();
|
|
|
+for (Map.Entry<String, Double> me : entries) {
|
|
|
+ System.out.println("key:" + me.getKey() + ",value:" + me.getValue());
|
|
|
+}
|
|
|
+
|
|
|
+// 总价统计(方式二)
|
|
|
+double total = 0;
|
|
|
+for (Map.Entry<String, Double> me : map.entrySet()) {
|
|
|
+ total += me.getValue();
|
|
|
+}
|
|
|
+System.out.println("========== 总价统计 ==========");
|
|
|
+System.out.println("商品总价:" + total);
|
|
|
+```
|
|
|
+
|
|
|
+## 进阶 1 参考
|
|
|
+
|
|
|
+```java
|
|
|
+// Student 类:重写 hashCode / equals
|
|
|
+@Override
|
|
|
+public int hashCode() {
|
|
|
+ return Objects.hash(id, name);
|
|
|
+}
|
|
|
+
|
|
|
+@Override
|
|
|
+public 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) && Objects.equals(name, student.name);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 进阶 2 参考(核心逻辑)
|
|
|
+
|
|
|
+```java
|
|
|
+String[] words = {"apple", "banana", "apple", "orange", "banana", "apple", "grape"};
|
|
|
+Map<String, Integer> map = new HashMap<>();
|
|
|
+
|
|
|
+for (String word : words) {
|
|
|
+ if (map.containsKey(word)) {
|
|
|
+ map.put(word, map.get(word) + 1);
|
|
|
+ } else {
|
|
|
+ map.put(word, 1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 使用 getOrDefault 简化
|
|
|
+for (String word : words) {
|
|
|
+ map.put(word, map.getOrDefault(word, 0) + 1);
|
|
|
+}
|
|
|
+
|
|
|
+// 找出出现次数最多的单词
|
|
|
+String maxWord = null;
|
|
|
+int maxCount = 0;
|
|
|
+for (Map.Entry<String, Integer> me : map.entrySet()) {
|
|
|
+ if (me.getValue() > maxCount) {
|
|
|
+ maxCount = me.getValue();
|
|
|
+ maxWord = me.getKey();
|
|
|
+ }
|
|
|
+}
|
|
|
+System.out.println("出现次数最多的单词:" + maxWord + "(" + maxCount + " 次)");
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+# 涵盖知识点总览
|
|
|
+
|
|
|
+| 知识点 | 对应练习 | 说明 |
|
|
|
+|--------|---------|------|
|
|
|
+| Iterator 迭代器遍历 | 练习 1 | `iterator()` / `hasNext()` / `next()`、Iterable 接口 |
|
|
|
+| for + get 遍历 vs 迭代器 | 练习 1 | 索引遍历只适合 List,迭代器通用所有单列集合 |
|
|
|
+| HashMap 创建与键值对操作 | 练习 2 | `put` / `get` / `containsKey` / `containsValue` / `size` / `isEmpty` / `remove` / `clear` |
|
|
|
+| 键不可重复特性 | 练习 2 | 重复 put 覆盖旧值 |
|
|
|
+| Map 遍历方式一 | 练习 3 | `keySet()` + foreach + `get(key)`(间接遍历) |
|
|
|
+| Map 遍历方式二 | 练习 3 | `entrySet()` + foreach + `getKey()` / `getValue()`(直接遍历,效率更高) |
|
|
|
+| `Map.Entry` 内部类型 | 练习 3 | 一个 Entry 代表一个键值对 |
|
|
|
+| HashMap 键唯一性契约 | 进阶 1 | 依赖 `hashCode()` 定位 + `equals()` 比较,二者必须同时重写 |
|
|
|
+| 自定义类型作为 Key | 进阶 1 | 属性相同 → 哈希值相同 → equals 相同 → 视为同一键 |
|
|
|
+| 哈希表结构(数组+链表/红黑树) | 进阶 1 | 树化条件:数组长度>64 且链表节点>8;退化条件:节点<6 |
|
|
|
+| Map 综合统计应用 | 进阶 2 | 单词词频统计、`getOrDefault`、找最大值 |
|
|
|
+| HashMap 无序性 | 练习 2、3 | 存储顺序 ≠ 添加顺序(哈希定位) |
|
|
|
+
|