java基础——java集合map详解

在 Java 中,Map 是一个接口,属于 Java 集合框架(java.util 包),用于存储键值对(Key-Value Pair)的数据结构。Map 的键是唯一的,每个键映射到一个值,适合快速查找、关联数据等场景。以下是对 Java Map 的详细讲解,涵盖其概念、实现类、常用方法、特性、示例代码以及注意事项,适合零基础到进阶学习者。


一、Map 概述

Map 接口定义了一种键值映射关系,键(Key)唯一,值(Value)可以重复。Map 不继承 Collection 接口,因此不直接支持集合操作(如迭代器),但可以通过其视图(如键集、值集、键值对集)进行操作。

核心特性

  1. 键唯一性:每个键最多关联一个值,重复设置键会覆盖旧值。
  2. 无序性:大多数 Map 实现(如 HashMap)不保证键值对的顺序。
  3. 灵活性:键和值可以是任意对象类型(包括 null,视实现而定)。
  4. 高效查找:通过键快速访问值,通常时间复杂度为 O(1)(如 HashMap)。

主要实现类

实现类描述特点
HashMap基于哈希表的无序映射快速(O(1) 平均时间),允许 null 键和值,非线程安全。
LinkedHashMapHashMap 的子类,维护插入顺序按插入顺序或访问顺序遍历,性能略低于 HashMap
TreeMap基于红黑树的有序映射键按自然顺序或自定义比较器排序,查找时间 O(log n)。
Hashtable类似 HashMap 的早期实现线程安全,性能较低,不允许 null 键或值,已较少使用。
ConcurrentHashMap线程安全的 HashMap高并发场景优化,性能优于 Hashtable

二、Map 接口的核心方法

以下是 Map 接口的常用方法,均定义在 java.util.Map 中:

方法功能返回值示例
put(K key, V value)插入或更新键值对旧值(若存在),否则 nullmap.put("name", "Alice")
get(Object key)获取键对应的值值,若键不存在返回 nullmap.get("name") 返回 "Alice"
remove(Object key)删除键值对删除的值,若键不存在返回 nullmap.remove("name")
containsKey(Object key)检查键是否存在booleanmap.containsKey("name")
containsValue(Object value)检查值是否存在booleanmap.containsValue("Alice")
size()获取键值对数量intmap.size()
isEmpty()检查是否为空booleanmap.isEmpty()
clear()清空所有键值对map.clear()
keySet()返回所有键的 Set 视图Set<K>map.keySet()
values()返回所有值的 Collection 视图Collection<V>map.values()
entrySet()返回键值对的 Set 视图Set<Map.Entry<K,V>>map.entrySet()
putAll(Map<? extends K, ? extends V> m)批量添加键值对map.putAll(anotherMap)
getOrDefault(Object key, V defaultValue)获取键值,若不存在返回默认值值或默认值map.getOrDefault("age", 0)

注意

  • Map.EntryMap 的内部接口,表示键值对,提供 getKey()getValue() 方法。
  • 方法操作可能抛出异常,如 NullPointerException(键或值不支持 null 时)。

三、主要实现类详解

1. HashMap

  • 底层实现:哈希表(数组 + 链表/红黑树)。JDK 8 起,当链表长度超过 8 且桶数量足够,链表转为红黑树以优化性能。
  • 特点
  • 无序,键值对存储顺序不可预测。
  • 允许一个 null 键和多个 null 值。
  • 非线程安全,高性能。
  • 适用场景:需要快速查找和插入的场景,如缓存、数据索引。
  • 示例
  import java.util.HashMap;
  import java.util.Map;

  public class HashMapExample {
      public static void main(String[] args) {
          Map<String, Integer> map = new HashMap<>();
          map.put("Alice", 25);
          map.put("Bob", 30);
          map.put(null, 0); // 允许 null 键
          System.out.println(map.get("Alice")); // 输出: 25
          System.out.println(map.getOrDefault("Charlie", 18)); // 输出: 18
          System.out.println(map); // 输出: {null=0, Alice=25, Bob=30}
      }
  }

