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?
- 53,024
- 17
- 128
- 180
- 7,097
- 24
- 65
- 105
-
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. – harsh Apr 19 '13 at 16:02
-
@Paul thanks now question makes clear sense – harsh Apr 19 '13 at 16:03
-
1I don't think `Map.Entry` is meant to be used this way. Does the implementation used by `HashMap` override `hashCode` and `equals` for example? – Paul Bellora Apr 19 '13 at 16:06
-
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); – Christian Bongiorno Apr 19 '13 at 16:42
7 Answers
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.
- 92,683
- 18
- 210
- 320
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.
- 7,322
- 3
- 29
- 31
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
- 1
- 1
- 5,773
- 1
- 26
- 20
-
-
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. – mikeyreilly Aug 30 '15 at 08:07
-
1@mikeyreilly, [the documentation](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.function.Supplier-java.util.function.BiConsumer-java.util.function.BiConsumer-) does not explicitly say that the combiner is never called for sequential stream. Thus you just violate the contract of the method. – Tagir Valeev Aug 30 '15 at 10:21
-
@TagirValeev A fair point, but I'd rather violate the contract than write code that does nothing – mikeyreilly Aug 31 '15 at 12:48
-
Ok, you guys, I feel bad about breaking the contract. I've amended my solution. – mikeyreilly Aug 31 '15 at 13:00
-
2You could just as easily use `Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b)`. – shmosel Jan 06 '16 at 07:07
These are some toMap utility in common libraries, but unfortunately none of them support Set directly so you need to do Set#toArray() first. (I left out Guava for Neil's answer which is arguably the best)
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;
Commons Collections' MapUtils.putAll
Map<K, V> map = MapUtils.putAll(new HashMap<K, V>(), entrySet.toArray());
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[0]));
// You need to copy again if you want a mutable one
Map<K, V> hashmap = new HashMap<>(map);
- 1,921
- 1
- 32
- 29
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 in order 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 on the presence of two Map.Entry with the same keys which is 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)); // or '-> second' if you need the latests
Java 7 and older
All, you can do is a procedural for-each iteration as the existing answer descirbes.
- 35,162
- 12
- 84
- 155
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;}););
- 7,782
- 18
- 52
- 83