欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > HashMap 中的 key 值类型

HashMap 中的 key 值类型

2025/3/15 12:35:54 来源:https://blog.csdn.net/qq_41893505/article/details/146219184  浏览:    关键词:HashMap 中的 key 值类型

在 Java 中,HashMapkey 一般建议使用 String 而不是自定义对象,主要有以下几个原因:

1. String 是不可变对象(Immutable)

  • String 在 Java 中是不可变的,一旦创建就不会改变其哈希值 (hashCode)。
  • HashMap 依赖 keyhashCode() 计算存储位置,如果 key 是可变对象,修改 key 后,它的 hashCode() 可能会改变,导致 HashMap 无法正确查找该 key,引发潜在问题(如数据丢失、无法查找等)。
  • 例如:
    Map<List<Integer>, String> map = new HashMap<>();
    List<Integer> key = new ArrayList<>();
    key.add(1);
    map.put(key, "value");key.add(2); // 修改 key
    System.out.println(map.get(key)); // 可能返回 null
    
    由于 ArrayListhashCode() 依赖于内容,key 变化后 hashCode 变化,导致 HashMap 无法找到原来的 value

2. StringhashCode() 计算高效且稳定

  • String 在 Java 中的 hashCode() 实现是经过高度优化的,并且被广泛使用,计算效率高。
  • StringhashCode() 计算方式如下:
    public int hashCode() {int h = 0;for (int i = 0; i < length(); i++) {h = 31 * h + charAt(i);}return h;
    }
    
  • 由于 Stringfinal 类,它的 hashCode() 计算逻辑不会被子类重写或修改,保证了哈希值的一致性。

3. String 具有良好的分布性,减少 Hash 冲突

  • HashMap 中,良好的 hashCode() 设计可以减少哈希冲突,提高查询效率。
  • StringhashCode() 计算方式能够较好地分布数据,避免大量 key 落在相同的桶(bucket)里。

4. String 适合作为标识符

  • String 直观易读,可以直接表示用户名、ID、类别等,便于代码理解。
  • 例如:
    Map<String, Integer> map = new HashMap<>();
    map.put("Alice", 25);
    map.put("Bob", 30);
    
    相比于自定义对象,String 更适合作为 key 来表示业务属性。

5. 避免 equals()hashCode() 方法实现错误

  • 使用自定义对象作为 key 时,需要正确实现 equals()hashCode() 方法,否则会导致 HashMap 行为异常,如无法正确查找 key 或发生哈希冲突。
  • 例如:
    class Person {String name;int age;
    }
    
    如果 Person 没有正确重写 equals()hashCode(),那么 HashMap 可能无法正确区分两个 Person 对象,即使它们的 nameage 相同。

6. String 具有内存优化(字符串常量池)

  • String 在 JVM 中有字符串常量池(String Pool),相同字符串可复用,减少内存占用。
  • 例如:
    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2); // true,共享同一个对象
    

总结

比较项使用 String 作为 key使用对象作为 key
不可变性不可变,哈希值稳定可能可变,影响 hashCode()
哈希分布性StringhashCode() 分布良好需要自定义 hashCode(),可能不均匀
易用性直观,易读易维护需要正确实现 equals()hashCode()
性能StringhashCode() 计算高效可能计算复杂,影响 HashMap 效率
内存优化享受 JVM String Pool 优势没有字符串池机制

因此,在 HashMap 中,推荐优先使用 String 作为 key,如果必须使用对象作为 key,需要确保:

  1. 该对象是不可变的;
  2. 正确重写 equals()hashCode() 方法。

HashMap 中,如果 key 是一个对象,并且该对象的某个属性发生变化,那么可能会导致无法通过原来的 key 找到对应的 value

为什么找不到原来的 value

  1. HashMap 依赖 keyhashCode() 计算存储位置
    • HashMap 通过 keyhashCode() 计算存储桶(bucket)的索引。
    • 如果 key 是对象,并且对象的某些属性影响了 hashCode() 计算,那么修改该属性会导致 hashCode() 变化,使得 HashMap 无法在原来的位置找到该 key
  2. HashMap 依赖 equals() 方法查找 key
    • HashMap 中,即使两个对象的 hashCode() 相同,最终还是要通过 equals() 方法确认它们是否相等。
    • 如果 equals() 方法依赖于某些可变属性,而这些属性发生了变化,那么即使 hashCode() 仍然相同,也可能会导致 equals() 失效,使得 HashMap 无法匹配原来的 key

示例代码

import java.util.HashMap;
import java.util.Objects;class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Person person = (Person) obj;return age == person.age && Objects.equals(name, person.name);}
}public class HashMapKeyTest {public static void main(String[] args) {HashMap<Person, String> map = new HashMap<>();Person p = new Person("Alice", 25);map.put(p, "Engineer");System.out.println("Before change: " + map.get(p)); // 正常获取 "Engineer"// 修改 key 对象的属性p.age = 30;System.out.println("After change: " + map.get(p)); // 可能返回 nullSystem.out.println("Contains key: " + map.containsKey(p)); // 可能返回 false}
}

分析

  1. Person 作为 key,它的 hashCode() 依赖于 nameage
  2. 存入 HashMap 后,p 计算的 hashCode() 确定了它在 HashMap 的存储位置。
  3. 修改 p.age 后,hashCode() 发生变化,导致 HashMap 在查找 p 时,计算出的 hashCode() 对应的存储桶位置可能已经改变。
  4. 结果:map.get(p) 可能返回 null,即无法找到原来的 value

如何解决?

方案 1:使用不可变对象作为 key

class Person {private final String name;private final int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Person person = (Person) obj;return age == person.age && Objects.equals(name, person.name);}
}
  • 优点:保证 key 不变,避免 hashCode() 变化导致查找失败。
  • 适用场景:如果 key 需要稳定,建议使用 final 关键字让属性不可变,或者使用 String 作为 key

方案 2:使用 put() 重新存入修改后的 key

如果 key 发生变化,需要重新 put() 进去:

map.remove(p); // 先删除旧的 key
p.age = 30;    // 修改属性
map.put(p, "Engineer"); // 重新放入 HashMap
  • 适用场景:如果一定要修改 key,需要确保 HashMap 结构同步更新。

总结

方案影响适用场景
修改 key 属性hashCode() 变更,导致查找失败不推荐
使用不可变对象key 保持不变,避免问题推荐
使用 remove() 后重新 put()需要额外操作,保证 HashMap 一致性适用于可变对象

👉 最佳实践:建议 HashMapkey 选择 String 或不可变对象,避免因 hashCode() 变化导致查找失败!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词