Convert Set<Map.Entry<K, V>> to HashMap<K, V>
Asked Answered
H

8

52

At one point in my code, I created a Set<Map.Entry<K, V>> from a map. Now I want to recreate the same map form, so I want to convert the HashSet<Map.Entry<K, V>> back into a HashMap<K, V>. Does Java have a native call for doing this, or do I have to loop over the set elements and build the map up manually?

Hemato answered 19/4, 2013 at 15:57 Comment(4)
How did you created Set from Map, using key or value or custom logic? besides there is no native method for HashSet to HashMap. You need to iterate and use some logic as while putting into HashMap how you choose key and value.Suit
@Paul thanks now question makes clear senseSuit
I don't think Map.Entry is meant to be used this way. Does the implementation used by HashMap override hashCode and equals for example?Congruity
a Set of Map.Entry IS a Map (logically). The implementation of TreeSet/HashSet is to use a tree/hash map to backup the set. Meaning Set.add(X) == Map.put(X,whatever);Tourism
S
23

There is no inbuilt API in java for direct conversion between HashSet and HashMap, you need to iterate through set and using Entry fill in map.

one approach:

Map<Integer, String> map = new HashMap<Integer, String>();
    //fill in map
    Set<Entry<Integer, String>> set = map.entrySet();

    Map<Integer, String> mapFromSet = new HashMap<Integer, String>();
    for(Entry<Integer, String> entry : set)
    {
        mapFromSet.put(entry.getKey(), entry.getValue());
    }

Though what is the purpose here, if you do any changes in Set that will also reflect in Map as set returned by Map.entrySet is backup by Map. See javadoc below:

Set<Entry<Integer, String>> java.util.Map.entrySet()

Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations.

Suit answered 19/4, 2013 at 16:5 Comment(0)
A
89

Simpler Java-8 solution involving Collectors.toMap:

Map<Integer, String> mapFromSet = set.stream()
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

An IllegalStateException will be thrown if duplicate key is encountered.

Ainslie answered 30/8, 2015 at 10:18 Comment(1)
what aboug NPEs if any value is null?Sussna
S
23

There is no inbuilt API in java for direct conversion between HashSet and HashMap, you need to iterate through set and using Entry fill in map.

one approach:

Map<Integer, String> map = new HashMap<Integer, String>();
    //fill in map
    Set<Entry<Integer, String>> set = map.entrySet();

    Map<Integer, String> mapFromSet = new HashMap<Integer, String>();
    for(Entry<Integer, String> entry : set)
    {
        mapFromSet.put(entry.getKey(), entry.getValue());
    }

Though what is the purpose here, if you do any changes in Set that will also reflect in Map as set returned by Map.entrySet is backup by Map. See javadoc below:

Set<Entry<Integer, String>> java.util.Map.entrySet()

Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations.

Suit answered 19/4, 2013 at 16:5 Comment(0)
S
9

Fairly short Java 8 solution. Can cope with duplicate keys.

    Map<Integer, String> map = new HashMap<>();
    //fill in map
    Set<Map.Entry<Integer, String>> set = map.entrySet();
    Map<Integer, String> mapFromSet = set.stream().collect(Collectors.toMap(Entry::getKey,
      Entry::getValue,
      (a,b)->b));

Edit: thanks to shmosel who deserves more credit than I do for this

Sydney answered 23/2, 2015 at 9:51 Comment(6)
No. Your BiConsumer does not combine the Maps.Ferrotype
In the above code, the BiConsumer's accept method is never called. I would have preferred to use null but the collect method stupidly throws an NPE in that case.Sydney
@mikeyreilly, the documentation does not explicitly say that the combiner is never called for sequential stream. Thus you just violate the contract of the method.Ainslie
@TagirValeev A fair point, but I'd rather violate the contract than write code that does nothingSydney
Ok, you guys, I feel bad about breaking the contract. I've amended my solution.Sydney
You could just as easily use Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b).Pacifist
A
9

As of Guava 19 you can use ImmutableMap.copyOf(Iterable<Map.Entry<K,V>>)

Appetite answered 4/7, 2017 at 11:34 Comment(0)
C
7

Java 9+ without suppressed warning

As of the Java 9 version, the collection interfaces provide a bunch of handy static methods allowing you to do a lot of things inline. There is a built-in solution using a static method Map.ofEntries(Map.Entry... entries) which creates an immutable map.

Map<Integer,String> map = Map.ofEntries(
        new SimpleEntry<>(1, "one"), 
        new SimpleEntry<>(2, "two"), 
        new SimpleEntry<>(3, "three"));

Note that also the Map.entry(K, V) method is available which makes everything less verbose.

Map<Integer,String> map2 = Map.ofEntries(
        Map.entry(1, "one"), 
        Map.entry(2, "two"), 
        Map.entry(3, "three"));

Assuming you have Set<Map.Entry<K, V>>, you need an array to use the method using Set#toArray(T[]). Explicit casting is required due to forbidden generic array creation, which is the only drawback of this solution.

