Java Hashmap教程
原文: https://javabeginnerstutorial.com/core-java-tutorial/java-hashmap/
Java HashMap是基于哈希表的Map实现。 这就是为什么面试官总是要求HashMap和HashTable之间存在差异的原因。 HashMap基本上等于HashTable,但以下两个区别除外。
- 当
HashTable同步时,HashMap不同步。 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中增加值。
重点
HashMap不会按Map中的元素顺序提供任何保证(均值顺序会随时间变化)。HashMap为获得和设置操作提供恒定时间性能(如果使用正确的哈希算法)。- 迭代集合所需的时间与
HashMap的“容量”(它可以容纳的元素)和大小(它当前容纳的元素)成比例。 - 如果迭代性能更为重要,那么建议不要将初始容量设置得太高而将负载系数设置得太低。 由于性能与初始容量和负载系数成正比。
- 容量是哈希表中的存储桶数。
- 初始容量(默认值为 16)只是创建哈希表时的容量。
- *负载因子(默认值 .75)*是衡量哈希表在自动增加其容量之前的填充程度的度量。
- 当哈希表中的条目数超过负载因子与当前容量的乘积时,哈希表将被重映射(即内部数据结构将被重建)。
- 使用“
Collections.synchronizedMap()”方法使映射同步。 - 由
HashMap类返回的迭代器为“故障快速”。 HashMap由数组(Key)和LinkedList(Value)支持。HashMap使用hashcode(使用键)来标识应在HashMap中放置或检索对象的确切位置。- 最后,
HashCode返回后备数组中的确切位置(索引)。 - 支持数组的大小固定。 因此,只要数组已满(此映射中的键数达到其阈值)。 将创建一个具有新容量的新数组,并将所有元素添加到该新数组中。
- 在两种情况下(添加和检索对象)都将使用
HashCode,而在任何情况下均可以使用或可以不使用equals()方法。 HashMap中Key的最佳候选者是具有正确实现Equals和Hashcode方法的不可变类(示例:字符串类)。- 更好的哈希码和
equals方法实现是HashMap的更好性能。 - 这样,所有
String和原始类型的包装类将是HashMap中键的理想选择。
什么是重新哈希
每个HashMap都有预定义的大小(初始容量),以及在需要时(超过阈值限制时)增加此大小(负载系数)的逻辑。
示例:
使用以下配置创建HashMap
初始容量:16(默认初始容量)
负载系数:.75(默认负载系数)
在给定的HashMap中添加第 13 个元素后,超过给定HashMap的阈值限制,系统将创建一个新的后备键集数组(此数组的大小将是前一个数组的两倍)。 系统将不得不再次计算确切的存储桶,应放置上一个存储桶中的元素,并将旧HashMap中的所有元素复制到新的HashMap中。 整个过程称为重新哈希,因为会再次为每个元素计算Hashcode。
因为超时的HashMap可能会被重新散布并且顺序可能会发生变化。
HashMap的示例
在此示例中,您将学到以下几点
- 如何迭代映射
- 迭代映射的不同方法
HashCode和Equals何时被调用。 (请特别注意HashCode的输出和equals方法调用)
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;@Overridepublic int hashCode() {System.out.println("Inside HashCode Method");int k = i + j;return k;}@Overridepublic boolean equals(Object obj) {System.out.println("Inside Equals Method");if (i == ((JBT) obj).i && j == ((JBT) obj).j)return true;elsereturn false;}@Overridepublic String toString() {return String.valueOf(i).concat(String.valueOf(j));}}
