Java集合:双列集合HashMap的概念、特点及使用
字数 1325 2025-08-10 08:28:18

Java HashMap 详解

1. HashMap 概念

HashMap 是 Java 集合框架中 Map 接口的一个实现类,用于存储键值对(key-value)映射关系。它具有以下特性:

  • 允许键(key)和值(value)为 null
  • 键不能重复(重复的键会覆盖原有值)
  • 集合中的元素是无序的
  • 非线程安全(不同步)

2. HashMap 底层结构

2.1 存储结构演变

  • Java 1.8 之前:数组 + 链表
  • Java 1.8 及之后:数组 + 链表 + 红黑树

2.2 核心结构说明

  1. 数组:HashMap 的主体结构,默认初始容量为 16
  2. 链表:解决哈希冲突的分支结构
  3. 红黑树:当链表长度超过阈值(默认为8)时,链表会转换为红黑树以提高查询效率

2.3 存储过程

  1. 计算键的 hashCode
  2. 对 hashCode 进行取模运算得到数组下标
  3. 如果该位置为空,直接存储
  4. 如果该位置不为空(哈希冲突),则:
    • Java 1.8 前:添加到链表末尾
    • Java 1.8 后:先添加到链表,链表长度超过8时转换为红黑树

3. HashMap 核心特点

  1. 高效性:增、删、改、查操作效率较高
  2. 无序性:元素存储顺序与插入顺序无关
  3. 动态扩容:当元素数量超过阈值(容量*负载因子,默认0.75)时自动扩容
  4. 线程不安全:不适合多线程环境使用

4. HashMap 使用示例

4.1 基本使用

Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
String value = map.get("key1");

4.2 自定义对象作为键

当使用自定义对象作为 HashMap 的键时,必须重写 hashCode() 和 equals() 方法:

public class Student {
    private String name;
    private int age;

    // 构造方法、getter/setter 省略

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

4.3 遍历方式

  1. 键找值方式
Set<Student> keySet = map.keySet();
for (Student key : keySet) {
    String value = map.get(key);
    System.out.println(key + "=" + value);
}
  1. 键值对对象方式
Set<Map.Entry<Student, String>> entrySet = map.entrySet();
for (Map.Entry<Student, String> entry : entrySet) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

5. LinkedHashMap

LinkedHashMap 是 HashMap 的子类,在 HashMap 基础上维护了一个双向链表,可以保持元素的插入顺序或访问顺序。

5.1 特点

  1. 继承自 HashMap
  2. 维护插入顺序或访问顺序
  3. 性能略低于 HashMap(因为要维护链表)

5.2 使用示例

LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("马云", "阿里巴巴");
map.put("马化腾", "腾讯");
map.put("李彦宏", "百度");

// 遍历时会按照插入顺序输出
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}

6. 性能优化与注意事项

  1. 初始容量:预估元素数量设置合理的初始容量,避免频繁扩容
  2. 负载因子:默认0.75,可根据实际情况调整
  3. 哈希冲突:良好的 hashCode() 实现可以减少冲突
  4. 线程安全:多线程环境下应使用 ConcurrentHashMap 或 Collections.synchronizedMap()
  5. 内存泄漏:避免使用可变对象作为键

7. 常见问题

  1. HashMap 和 HashSet 的关系

    • HashSet 底层实际上使用 HashMap 实现
    • HashSet 只使用 HashMap 的键,值统一为一个静态常量
  2. 为什么重写 equals() 必须重写 hashCode()

    • 保证相同对象有相同哈希码
    • 确保在 HashMap 中能正确找到对象
  3. 链表转红黑树的阈值

    • 默认是链表长度超过8时转换
    • 红黑树退化为链表的阈值是6(避免频繁转换)

HashMap 是 Java 集合框架中非常重要且高效的集合类,合理使用可以大大提高程序处理数据的效率,但需要注意其线程不安全特性和正确实现键对象的 hashCode() 和 equals() 方法。

Java HashMap 详解 1. HashMap 概念 HashMap 是 Java 集合框架中 Map 接口的一个实现类,用于存储键值对(key-value)映射关系。它具有以下特性: 允许键(key)和值(value)为 null 键不能重复(重复的键会覆盖原有值) 集合中的元素是无序的 非线程安全(不同步) 2. HashMap 底层结构 2.1 存储结构演变 Java 1.8 之前:数组 + 链表 Java 1.8 及之后:数组 + 链表 + 红黑树 2.2 核心结构说明 数组 :HashMap 的主体结构,默认初始容量为 16 链表 :解决哈希冲突的分支结构 红黑树 :当链表长度超过阈值(默认为8)时,链表会转换为红黑树以提高查询效率 2.3 存储过程 计算键的 hashCode 对 hashCode 进行取模运算得到数组下标 如果该位置为空,直接存储 如果该位置不为空(哈希冲突),则: Java 1.8 前:添加到链表末尾 Java 1.8 后:先添加到链表,链表长度超过8时转换为红黑树 3. HashMap 核心特点 高效性 :增、删、改、查操作效率较高 无序性 :元素存储顺序与插入顺序无关 动态扩容 :当元素数量超过阈值(容量* 负载因子,默认0.75)时自动扩容 线程不安全 :不适合多线程环境使用 4. HashMap 使用示例 4.1 基本使用 4.2 自定义对象作为键 当使用自定义对象作为 HashMap 的键时,必须重写 hashCode() 和 equals() 方法: 4.3 遍历方式 键找值方式 : 键值对对象方式 : 5. LinkedHashMap LinkedHashMap 是 HashMap 的子类,在 HashMap 基础上维护了一个双向链表,可以保持元素的插入顺序或访问顺序。 5.1 特点 继承自 HashMap 维护插入顺序或访问顺序 性能略低于 HashMap(因为要维护链表) 5.2 使用示例 6. 性能优化与注意事项 初始容量 :预估元素数量设置合理的初始容量,避免频繁扩容 负载因子 :默认0.75,可根据实际情况调整 哈希冲突 :良好的 hashCode() 实现可以减少冲突 线程安全 :多线程环境下应使用 ConcurrentHashMap 或 Collections.synchronizedMap() 内存泄漏 :避免使用可变对象作为键 7. 常见问题 HashMap 和 HashSet 的关系 : HashSet 底层实际上使用 HashMap 实现 HashSet 只使用 HashMap 的键,值统一为一个静态常量 为什么重写 equals() 必须重写 hashCode() : 保证相同对象有相同哈希码 确保在 HashMap 中能正确找到对象 链表转红黑树的阈值 : 默认是链表长度超过8时转换 红黑树退化为链表的阈值是6(避免频繁转换) HashMap 是 Java 集合框架中非常重要且高效的集合类,合理使用可以大大提高程序处理数据的效率,但需要注意其线程不安全特性和正确实现键对象的 hashCode() 和 equals() 方法。