Set<Map.Entry<Integer,String>> set = ...
Entry<Integer, String>[] entries = set.<Entry<Integer,String>>toArray(Entry[]::new);
Map<Integer,String> map = Map.ofEntries(entries);

Java 8

Java 8 brings Stream API which is pretty straightforward using Collectors.toMap(keyFn, valueFn).

Set<Map.Entry<Integer,String>> set = ...
Map<Integer,String> map = set.stream()
    .collect(Collectors.toMap(
            Entry::getKey,
            Entry::getValue));

To avoid erroneous behavior in the presence of two Map.Entry with the same keys that are forbidden in dictionaries, use Collectors.toMap(keyFn, valueFn, mergeFn). Return either first or second depending on if you want to keep the first value found for a certain key or the latest one.

Map<Integer,String> map = set.stream()
    .collect(Collectors.toMap(
            Entry::getKey, 
            Entry::getValue,
            (first, second) -> first));  // if you need the 1st occurrence
         // (first, second) -> second)); // if you need the last occurrence

Java 7 and older

All, you can do is a procedural for-each iteration as the existing answer describes.

Constriction answered 7/3, 2021 at 23:20 Comment(0)
L
6

As of 2018, there are some toMap utility in Apache libraries, but unfortunately most of them take an array instead of Set, so you need to do Set#toArray() first.

I left Guava out for @Neil's answer which is arguably the best.

Apache Commons Collections' MapUtils.putAll

Map<K, V> map = MapUtils.putAll(new HashMap<K, V>(), entrySet.toArray());

Apache Commons Collections' MapUtils.populateMap

HashMap<K, V> map = new HashMap<>();
MapUtils.populateMap(map, entrySet, Map.Entry::getKey, Map.Entry::getValue);

Apache Commons Lang's ArrayUtils.toMap

Map<Object, Object> map = ArrayUtils.toMap(entrySet.toArray());

// to recover the type...
@SuppressWarnings("unchecked")
Map<K, V> typedMap = (Map<K, V>)(Map<?, ?>)map;

Java 9's Map.ofEntries

 // convert to array and recover the type...
 @SuppressWarnings("unchecked")
 Map<K, V> map = Map.ofEntries(entrySet.toArray(new Map.Entry[entrySet.size()]));

 // Somehow this makes javac happy
 IntFunction<Map.Entry<K, V>[]> c = Map.Entry[]::new;
 Map<K, V> map2 = Map.ofEntries(entrySet.toArray(c.apply(entrySet.size())));

 // Using an overload available since Java 11
 Map<K, V> map3 = Map.ofEntries(entrySet.toArray(c));

 // You need to copy again if you want a mutable one
 Map<K, V> hashmap = new HashMap<>(map);
Lunarian answered 6/3, 2018 at 9:16 Comment(0)
F
1

In Java 8 with correct combiner

Map<Integer, String> map = new HashMap<>();
//fill in map
Set<Map.Entry<Integer, String>> set = map.entrySet();

Map<Integer, String> mapFromSet =set.stream().collect(HashMap::new,(t, u) -> t.put(u.getKey(), u.getValue()), 
(Map mapToReturn, Map otherMap) ->
    {
        otherMap.entrySet().forEach((Map.Entry entry) -> {
            mapToReturn.put(entry.getKey(),entry.getValue());
        });
        return mapToReturn;}););
Ferrotype answered 29/8, 2015 at 17:53 Comment(0)
S
0

I wanted to try this for a practice problem. My issue was that I needed to convert two "maps" into "sets" so I can use the math operations that come with sets, like Union, Intersection, and Difference. While learning about "Map.Entry" I found I could convert the "Maps" into "Sets" perform the math operation as required. However, I still needed to convert my return value back into a "Map" per problem requirements. I found that I could do this if you typecast the objects back into the original variables. My end result is a "Map" that includes only the Intersections of both Maps.

public static Map<String, Integer> intersect(Map<String, Integer> map_one, Map<String, Integer> map_two)
{
    Set<Map.Entry<String, Integer>> set_one = map_one.entrySet(); //Map one now a set
    Set<Map.Entry<String, Integer>> set_two = map_two.entrySet(); //Map two now a set
    set_one.retainAll(set_two); //a math intersection operation set_one and set_two whose contents are the intersection of the two sets
    Map<String, Integer> map_retur = new HashMap<>(); //instantiation  of a new Map object
    Iterator i = set_one.iterator(); //instantiation of an iterator
      while(i.hasNext())
      {
         Map.Entry me = (Map.Entry)i.next(); // instantiation of Map.Entry as me and convert i into that Map.Entry
         map_retur.put((String)me.getKey(), (Integer)me.getValue()); // store each key/value pair into the Map as required
      }
    return map_retur; // returned value is a map with only the intersections
}

Thanks Ref: https://www.tutorialspoint.com/java/java_mapentry_interface.html For how to do the iterations in the for each loop. However, it does not include the typecasting I have shown here.

Scut answered 22/8, 2022 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.