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

Java 如何优雅地交换地图中的键和值,java,collections,hashmap,Java,Collections,Hashmap,我已经知道如何通过艰苦的方式完成它,并使它工作起来——迭代条目并“手动”交换。但我想知道,像许多任务一样,这个问题能否以更优雅的方式解决 不幸的是,我读过,它没有优雅的解决方案。我也不可能使用任何花哨的番石榴Bimap或jdk之外的任何东西(项目堆栈已经定义) 我可以假设我的映射是双射的,顺便说一句:)映射不像列表,可以通过交换头部和尾部来反转 地图中的对象有一个计算的位置,使用值作为键和键作为值需要重新计算存储位置,基本上是构建另一个地图。没有优雅的方式 然而,存在双向映射。这些可能适合你的需

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

不幸的是,我读过,它没有优雅的解决方案。我也不可能使用任何花哨的番石榴Bimap或jdk之外的任何东西(项目堆栈已经定义)


我可以假设我的映射是双射的,顺便说一句:)

映射不像列表,可以通过交换头部和尾部来反转

地图中的对象有一个计算的位置,使用值作为键和键作为值需要重新计算存储位置,基本上是构建另一个地图。没有优雅的方式


然而,存在双向映射。这些可能适合你的需要。我会重新考虑使用第三方库。

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

您可以做的是创建一个包装类,它包含两个映射,并在内部执行双
put()
,这样您就可以快速查看数据


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

有些作业可以简化到某一点,仅此而已。这可能是其中之一


如果你想使用java集合API来完成这项工作,那么蛮力就是方法——它会很快(除非集合很大),它将是一个明显的代码。

如果你没有选择使用第三方库,我不认为下面的代码如此丑陋。(尽管有些脚本语言确实有优雅的方式来实现这一点):

//映射必须是双射才能正常工作
公共静态哈希映射反向(映射映射){
HashMap rev=新的HashMap();
对于(Map.Entry:Map.entrySet())
rev.put(entry.getValue(),entry.getKey());
返回版本;
}
Map Map=newhashmap();
Map swapped=Map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue,Map.Entry::getKey));
作为回答的提示

这仅适用于映射不是HashMap且不包含重复值的情况

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

java.lang.IllegalStateException:重复密钥

如果存在不止一次的值

解决方案:

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);
HashMap newMap=newhashmap();
对于(Map.Entry:oldMap.entrySet())
newMap.put(entry.getValue(),entry.getKey());
//给旧的加反
oldMap.putAll(newMap);

如果您有权访问apache commons集合,则可以使用

注:重复值情况下的行为未定义


(回答这个问题,因为这是“java反转映射”的第一个google结果。)

这也适用于映射中的重复值,但不适用于
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()); 
    } 
}
包装样品;
导入java.util.HashMap;
导入java.util.HashSet;
导入java.util.Map;
导入java.util.Set;
公共类样本{
公共静态void main(字符串[]args){
Map Map=newhashmap();
Map newmap=newhashmap();
地图放置(“1”、“a”);
地图放置(“2”、“a”);
地图放置(“3”、“b”);
地图放置(“4”、“b”);
System.out.println(“反转前\n”+map.toString());
对于(Map.Entry:Map.entrySet())
{ 
字符串oldVal=entry.getValue();
字符串oldKey=entry.getKey();
设置newVal=null;
if(newmap.containsKey(oldVal))
{ 
newVal=newmap.get(oldVal);
newVal.add(oldKey);
} 
其他的
{ 
newVal=newhashset();
newVal.add(oldKey);
} 
newmap.put(oldVal,newVal);
} 
System.out.println(“反转后\n”+newmap.toString());
} 
}

Java stream API提供了一套很好的API,可以帮助您实现这一点

如果值是唯一的那么下面的方法就行了。当我指的是值时,我指的是地图中的V

Map Map=newhashmap();
Map 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())));
Map swapped=Map.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue,Collectors.mapping(Map.Entry::getKey,Collectors.toList());

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

添加额外的实用程序库并没有真正改变项目“堆栈”的方式(比如)如果可能的话,改变你正在使用的UI框架将是一个巨大的挑战。我敦促你重新考虑你对使用番石榴的反对意见。你所需要的只是一个单循环和一行代码,这是简单而简洁的。IMHO.Java不是一种函数式语言。谢谢大家,有这么多重叠和简洁的答案,你真的很难选择哪一种一个可以接受。我想我会和Aaron Digulla一起提供一个包装解决方案。很棒的解决方案。这将是使用Java 8添加流之后的最佳答案。如果原始映射中的值在多个键中的位置会怎样?交换时会丢失值&得到一个ExceptionMap swapped=map.entrySet().stream().collect(Collectors.groupingBy(Map.Entry::getValue,Collectors.mapping(Map.Entry::getKey,Collectors.toList());@FreyaZ-omg,这会让我失败一整天!
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()); 
    } 
}
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())));