覆盖 ArrayList <String>中的哈希映射中的值



假设我有一个带有字符串键和整数值的哈希映射:

map = {cat=1, kid=3, girl=3, adult=2, human=5, dog=2, boy=2}

我想通过将此信息放入另一个 HashMap 来切换键和值。我知道 HashMap 不能有重复的键,所以我尝试将信息放入带有 Integer 的 HashMap 中,以便将映射到字符串 ArrayList 的键,以便我有可能将一个整数映射到多个字符串:

swap = {1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}

我尝试了以下代码:

HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (String x : map.keySet()) {
for (int i = 0; i <= 5; i++) {
ArrayList<String> list = new ArrayList<String>();
if (i == map.get(x)) {
list.add(x);
swap.put(i, list);
}
}
}

我的代码的唯一区别是我没有将数字 5 硬编码到我的索引中;我有一个方法可以在原始 HashMap 中找到最高的整数值并使用它。我知道它工作正常,因为即使我在那里对 5 进行硬编码,我也会得到相同的输出,我只是没有包含它以节省空间。

我的目标是能够使用任何一组数据进行这种"反转",否则我可以对值进行硬编码。我从上面的代码中得到的输出是这样的:

swap = {1=[cat], 2=[boy], 3=[girl], 5=[human]}

如您所见,我的问题是值 ArrayList 只保留放入其中的最后一个字符串,而不是收集所有这些字符串。如何使 ArrayList 存储每个字符串,而不仅仅是最后一个字符串?

使用 Java 8,您可以执行以下操作:

Map<String, Integer> map = new HashMap<>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
Map<Integer, List<String>> newMap = map.keySet()
.stream()
.collect(Collectors.groupingBy(map::get));
System.out.println(newMap);

输出将是:

{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}

您正在为每次迭代重新创建 arrayList,但我无法找到使用该逻辑的方法,这是一个好方法,无需检查最大整数:

for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
List<String> get = swap.get(value);
if (get == null) {
get = new ArrayList<>();
swap.put(value, get);
}
get.add(key);
}

最好的方法是遍历原始地图的键集

此外,您还必须确保目标映射中的任何键都存在 List:

for (Map.Entry<String,Integer>  inputEntry : map.entrySet())
swap.computeIfAbsent(inputEntry.getValue(),()->new ArrayList<>()).add(inputEntry.getKey());

这显然不是最好的解决方案,但通过交换内部和外部循环来解决问题的方式与您相同,如下所示。

Map<String, Integer> map = new HashMap<String, Integer>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (Integer value = 0; value <= 5; value++) {
ArrayList<String> list = new ArrayList<String>();
for (String key : map.keySet()) {
if (map.get(key) == value) {
list.add(key);
}
}
if (map.containsValue(value)) {
swap.put(value, list);
}
}

输出

{1=[猫], 2=[成人, 狗, 男孩], 3=[孩子, 女孩], 5=[人类]}

我能想到的最好的方法是在现有地图上使用Map.forEach方法,在新地图上使用Map.computeIfAbsent方法:

Map<Integer, List<String>> swap = new HashMap<>();
map.forEach((k, v) -> swap.computeIfAbsent(v, k -> new ArrayList<>()).add(k));

作为旁注,您可以使用菱形运算符<>来创建新映射(调用映射的构造函数时无需重复键和值的类型,因为编译器会推断它们(。

作为第二个旁注,对于泛型参数类型和实际类型,最好使用接口类型而不是具体类型。这就是为什么我分别使用ListMap而不是ArrayListHashMap

使用groupingBy就像雅各布的回答一样,但为了更好的性能,Map.entrySet,正如鲍里斯所建议的那样:

// import static java.util.stream.Collectors.*
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(groupingBy(Entry::getValue, mapping(Entry::getKey, toList())));

这使用了另外两种Collectors方法:mappingtoList

如果不是这两个帮助程序函数,解决方案可能如下所示:

Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
groupingBy(
Entry::getValue,
Collector.of(
ArrayList::new,
(list, e) -> {
list.add(e.getKey());
},
(left, right) -> { // only needed for parallel streams
left.addAll(right);
return left;
}
)
)
);

或者,使用toMap代替groupingBy

Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
toMap(
Entry::getValue,
(e) -> new ArrayList<>(Arrays.asList(e.getKey())),
(left, right) -> {
left.addAll(right);
return left;
}
)
);

它覆盖了将它们添加到已经重新设置的数组列表中的值。试试这个:

HashMap<Integer, ArrayList<String>> swapedMap = new HashMap<Integer, ArrayList<String>>();
for (String key : map.keySet()) {
Integer swappedKey = map.get(key);
ArrayList<String> a = swapedMap.get(swappedKey);
if (a == null) {
a = new ArrayList<String>();
swapedMap.put(swappedKey, a)
}
a.add(key);
}

我没有时间运行它(对不起,现在没有 Java 编译器(,但应该差不多可以:)

您可以从Map的 java-8 中使用新的merge方法:

Map<Integer, List<String>> newMap = new HashMap<>();
map.forEach((key, value) -> {
List<String> values = new ArrayList<>();
values.add(key);
newMap.merge(value, values, (left, right) -> {
left.addAll(right);
return left;
});
});

最新更新