2. LinkedHashMap

  • 底层实现:哈希表 + 双向链表,维护插入顺序或访问顺序(通过构造器参数 accessOrder=true)。
  • 特点
  • 按插入顺序或访问顺序遍历。
  • 性能略低于 HashMap,因需维护链表。
  • 允许 null 键和值。
  • 适用场景:需要保持插入顺序或实现 LRU(最近最少使用)缓存。
  • 示例
  import java.util.LinkedHashMap;
  import java.util.Map;

  public class LinkedHashMapExample {
      public static void main(String[] args) {
          Map<String, Integer> map = new LinkedHashMap<>();
          map.put("Alice", 25);
          map.put("Bob", 30);
          map.put("Charlie", 35);
          System.out.println(map); // 输出: {Alice=25, Bob=30, Charlie=35}
      }
  }

3. TreeMap

  • 底层实现:红黑树,键按自然顺序(Comparable)或自定义比较器(Comparator)排序。
  • 特点
  • 有序,键按升序或自定义顺序排列。
  • 不允许 null 键,但允许 null 值。
  • 查找和插入时间为 O(log n)。
  • 适用场景:需要按键排序的场景,如字典、范围查询。
  • 示例
  import java.util.TreeMap;
  import java.util.Map;

  public class TreeMapExample {
      public static void main(String[] args) {
          Map<String, Integer> map = new TreeMap<>();
          map.put("Charlie", 35);
          map.put("Alice", 25);
          map.put("Bob", 30);
          System.out.println(map); // 输出: {Alice=25, Bob=30, Charlie=35}
      }
  }

4. Hashtable

  • 底层实现:哈希表,类似 HashMap
  • 特点
  • 线程安全(方法加 synchronized)。
  • 不允许 null 键或值。
  • 性能低于 HashMapConcurrentHashMap
  • 适用场景:早期多线程场景,现多被 ConcurrentHashMap 替代。
  • 示例
  import java.util.Hashtable;
  import java.util.Map;

  public class HashtableExample {
      public static void main(String[] args) {
          Map<String, Integer> map = new Hashtable<>();
          map.put("Alice", 25);
          map.put("Bob", 30);
          // map.put(null, 0); // 抛出 NullPointerException
          System.out.println(map); // 输出: {Bob=30, Alice=25}
      }
  }

5. ConcurrentHashMap

  • 底层实现:分段锁(JDK 8 起用 CAS + 同步块),优化并发性能。
  • 特点
  • 线程安全,高并发下性能优于 Hashtable
  • 不允许 null 键或值。
  • 迭代时允许并发修改(弱一致性)。
  • 适用场景:多线程环境,如并发缓存。
  • 示例
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.Map;

  public class ConcurrentHashMapExample {
      public static void main(String[] args) {
          Map<String, Integer> map = new ConcurrentHashMap<>();
          map.put("Alice", 25);
          map.put("Bob", 30);
          System.out.println(map); // 输出: {Alice=25, Bob=30}
      }
  }

四、遍历 Map 的方式

Map 提供了三种视图进行遍历:

  1. 通过 keySet() 遍历键
   Map<String, Integer> map = new HashMap<>();
   map.put("Alice", 25);
   map.put("Bob", 30);
   for (String key : map.keySet()) {
       System.out.println(key + ": " + map.get(key));
   }
  1. 通过 entrySet() 遍历键值对(推荐,效率高):
   for (Map.Entry<String, Integer> entry : map.entrySet()) {
       System.out.println(entry.getKey() + ": " + entry.getValue());
   }
  1. 通过 values() 遍历值
   for (Integer value : map.values()) {
       System.out.println("Value: " + value);
   }
  1. 使用 forEach(Java 8+)
   map.forEach((key, value) -> System.out.println(key + ": " + value));

五、Map 的性能分析

实现类插入查找删除线程安全键排序null 键
HashMapO(1)O(1)O(1)无序允许
LinkedHashMapO(1)O(1)O(1)插入/访问顺序允许
TreeMapO(log n)O(log n)O(log n)有序不允许
HashtableO(1)O(1)O(1)无序不允许
ConcurrentHashMapO(1)O(1)O(1)无序不允许

注意

  • HashMap 适合大多数场景,性能最佳。
  • TreeMap 适合需要排序的场景。
  • ConcurrentHashMap 适合高并发环境。

