CodeGym /课程 /JAVA 25 SELF /Map:HashMap 和 TreeMap,键与值

Map:HashMap 和 TreeMap,键与值

JAVA 25 SELF
第 26 级 , 课程 2
可用

1. 引言

在现实生活中,我们常常需要把某个唯一的“键”对应到某个“值”。电话簿中存的是姓名与电话号码,词典把单词与其翻译关联,在成绩表中每个学生都有自己的名字和对应的分数。

在 Java 中,这类任务可以用 Map 接口完成。它是一种存储“键—值”对(key-value pair)的集合。

Map 的关键性质:

  • 每个键都是唯一的(不允许重复)。
  • 一个键只能对应一个值。
  • 值可以重复。

打个比方:列表(List)就像食堂排队(每个人都有自己的位置,可以按下标访问);而映射(Map)则像带编号的储物柜:每个柜子都有一个编号(键),里面放着某样东西(值)。

Map 接口:基础操作

接口 Map 声明了处理键值对的基本方法:

方法 说明
put(K key, V value)
按键添加/替换值
get(K key)
按键获取值
remove(K key)
按键删除键值对
containsKey(K key)
检查是否存在该键
containsValue(V value)
检查是否存在该值
size()
映射中的键值对数量
isEmpty()
检查 Map 是否为空
clear()
删除所有键值对

类型 KV 是泛型参数:K(Key)为键的类型,V(Value)为值的类型。

3. HashMap 类:按键快速访问

什么是 HashMap?

HashMapMap 接口最常用的实现,它能根据键快速访问对应的值。

重要:HashMap 不保证元素的存储顺序!即使你按某个顺序添加键,遍历时顺序也可能不同。

如何创建 HashMap?

import java.util.HashMap;
import java.util.Map;

public class Example {
    public static void main(String[] args) {
        // 创建映射:键为 String,值为 Integer
        Map<String, Integer> ages = new HashMap<>();

        // 添加元素
        ages.put("Basil", 25);
        ages.put("Peter", 30);
        ages.put("Mary", 22);

        // 根据键获取值
        int vasyaAge = ages.get("Basil");
        System.out.println("Basil 的年龄: " + vasyaAge); // 25

        // 检查是否存在某个键
        if (ages.containsKey("Mary")) {
            System.out.println("Mary 在列表中!");
        }

        // 删除元素
        ages.remove("Peter");

        // 遍历所有键值对
        for (String name : ages.keySet()) {
            System.out.println(name + ": " + ages.get(name));
        }
    }
}

输出:

Basil 的年龄: 25
Mary 在列表中!
Basil: 25
Mary: 22

HashMap 的特点

最重要的一点:HashMap 中的键始终是唯一的。如果你放入一个已存在键的新元素,旧值会被新值替换。

值可以重复:不同的键可以指向相同的值。

还有一个重要点——元素顺序。HashMap 不关心添加顺序。输出时条目可能是乱序的——这是正常行为。

4. TreeMap 类:按键排序

HashMap 不同,TreeMap 会按键的顺序存储元素。

何时使用 TreeMap?

当你需要元素按键递增(或递减)排列时。例如,希望按字母顺序输出电话簿。

示例:

import java.util.Map;
import java.util.TreeMap;

public class TreeMapExample {
    public static void main(String[] args) {
        Map<String, String> phoneBook = new TreeMap<>();

        phoneBook.put("Basil", "+1-900-123-45-67");
        phoneBook.put("Mary", "+1-900-555-55-55");
        phoneBook.put("Peter", "+1-900-222-33-44");

        for (String name : phoneBook.keySet()) {
            System.out.println(name + ": " + phoneBook.get(name));
        }
    }
}

输出:

Mary: +1-900-555-55-55
Peter: +1-900-222-33-44
Basil: +1-900-123-45-67

请注意:键是按字典序排序的。

5. Map 的基本操作

添加与替换元素
Map<String, Integer> scores = new HashMap<>();
scores.put("Anna", 90);
scores.put("John", 85);
scores.put("Anna", 95); // 会覆盖 "Anna" 的旧值
获取值
Integer annaScore = scores.get("Anna"); // 95
Integer unknown = scores.get("Basil");   // 如果没有该键,则为 null
检查是否存在某个键或值
scores.containsKey("John");    // true
scores.containsValue(85);      // true
按键删除键值对
scores.remove("John");
Map 的大小与清空
int size = scores.size();
scores.clear(); // 删除所有元素

5. 遍历 Map 的元素

Map 不是列表,没有索引。但可以这样遍历:

按键遍历:

for (String key : scores.keySet()) {
    System.out.println("键: " + key + ", 值: " + scores.get(key));
}

按值遍历:

for (Integer value : scores.values()) {
    System.out.println("值: " + value);
}

按键值对遍历(最佳方式):

for (Map.Entry<String, Integer> entry : scores.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key + " => " + value);
}

何时使用 HashMap,何时使用 TreeMap?

HashMap 是“默认”的通用选择。如果不关心键的顺序、而更在意操作速度,几乎总是使用它。

TreeMap 适用于需要顺序的场景。它会自动按键排序,并能快速找到最小/最大键或处理区间。

总结:在 90% 的情况下使用 HashMap。当数据需要“天然有序”时,使用 TreeMap

6. Map 的使用示例

示例 1:电话簿

Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("Kate", "+1-999-111-22-33");
phoneBook.put("Oliver", "+1-999-222-33-44");
phoneBook.put("Kate", "+1-999-555-66-77"); // Kate 的旧号码会被新号码覆盖

for (Map.Entry<String, String> entry : phoneBook.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

输出:

Oliver: +1-999-222-33-44
Kate: +1-999-555-66-77

示例 2:统计单词次数

假设我们有一个单词列表,希望知道每个单词出现了多少次:

import java.util.*;

public class WordCount {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("苹果", "香蕉", "苹果", "梨", "香蕉", "苹果");
        Map<String, Integer> counts = new HashMap<>();

        for (String word : words) {
            int oldCount = counts.getOrDefault(word, 0); // 如果没有该键 — 0
            counts.put(word, oldCount + 1);
        }

        System.out.println(counts); // {梨=1, 苹果=3, 香蕉=2}
    }
}

7. 使用 Map 时的常见错误

错误 1:混淆键和值。 新手常常尝试像在列表中那样按索引取值,或者忘了键必须唯一。在 Map 中没有索引——只有键。

错误 2:使用 null 键或值。HashMap 中允许键为 null,但在 TreeMap 中不允许(会抛出 NullPointerException)。两种实现中值都可以为 null,但这通常并不实用。

错误 3:期待 HashMap 中有固定顺序。 HashMap 不保证任何顺序。如果需要顺序——使用 LinkedHashMap(保留插入顺序)或 TreeMap(按键排序)。

错误 4:遍历过程中修改 Map。 如果在循环遍历 Map 的同时添加/删除元素,可能会出现 ConcurrentModificationException。此类任务请使用带有 remove() 方法的迭代器,或使用专用并发集合。

错误 5:用 == 比较键和值,而不是 equals。 Map 使用 equals 来比较键(和值)。如果你创建自定义键类,一定要重写 equalshashCode

1
任务
JAVA 25 SELF, 第 26 级, 课程 2
已锁定
智能任务调度器:星期几 🗓️
智能任务调度器:星期几 🗓️
1
任务
JAVA 25 SELF, 第 26 级, 课程 2
已锁定
库存水果按字母排序清单 🍏🍌🍐
库存水果按字母排序清单 🍏🍌🍐
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION