如何优雅地交换Map中的键和值

时间:2010-12-14 07:55:10

标签: java collections hashmap

我已经知道如何以艰难的方式完成它并使其工作 - 迭代条目并“手动”交换。但是我想知道,像这么多任务一样,这个问题能否以更优雅的方式解决。

我已阅读this post,遗憾的是它没有优雅的解决方案。我也没有可能使用任何花哨的Guava BiMaps或jdk之外的任何东西(已经定义了项目堆栈)。

我可以假设我的地图是双射的,顺便说一下:)

9 个答案:

答案 0 :(得分:27)

Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

答案 1 :(得分:22)

如果您没有选择使用第三方库,我不认为以下代码如此丑陋(尽管某些脚本语言确实有优雅的方法):

//map must be a bijection in order for this to work properly
public static <K,V> HashMap<V,K> reverse(Map<K,V> map) {
    HashMap<V,K> rev = new HashMap<V, K>();
    for(Map.Entry<K,V> entry : map.entrySet())
        rev.put(entry.getValue(), entry.getKey());
    return rev;
}

答案 2 :(得分:9)

标准API / Java运行时不提供双向映射,因此唯一的解决方案是迭代所有条目并手动交换它们。

你可以做的是创建一个包装器类,它包含两个映射,并在内部执行双put(),因此您可以快速获得两个数据视图。

[编辑]此外,由于开源,您不必包含第三方库,您只需将所需的类复制到您自己的项目中。

答案 3 :(得分:3)

地图不像列表,可以通过尾部交换来反转。

地图中的对象具有计算位置,并且使用值作为键,并且将键作为值来重新计算存储位置,从而构建另一个地图。没有优雅的方式。

然而,有双向地图。这些可能适合您的需求。我会重新考虑使用第三方库。

答案 4 :(得分:2)

有些工作可以简化到某一点,而不是更多。这可能就是其中之一!

如果你想使用Java集合apis来完成这项工作,那么蛮力就是要走的路 - 它会很快(除非集合很大)并且它将是一段显而易见的代码。

答案 5 :(得分:1)

作为回答的提示 https://stackoverflow.com/a/42091477/8594421

仅当地图不是HashMap并且不包含重复值时才有效。

Map<String,String> newMap = oldMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

抛出异常

  

java.lang.IllegalStateException:Duplicate key

如果有多个值。

解决方案:

HashMap<String,String> newMap = new HashMap<>();

for(Map.Entry<String,String> entry : oldMap.entrySet())
        newMap.put(entry.getValue(), entry.getKey());

// Add inverse to old one
oldMap.putAll(newMap);

答案 6 :(得分:1)

如果您有权访问apache commons-collection,则可以使用MapUtils.invertMap

注意:值重复的情况是不确定的。

(回复此,因为这是Google对“ java反向映射”的第一个结果)。

答案 7 :(得分:0)

这也适用于地图中的重复值,但不适用于HashMap作为值。

package Sample;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Sample {
    public static void main(String[] args) { 
        Map<String,String> map = new HashMap<String,String>(); 
        Map<String, Set<String> > newmap = new HashMap<String, Set<String> >(); 

        map.put("1", "a"); 
        map.put("2", "a"); 
        map.put("3", "b"); 
        map.put("4", "b"); 
        System.out.println("before Reversing \n"+map.toString()); 

        for (Map.Entry<String, String> entry : map.entrySet()) 
        { 
            String oldVal = entry.getValue(); 
            String oldKey = entry.getKey(); 
            Set<String> newVal = null; 

            if (newmap.containsKey(oldVal)) 
            { 
                newVal = newmap.get(oldVal); 
                newVal.add(oldKey); 
            } 
            else 
            { 
                newVal= new HashSet<>(); 
                newVal.add(oldKey); 
            } 
            newmap.put(oldVal, newVal); 
        } 
        System.out.println("After Reversing \n "+newmap.toString()); 
    } 
}

答案 8 :(得分:0)

Java 流 API 提供了一组很好的 API,可以帮助您解决这个问题。

如果值是唯一的,那么下面的方法会起作用。当我指的是值时,我指的是 Map 中的 V。

Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet()
                                  .stream()
                                  .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

如果值不是唯一的,则使用以下内容:

Map<Integer, List<String>> swapped = map.entrySet()
                                        .stream()
                                        .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

感谢 Nikita 和 FreyaZ。发布新答案,因为 Nikita 的答案有很多编辑队列