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;
@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));
}
}