HashMap 基于哈希算法工作,根据 Java 文档 HashMap 有以下四个构造函数,
HashMap()
HashMap(int initialCapacity)
HashMap(int initialCapacity, float loadFactor)
HashMap(Map<? extends K,? extends V> m)
构造函数 | 描述 |
---|---|
构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap 。 |
|
构造一个具有指定初始容量和默认加载因子 (0.75) 的空 HashMap 。 |
|
构造一个具有指定初始容量和加载因子的空 HashMap 。 |
|
构造一个新的 HashMap ,其映射与指定的 Map 相同。 |
让我们编写简单的 java 程序,检查 Map 内部是如何工作的
public static void main(String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(1, "Java"); }
我们刚刚创建了 Simple Map,它以 Integer 为键,以 String 为值,并添加了“1”作为 Key 和“Java”作为值。通过使用 eclipse 调试功能,让我们看看Map里面有什么
它创建了 16 个块(0-15)并插入了第一个块,键为整数“1”,值为字符串“Java”。请检查红色框,其余所有框都用 null 初始化。
2.将第二个键和值添加到同一个映射
public static void main(String[] args) { Map<Integer, String> map = new HashMap<>(); map.put(1, "Java"); map.put(2, "Angular"); }
让我们再次在 Eclipse 调试中查看Map
现在映射包含两个键 (1,2) 和两个值(“Java”、“Angular”),正如预期的那样,但是键分别准确地添加在第一个块和第二个块中,为什么?
因为我们知道 Map 基于哈希算法工作,每当我们将键插入映射时,它会调用 Object#hashcode() 方法,根据 hashCode() 的值,它会将键插入到该块中。
在上面的例子中,Integer 类用它的原始 int 值覆盖了 hashCode,这就是为什么 (1,java) 存储在第一个块中而 (2,Angular) 存储在第二个块中的原因。
3.让我们用我们自己的类做同样的实验
创建一个简单的 Employee 类,如下所示
private static class Employee{ int id; String name; Employee(int id, String name){ this.id = id; this.name = name; } }
使用此类作为Map的 Key 并以相同的方式检查
public static void main(String[] args) { Map<Employee, String> map = new HashMap<>(10); map.put(new Employee(1, "Ramesh"), "Java"); map.put(new Employee(2, "Sathish"), "Angular"); }
我们添加了两个键作为 Employee 对象和值作为字符串,让我们看看这次键存储在哪个块中
这次,它存储在第 8 个块和第 14 个块中(为什么?简单的答案是因为 Employee 对象的 hashCode),为了确认这一点,让将 Employee 的 hashCode() 重写为常量值并检查映射。如果我们的分析正确,它必须将所有密钥存储在同一个块中。
相应地更新 Employee 类
private static class Employee{ int id; String name; Employee(int id, String name){ this.id = id; this.name = name; } @Override public int hashCode() { return 10; } }
我们不需要对Map进行任何更改,现在让我们看看密钥存储在哪里
是的,只有第 10 个方块被两个物体填满,为什么?因为两个员工对象都返回了相同的 hashCode(即 10)。但是 Map 如何识别这两个对象不是重复的呢?我们在内部知道 Map#Key 是一个 entrySet(java.util.Set) 它调用 equals 方法来验证键是否重复。
在从 Map 中检索值的同时,首先它会检查给定键的 hashCode,并根据它转到该块,在找到该块后,它将调用 equals() 以获得确切的值。
因此,将 hashCode() 覆盖为常量根本不值得推荐。并且当我们覆盖 hashCode() 时,我们不应该忘记也覆盖 equals() 方法(即 hashCode()/equals() 契约)。
标签2: Java教程地址:https://www.cundage.com/article/jcg-java-hashmap-detail-explanation.html