六、注意事项

  1. 键的 hashCodeequals
  • HashMapConcurrentHashMap 依赖键的 hashCode()equals() 方法,自定义键类必须正确实现。
  • 示例:
    java class Key { String id; public Key(String id) { this.id = id; } @Override public boolean equals(Object o) { /* 实现 */ } @Override public int hashCode() { /* 实现 */ } }
  1. 线程安全
  • HashMapLinkedHashMap 非线程安全,多线程下可能导致数据不一致。
  • 使用 Collections.synchronizedMap(map)ConcurrentHashMap 解决。
  1. null 处理
  • HashMapLinkedHashMap 允许 null 键和值,TreeMapHashtable 不允许。
  • 小心 get() 返回 null(可能是键不存在或值是 null)。
  1. 性能优化
  • 设置初始容量(initialCapacity)和负载因子(loadFactor)以减少哈希表扩容。
    java Map<String, Integer> map = new HashMap<>(16, 0.75f);
  1. 不可变 Map(Java 9+):
  • 使用 Map.of()Map.ofEntries() 创建不可变映射:
    java Map<String, Integer> map = Map.of("Alice", 25, "Bob", 30);

七、综合示例

以下是一个综合示例,展示 HashMapLinkedHashMapTreeMap 的使用:

import java.util.*;

public class MapExample {
public static void main(String[] args) {
// HashMap 示例
Map hashMap = new HashMap<>();
hashMap.put(“Alice”, 25);
hashMap.put(“Bob”, 30);
hashMap.put(“Charlie”, 35);
System.out.println(“HashMap: ” + hashMap);
System.out.println(“Alice’s age: ” + hashMap.getOrDefault(“Alice”, 0));

    // LinkedHashMap 示例(保持插入顺序)
    Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
    linkedHashMap.put("Alice", 25);
    linkedHashMap.put("Bob", 30);
    linkedHashMap.put("Charlie", 35);
    System.out.println("LinkedHashMap: " + linkedHashMap);

    // TreeMap 示例(按键排序)
    Map<String, Integer> treeMap = new TreeMap<>();
    treeMap.put("Charlie", 35);
    treeMap.put("Alice", 25);
    treeMap.put("Bob", 30);
    System.out.println("TreeMap: " + treeMap);

    // 遍历 HashMap(使用 entrySet)
    System.out.println("Traversing HashMap:");
    for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
        System.out.println(entry.getKey() + ": " + entry.getValue());
    }

    // 使用 forEach 遍历
    System.out.println("Traversing with forEach:");
    hashMap.forEach((key, value) -> System.out.println(key + ": " + value));
}

}

输出

HashMap: {Alice=25, Bob=30, Charlie=35}
Alice's age: 25
LinkedHashMap: {Alice=25, Bob=30, Charlie=35}
TreeMap: {Alice=25, Bob=30, Charlie=35}
Traversing HashMap:
Alice: 25
Bob: 30
Charlie: 35
Traversing with forEach:
Alice: 25
Bob: 30
Charlie: 35

八、常见应用场景

  1. 缓存:用 HashMap 存储查询结果,减少数据库访问。
  2. 数据分组:用 Map 按键分组数据(如按城市统计人数)。
  3. 配置管理:用 Map 存储键值配置(如属性文件)。
  4. LRU 缓存:用 LinkedHashMap 实现最近最少使用缓存。
  5. 排序映射:用 TreeMap 按键排序,如字典应用。

九、总结

  • Map 接口:提供键值对存储,核心方法包括 putgetremove 等。
  • 实现类
  • HashMap:高性能,无序,适合大多数场景。
  • LinkedHashMap:保持插入或访问顺序,适合有序遍历。
  • TreeMap:按键排序,适合排序需求。
  • ConcurrentHashMap:线程安全,适合并发场景。
  • Hashtable:老旧,线程安全但性能较低。
  • 注意事项:键的 hashCodeequals、线程安全、null 处理、初始容量。
  • 学习建议
  • 练习不同实现类的使用,比较性能和顺序。
  • 实现小型项目,如学生管理系统(键为学号,值为学生对象)。
  • 阅读 JDK 源码(如 HashMap.put)以深入理解。

如果需要更深入的某实现类讲解(如 HashMap 的红黑树机制)、并发场景应用或具体代码调试,请告诉我,我可以提供进一步帮助!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注