253. Java 集合 - 访问和操作 Map 的视图

48 阅读3分钟

253. Java 集合 - 访问和操作 Map 的视图(Views)


1. 🧩 获取 Map 的不同视图

Java 中,Map 提供了三种常用的“视图”,让我们分别访问键、值或者键值对:

方法返回类型内容说明
keySet()Set<K>返回 Map 中所有的键
values()Collection<V>返回 Map 中所有的值
entrySet()Set<Map.Entry<K,V>>返回 Map 中所有键值对(Entry

这些集合**是由 Map 支持(backed)**的,意味着:

  • 改变集合(比如移除元素)会影响原 Map
  • 改变 Map 也会影响这些集合。

📖 示例:访问 KeysValuesEntries

import java.util.*;

public class MapViewExample {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "one");
        map.put(2, "two");
        map.put(3, "three");
        map.put(4, "four");
        map.put(5, "five");

        // 获取所有键
        Set<Integer> keys = map.keySet();
        System.out.println("Keys: " + keys); 

        // 获取所有值
        Collection<String> values = map.values();
        System.out.println("Values: " + values);

        // 获取所有键值对
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        System.out.println("Entries: " + entries);
    }
}

🖨️ 输出结果:

Keys: [1, 2, 3, 4, 5]
Values: [one, two, three, four, five]
Entries: [1=one, 2=two, 3=three, 4=four, 5=five]

2. 🗑️ 通过视图修改 Map

因为这些视图是与原 Map 联动的,所以:

  • 从 keySet 中移除一个键,相应的键值对会从 Map 中消失。
  • 从 values 中移除一个值,只会删除第一次出现该值的键值对

✨ 示例:通过 keySet() 移除键

public class KeySetRemoveExample {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "one");
        map.put(2, "two");
        map.put(3, "three");

        Set<Integer> keys = map.keySet();
        keys.remove(2); // 移除键 2

        System.out.println("Map after removing key 2: " + map);
    }
}

🖨️ 输出:

Map after removing key 2: {1=one, 3=three}

✅ 可以看到,删除 keySet() 中的元素,直接影响了原 Map。


✨ 示例:通过 values() 移除值

public class ValuesRemoveExample {
    public static void main(String[] args) {
        Map<Integer, String> map = Map.ofEntries(
            Map.entry(1, "alpha"),
            Map.entry(2, "beta"),
            Map.entry(3, "alpha"),
            Map.entry(4, "gamma")
        );

        System.out.println("Map before removal: " + map);

        // 复制成可变的 HashMap
        map = new HashMap<>(map);
        map.values().remove("alpha"); // 只会移除第一个找到的 "alpha"

        System.out.println("Map after removing 'alpha': " + map);
    }
}

🖨️ 输出:

Map before removal: {1=alpha, 2=beta, 3=alpha, 4=gamma}
Map after removing 'alpha': {2=beta, 3=alpha, 4=gamma}

⚠️ 注意:

  • 只移除了 第一个 "alpha" 对应的键值对。
  • HashMap 中,元素顺序不可预测,所以无法提前确定哪一个会被删除。

3. 🚫 视图的限制

虽然可以删除元素,但不能添加新元素到这些视图中!

例如:

keys.add(7); // 会抛出 UnsupportedOperationException
values.add("seven"); // 也会抛出 UnsupportedOperationException

⛔ 不能直接往 keySet()values() 中新增元素。要新增数据,必须通过 put(key, value) 添加到原 Map!


4. 🔥 最佳实践:遍历 Map 的推荐方式

如果你需要遍历 Map 中的键值对,最推荐的方式是直接遍历 entrySet()

📖 示例:使用 entrySet 高效遍历

public class EntrySetTraversalExample {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(10, "ten");
        map.put(20, "twenty");
        map.put(30, "thirty");

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

🖨️ 输出:

10 -> ten
20 -> twenty
30 -> thirty

✅ 比起遍历 keySet() 再通过 get(key) 获取值,遍历 entrySet()高效且直观


🎯 小结

视图方法功能是否能修改内容是否能新增元素
keySet()访问所有键可以删除键❌ 不能新增键
values()访问所有值可以删除第一个匹配的值❌ 不能新增值
entrySet()访问所有键值对可以删除Entry❌ 不能新增Entry

👀 互动问题

问题1values().remove("X") 为什么只能删除第一个匹配值?

答案示例:因为一个值可能对应多个键,values() 只删除遇到的第一个匹配项,且在 HashMap 中顺序不可预测。

问题2:能不能通过 keySet().add(key) 添加新的键到 Map?

答案示例:不能,会抛出 UnsupportedOperationException;新增必须用 put() 方法。

问题3:为什么遍历 Map 推荐用 entrySet()

答案示例:遍历 entrySet() 能一次性同时拿到键和值,性能更好,比每次取 key 再 get() 取 value 高效。