开发者

How to swap keys and values in a Map elegantly

I already know how to do it 开发者_如何转开发the hard way and got it working - iterating over entries and swapping "manually". But i wonder if, like so many tasks, this one can be solved in a more elegant way.

I have read this post, unfortunately it does not feature elegant solutions. I also have no possibility to use any fancy Guava BiMaps or anything outside the jdk (project stack is already defined).

I can assume that my map is bijective, btw :)


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


If you don't have a choice to use a third party library, I don't consider the following code so ugly (though some scripting languages do have elegant ways of doing it):

//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;
}


The standard API / Java runtime doesn't offer a bi-directional map, so the only solution is to iterate over all entries and swap them manually.

What you can do is create a wrapper class which contains two maps and which does a dual put() internally so you have fast two views on the data.

[EDIT] Also, thanks to open source, you don't have to include a third party library, you can simply copy the classes you need into your own project.


Maps are not like lists, which can be reversed by swapping head with tail.

Objects in maps have a computed position, and using the value as key and the key as value would requiere to re-compute the storage place, essentialy building another map. There is no elegant way.

There are, however, bidirectional maps. Those may suit your needs. I'd reconsider using third-party libraries.


There are some jobs that can be simplified to a certain point and no more. This may be one of them!

If you want to do the job using Java collections apis only then brute force is the way to go - it will be quick (unless the collection is huge) and it will be an obvious piece of code.


As a hint to answer https://stackoverflow.com/a/42091477/8594421

This only works, if the map is not a HashMap and does not contain duplicate values.

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

throws an exception

java.lang.IllegalStateException: Duplicate key

if there are values more than once.

The solution:

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);


If you had access to apache commons-collections, you could have used MapUtils.invertMap.

Note: The behaviour in case of duplicated values is undefined.

(Replying to this as this is the first google result for "java invert map").


Java stream API provides nice set of APIs that would help you with this.

If the values are unique then the below would work. When I mean values, I mean the V in the Map<K, V>.

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

If the values are not unique, then use below:

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

Thanks Nikita and FreyaZ. Posting as new answer as there were so many edit queues for Nikita's Answer


This will work for duplicate values in the map also, but not for HashMap as values.

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()); 
    } 
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