1. 概述
在Java中操作数据结构时,一个常见的场景是将HashMap
中的值和键提取出来,并组织成一个ArrayList
。本教程将探讨实现这一目标的各种实用方法。
2. 问题介绍
首先,我们创建一个HashMap
对象作为示例:
static final HashMap<String, String> DEV_MAP;
static {
DEV_MAP = new HashMap<>();
DEV_MAP.put("Kent", "Linux");
DEV_MAP.put("Eric", "MacOS");
DEV_MAP.put("Kevin", "Windows");
DEV_MAP.put("Michal", "MacOS");
DEV_MAP.put("Saajan", "Linux");
}
如上代码所示,我们使用静态块初始化了一个HashMap
。该映射包含一些开发者及其主要使用的操作系统。
当我们讨论从映射中提取键值对列表时,会根据特定需求出现不同的情况。
一种情况是,给定索引i
,原始映射中元素keyList[i]
和valueList[i]
之间存在直接关联。简单来说,在原始映射的上下文中,对应索引的列表元素之间存在相关性:
Map:
k1 -> v1
k2 -> v2
k3 -> v3
index : 0, 1, 2
KeyList : k1, k2, k3
ValueList: v1, v2, v3
第二种情况相对简单,目标是从提供的映射中提取键列表和值列表,而不考虑原始键值对的关联。
本教程将涵盖这两种情况。为了确保清晰性和验证,我们将使用单元测试断言来验证每种方法结果的正确性。
3. 使用HashMap的keySet()
和values()
方法
首先,让我们解决更简单的情况:忽略元素之间的关联,从DEV_MAP
中获取键和值列表。
Map
接口提供了两个方法,可以帮助我们快速解决这个问题:
-
keySet()
- 获取映射中所有键作为Set
-
values()
- 返回所有值作为Collection
我们可以将Set
和Collection
传递给ArrayList
的构造函数,以获得预期的列表对象,例如获取键列表:
List<String> keyList = new ArrayList<>(DEV_MAP.keySet());
assertEquals(Lists.newArrayList("Kent", "Eric", "Kevin", "Michal", "Saajan"), keyList);
然而,运行测试可能会失败,因为HashMap
不维护其条目的顺序。换句话说,列表中元素的顺序无法预测。
接下来,我们可以使用AssertJ的containsExactlyInAnyOrder()
方法来检查元素并忽略它们的顺序:
assertThat(keyList).containsExactlyInAnyOrder("Kent", "Eric", "Kevin", "Michal", "Saajan");
同样,我们也可以通过Map.values()
获取值列表:
List<String> valueList = new ArrayList<>(DEV_MAP.values());
assertThat(valueList).containsExactlyInAnyOrder("Linux", "MacOS", "Windows", "MacOS", "Linux");
4. 获取关联的键值列表
现在,我们转向另一种情况:获取一个键列表和一个值列表,其中keyList[i]
和valueList[i]
在映射中保持关联。
首先,我们创建一个方法来验证这两个列表是否符合预期:
void assertKeyAndValueList(List<String> keyList, List<String> valueList) {
assertThat(keyList).containsExactlyInAnyOrder("Kent", "Eric", "Kevin", "Michal", "Saajan");
assertThat(valueList).containsExactlyInAnyOrder("Linux", "MacOS", "Windows", "MacOS", "Linux");
for (int i = 0; i < keyList.size(); i++) {
assertThat(DEV_MAP).containsEntry(keyList.get(i), valueList.get(i));
}
}
如上述方法所示,除了验证两个列表应包含所需的元素外,我们还确保它们在相同索引处的对应元素在DEV_MAP
中有关联。
解决这个问题的一种方法是遍历映射的条目,将每个条目的键和值填充到预先初始化的两个列表中:
List<String> keyList = new ArrayList<>();
List<String> valueList = new ArrayList<>();
for (Map.Entry<String, String> entry : DEV_MAP.entrySet()) {
keyList.add(entry.getKey());
valueList.add(entry.getValue());
}
assertKeyAndValueList(keyList, valueList);
如果运行测试,它会通过。所以这种方法可以完成任务。
如果我们使用Java 8或更高版本,可以使用forEach()
调用和lambda表达式来替换for
循环,提高代码可读性:
List<String> keyList = new ArrayList<>();
List<String> valueList = new ArrayList<>();
DEV_MAP.forEach((k, v) -> {
keyList.add(k);
valueList.add(v);
});
assertKeyAndValueList(keyList, valueList);
5. 总结
在这篇文章中,我们首先讨论了从HashMap
获取键列表和值列表的两种情况。随后,我们探讨了如何在每种情况下解决问题。
如往常一样,这些示例的完整源代码可在GitHub上找到:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-collections-maps-3。