ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# Java `Hashmap`教程 > 原文: [https://javabeginnerstutorial.com/core-java-tutorial/java-hashmap/](https://javabeginnerstutorial.com/core-java-tutorial/java-hashmap/) Java `HashMap`是基于哈希表的`Map`实现。 这就是为什么面试官总是要求`HashMap`和`HashTable`之间存在差异的原因。 `HashMap`基本上等于`HashTable`,但以下两个区别除外。 1. 当`HashTable`同步时,`HashMap`不同步。 2. `HashMap`允许为`null`,而`HashTable`不允许为`null`。 ## `HashMap`的重要属性 | | | | --- | --- | | **`DEFAULT_INITIAL_CAPACITY`** | 默认初始容量(2 的 n 次幂)。 `HashMap`可以包含许多元素。 | | **`MAXIMUM_CAPACITY`** | `HashMap`的最大容量(2 的 n 次幂)。 | | **`loadFactor`** | 定义`HashMap`的阈值。 重新调整大小时将在`HashMap`中发生。 | | **`DEFAULT_LOAD_FACTOR`** | 在`HashMap`的构造器中未定义任何负载因子时将使用。 | | **`size`** | `HashMap`包含的键/值对映射数。 | ## 创建`HashMap` 在创建`HashMap`时未定义任何参数时,将使用默认的初始容量(16)和默认的负载系数(0.75)。 该`HashMap`最多可以包含 16 个元素,并且在插入第 13 个元素时会调整`HashMap`的大小。 这是因为负载系数为 75% (.75),并且在添加第 13 个元素(`12 + 1`)时将超过此阈值。 您还可以提供初始容量和`loadFactor`。 但是初始容量不能超过最大容量(2 的 30 次幂),并且负载系数不能为零或负数。 ## `HashMap`中元素的添加 为了添加一个元素,您需要提供 2 个东西:键和值。 **键**:将与指定值关联的键。 `null`是允许的。 **值**:与指定键关联的值。 首先,`HashMap`将为给定键生成一个哈希码,然后检查是否已经有与给定键相关联的值。 如果是,则它将返回已经关联的值。 否则,它将通过提供的键在`HashMap`中增加值。 ## 重点 1. `HashMap`不会按`Map`中的元素顺序提供任何保证(均值顺序会随时间变化)。 2. `HashMap`为**获得**和**设置**操作提供恒定时间性能(如果使用正确的哈希算法)。 3. 迭代集合所需的时间与`HashMap`的“**容量**”(它可以容纳的元素)和**大小**(它当前容纳的元素)成比例。 4. 如果迭代性能更为重要,那么建议不要将初始容量设置得太高而将负载系数设置得太低。 由于性能与*初始容量*和*负载系数*成正比。 * *容量*是哈希表中的存储桶数。 * 初始容量(默认值为 16)只是创​​建哈希表时的容量。 * *负载因子(默认值 .75)*是衡量哈希表在自动增加其容量之前的填充程度的度量。 * 当哈希表中的条目数超过负载因子与当前容量的乘积时,哈希表将被*重映射*(即内部数据结构将被重建)。 5. 使用“`Collections.synchronizedMap()`”方法使映射同步。 6. 由`HashMap`类返回的迭代器为“**故障快速**”。 7. `HashMap`由数组(`Key`)和`LinkedList`(`Value`)支持。 8. `HashMap`使用`hashcode`(使用键)来标识应在`HashMap`中放置或检索对象的确切位置。 9. 最后,`HashCode`返回后备数组中的确切位置(索引)。 10. 支持数组的大小固定。 因此,只要数组已满(此映射中的键数达到其阈值)。 将创建一个具有新容量的新数组,并将所有元素添加到该新数组中。 11. 在两种情况下(添加和检索对象)都将使用`HashCode`,而在任何情况下均可以使用或可以不使用`equals()`方法。 12. `HashMap`中`Key`的最佳候选者是具有正确实现`Equals`和`Hashcode`方法的不可变类(示例:字符串类)。 13. 更好的哈希码和`equals`方法实现是`HashMap`的更好性能。 14. 这样,所有`String`和原始类型的包装类将是`HashMap`中键的理想选择。 # 什么是重新哈希 每个`HashMap`都有预定义的大小(初始容量),以及在需要时(超过阈值限制时)增加此大小(负载系数)的逻辑。 **示例**: 使用以下配置创建`HashMap` 初始容量:16(*默认初始容量*) 负载系数:.75(*默认负载系数*) 在给定的`HashMap`中添加第 13 个元素后,超过给定`HashMap`的阈值限制,系统将创建一个新的后备键集数组(此数组的大小将是前一个数组的两倍)。 系统将不得不再次计算确切的存储桶,应放置上一个存储桶中的元素,并将旧`HashMap`中的所有元素复制到新的`HashMap`中。 整个过程称为重新哈希,因为会再次为每个元素计算`Hashcode`。 因为超时的`HashMap`可能会被重新散布并且顺序可能会发生变化。 # `HashMap`的示例 在此示例中,您将学到以下几点 1. 如何迭代映射 2. 迭代映射的不同方法 3. `HashCode`和`Equals`何时被调用。 (请特别注意`HashCode`的输出和`equals`方法调用) ```java import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class HashMapExample { public static void main(String[] args) { Map<JBT, Integer> m1 = new HashMap<JBT, Integer>(); JBT t1 = new JBT(1, 2); JBT t2 = new JBT(1, 3); JBT t3 = new JBT(2, 1); m1.put(t1, 1); m1.put(t2, 2); m1.put(t3, 3); System.out.println("Addition Done"); /* * Below you can find 3 different ways to iterate a Map. Uncomment * different section and see the different in Output. Pay attention to * when Hashcode and Equals is called */ /* Set s = m1.entrySet(); for (Iterator i = s.iterator(); i.hasNext();) { Map.Entry me = (Map.Entry) i.next(); System.out.println(me.getKey() + " : " + me.getValue()); } */ /* for (Map.Entry<JBT, Integer> entry : m1.entrySet()) { System.out.println("Key : " + entry.getKey() + " Value : " + entry.getValue()); } */ for (Object key : m1.keySet()) { System.out.println("Key : " + key.toString() + " Value : " + m1.get(key)); } } } class JBT { JBT(int i, int j) { this.i = i; this.j = j; } int i, j; @Override public int hashCode() { System.out.println("Inside HashCode Method"); int k = i + j; return k; } @Override public boolean equals(Object obj) { System.out.println("Inside Equals Method"); if (i == ((JBT) obj).i && j == ((JBT) obj).j) return true; else return false; } @Override public String toString() { return String.valueOf(i).concat(String.valueOf(j)); } } ```