你好!在本课中,我们将对 Java HashMap 这个知识点进行深入复习。之前,我们研究了数据结构,即元数据本身所存储的地方。在数组或 ArrayList/LinkedList 中,我们存储了一些元素。但如果我们的任务稍加改变该如何呢?
假想下列任务:创建一个包含 100 人的列表,存储每个人的姓名和护照号码。原理上这并不难。例如,你可以将两者都填充到一个字符串中,然后创建下列字符串的列表:
![HashMap:这是哪种映射?- 2]()
键被提取到一个 Set 中,这个知识点我们还没涉及。它的特殊之处在于不能包含重复的元素。现在最重要的是要记住,所有键的列表都可以从 HashMap 中检索到单独的集合中。
在示例中,我们将值保存到普通的 ArrayList 中。
控制台输出:
"艾米莉亚 阿基拉, 4211 717171"。
但该解决方案有两个缺点。首先,我们可能需能按护照号码进行搜索。鉴于这种信息存储格式,这种搜索会有问题。
其次,我们可以轻而易举地使用相同的护照号码创建两个不同的人。这是该解决方案最严重的缺点。这种情况绝对不允许:两个人的护照号码是相同的。
我们可以使用一种新数据结构:映射。这种数据结构称为“关联数组”,但是这个术语很少使用。我们常将其称为“字典”或“映射”。:)
该数据结构与我们之前考虑的数据结构有什么根本不同吗?
最重要一项事实是,Map 中的数据以键值对的方式存储。任何事情都可以用键和值表示:数字、字符串或其他类的对象。
今天,我们将学习 Map 类的常见实现:Java HashMap。

那么,关于 Java 中的 HashMap,我们需要了解些什么?
创建起来非常轻松:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
}
在这里,我们创建了一个字典,将元素以“数字-字符串”对的形式存储。数字将作为键,字符串作为值。我们还指定键类型 (Integer) 和值类型 (String)。
为什么?
首先,HashMap 键始终是唯一的。这非常适合我们使用,因为可以将护照号码用作键,从而避免重复出现。值将是一个带有全名的字符串(不同的人可以有相同的名字;我们不考虑这种情况)。
将一个新对添加到 HashMap,如下所示:
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames);
}
}
为此,我们使用方法 put()。此外,HashMap 重写了 toString() 方法,因此可以在控制台上显示。输出如下所示:
{212133=布里奇特·洛根, 8082771=唐纳德·特朗普, 162348=伊万很棒}
现在让我们验证键是否真的是唯一的?
我们尝试使用已在映射中使用的键添加一个新元素:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
passportsAndNames.put(162348, "Albert Kent");// This key has already been used
System.out.println(passportsAndNames);
}
输出:
{212133=布里奇特·洛根, 8082771=唐纳德·特朗普, 162348=阿尔伯特·肯特}
如你所见,与键 162348 关联的先前值已被覆盖。
我们使用术语“键”是有原因的。HashMap 中的值是使用键访问的,反之则不行。无法使用值获取键,因为值可能不是唯一的。
从 HashMap 获取或删除元素时可以清楚地看到这一点:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
String lidiaName = passportsAndNames.get(212133);
System.out.println(lidiaName);
passportsAndNames.remove(162348);
System.out.println(passportsAndNames);
}
要从字典中获取一个值或删除一个对,我们必须将与该值对应的唯一键传递给 get() 和 remove()。与数组和列表不同,Java 中的 HashMap 没有数字下标:值是使用键访问的。
控制台输出:
布里奇特·洛根
{212133=布里奇特·洛根, 8082771=唐纳德·特朗普}
ArrayList 和 LinkedList 类可用于检查列表是否包含任何特定元素。
Java HashMap 可以做此工作。更重要的是,我们可以为该对的两个成员执行此操作:这就是 containsKey()(检查键)和 containsValue()(检查值)方法的用途。
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames.containsKey(11111));
System.out.println(passportsAndNames.containsValue("Donald John Trump"));
}
输出:
false
true
Java 中 HashMap 的另一个方便特性是,你可以获得所有键和所有值的单独列表。
这可以通过 keySet() 和 values() 方法来实现。
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
Set keys = passportsAndNames.keySet();
System.out.println("Keys: " + keys);
ArrayList<String> values = new ArrayList<>(passportsAndNames.values());
System.out.println("Values: " + values);
}
}

键:[212133, 8082771, 162348]
值:[布里奇特·洛根, 唐纳德·特朗普, 伊万很棒]
size() 和 clear() 方法的作用与我们之前讨论过的结构完全相同:第一个方法返回字典中当前元素的编号,第二个方法删除所有元素。
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames.size());
passportsAndNames.clear();
System.out.println(passportsAndNames);
}
输出:
3
{}
要检查我们的 HashMap 中是否至少有一个元素,我们可以使用 isEmpty() 方法:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
if (!passportsAndNames.isEmpty()) {
System.out.println(passportsAndNames);
}
}
输出:
{212133=布里奇特·洛根, 8082771=唐纳德·特朗普, 162348=伊万很棒}
现在我们只会在初步检查后输出到控制台。:)
另一个有趣的地方是两个 Map 可以合并为一个 Map 。这是使用 putAll() 方法完成的。我们在第一个 HashMap 上调用它,将第二个 HashMap 作为参数传递,然后将第二个中的元素添加到第一个即可:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
HashMap<Integer, String> passportsAndNames2 = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
passportsAndNames2.put(917352, "Clifford Patrick");
passportsAndNames2.put(925648, "Mitchell Salgado");
passportsAndNames.putAll(passportsAndNames2);
System.out.println(passportsAndNames);
}
输出:
{917352=克利福德·帕特里克, 212133=布里奇特·洛根, 8082771=唐纳德·特朗普, 925648=米切尔·萨尔加多, 162348=伊万很棒}
passportsAndNames2 中的所有对已复制到 passportsAndNames。
现在考虑一个更复杂的例子。具体而言,即时在循环中迭代 HashMap。
for (Map.Entry<Integer, String> entry: passportsAndNames.entrySet()) {
System.out.println(entry);
}
Map.Entry 类表示字典中的键值对。entrySet() 方法返回 HashMap 中所有对的列表。因为我们的映射由这些 Map.Entry 对组成,所以我们迭代的是对,而不是单独的键或值。
输出:
212133=布里奇特·洛根
8082771=唐纳德·特朗普
162348=伊万很棒
另外,不要忘记研究 HashMap 的官方 Oracle 文档。
GO TO FULL VERSION