It's a simple question,
I have a simple HashMap of which i want to reverse the keys and values.
HashMap<Character, String> myHashMap = new HashMap<Character, String>();
myHashMap.put('a', "test one");
myHashMap.put('b', "test two");
and I want to create a new HashMap in which i put the opposites.
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
e.g. Keys "test one" & "test two" and values 'a' & 'b'.
They all are unique, yes
If you're sure that your values are unique you can iterate over the entries of your old map .
Map<String, Character> myNewHashMap = new HashMap<>();
for(Map.Entry<Character, String> entry : myHashMap.entrySet()){
myNewHashMap.put(entry.getValue(), entry.getKey());
}
Alternatively, you can use a Bi-Directional map like Guava provides and use the inverse() method :
BiMap<Character, String> myBiMap = HashBiMap.create();
myBiMap.put('a', "test one");
myBiMap.put('b', "test two");
BiMap<String, Character> myBiMapInversed = myBiMap.inverse();
As java-8 is out, you can also do it this way :
Map<String, Integer> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
Map<Integer, String> mapInversed =
map.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))
Finally, I added my contribution to the proton pack library, which contains utility methods for the Stream API. With that you could do it like this:
Map<Character, String> mapInversed = MapStream.of(map).inverseMapping().collect();
Apache commons collections library provides a utility method for inversing the map. You can use this if you are sure that the values of myHashMap are unique
org.apache.commons.collections.MapUtils.invertMap(java.util.Map map)
Sample code
HashMap<String, Character> reversedHashMap = MapUtils.invertMap(myHashMap)
If the values are not unique, the safe way to inverse the map is by using java 8's groupingBy function
Map<String, Integer> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
Map<Integer, List<String>> mapInversed =
map.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
I wrote a simpler loop that works too (note that all my values are unique):
HashMap<Character, String> myHashMap = new HashMap<Character, String>();
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
for (char i : myHashMap.keySet()) {
reversedHashMap.put(myHashMap.get(i), i);
}
To answer your question on how you can do it, you could get the entrySet from your map and then just put into the new map by using getValue as key and getKey as value.
But remember that keys in a Map are unique, which means if you have one value with two different key in your original map, only the second key (in iteration order) will be kep as value in the new map.
Iterate through the list of keys and values, then add them.
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
for (String key : myHashMap.keySet()){
reversedHashMap.put(myHashMap.get(key), key);
}
private <A, B> Map<B, A> invertMap(Map<A, B> map) {
Map<B, A> reverseMap = new HashMap<>();
for (Map.Entry<A, B> entry : map.entrySet()) {
reverseMap.put(entry.getValue(), entry.getKey());
}
return reverseMap;
}
It's important to remember that put replaces the value when called with the same key. So if you map has two keys with the same value only one of them will exist in the inverted map.
Tested with below sample snippet, tried with MapUtils, and Java8 Stream feature. It worked with both cases.
public static void main(String[] args) {
Map<String, String> test = new HashMap<String, String>();
test.put("a", "1");
test.put("d", "1");
test.put("b", "2");
test.put("c", "3");
test.put("d", "4");
test.put("d", "41");
System.out.println(test);
Map<String, String> test1 = MapUtils.invertMap(test);
System.out.println(test1);
Map<String, String> mapInversed =
test.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
System.out.println(mapInversed);
}
Output:
{a=1, b=2, c=3, d=41}
{1=a, 2=b, 3=c, 41=d}
{1=a, 2=b, 3=c, 41=d}
Use forEach introduced in Java 8
Map<Short, String> regularMap = new HashMap<>();
Map<String, Short> inversedMap = new HashMap<>();
regularMap.forEach((key, value) -> inversedMap.put(value, key));
for reverting the map, in your case:
private void reverseMap(Map<Character, String> map) {
Map<String, Character> newList = new HashMap<>();
map.forEach((key, value) -> newList.put(value, key));
System.out.println(newList);
}
or you can traverse the old hashmap
HashMap<String, Character> newList = new HashMap<String, Character>();
for (String key : list.keySet()){
newList.put(list.get(key), key);
}
For Reversing the Array of Dictionary. (If values are Unique)
private void reverseArrayMap(List<Map<String, String>> list) {
// reversing the array of dictionary
List<Map<String, String>> newList = new ArrayList<>();
Map<String, String> resDic = new HashMap<>();
for (Map<String, String> map : list) {
map.forEach((key, value) -> resDic.put(value, key));
newList.add(resDic);
}
System.out.println("Original Array of Dictionary" + list);
System.out.println("Reversed Array of Dictionary" + newList);
}
Java :
Simple approach, No need for java 8
Map<String,String> map=new HashMap<>();
Map<String,String> mapInv=new HashMap<>();
for (String key : map.keySet())
mapInv.put(map.get(key), key);
Java 8:
forEach() is a new method to iterate the elements. It is defined in Iterable and Stream interface.
Map<String,String> map=new HashMap<>();
Map<String,String> mapInv=new HashMap<>();
map.forEach((key, value) -> mapInv.put(value, key));
Kotlin :
val map: Map<String, String> = HashMap()
val mapInv: MutableMap<String?, String> = HashMap()
for (key in map.keys) mapInv[map[key]] = key
Related
I want to be able to inverse a given HashMap that has multiple keys can point to the same value.
HashMap<String, String> cities = new HashMap<String, String>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
static HashMap<String, String> inverseMap(HashMap map) {
// something that has "UK" point to both "Manchester" and "London"
// if it can be done without using any special Java 8 feature, that would be great
}
I am unsure where to start.
Do it like this. Basically, it employs a merge function which concatenates values for a duplicate key.
Create a new map
Use the values of the old map for the keys to the new
If the new map does not have a value for the key, put the value in the new map
Otherwise, concatenate the value to the old value for that key
HashMap<String, String> cities = new HashMap<String, String>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
cities.put("New York", "US");
cities.put("Chicago", "US");
Map<String,String> inverted = new HashMap<>();
for (String key : cities.keySet()) {
String newKey = cities.get(key);
String value = inverted.get(newKey);
if (value == null) {
inverted.put(newKey, key);
} else {
value = value + ", " + key;
inverted.put(newKey, value);
}
}
for (Entry<String,String> e : inverted.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
It prints
UK -> Manchester, London
US -> New York, Chicago
Since you didn't specify how to handle duplicate keys. I could also have stored it in a Map<String,List<String>>
Is this what you're looking for?
Map<String, String> cities = new HashMap<>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
Map<String, List<String>> reverseMap = new HashMap<>();
for (Entry<String, String> entry : cities.entrySet()) {
List<String> list = reverseMap.get(entry.getValue());
if (list == null) {
list = new ArrayList<>();
reverseMap.put(entry.getValue(), list);
}
list.add(entry.getKey());
}
System.out.println(reverseMap);
As multiple keys can contain the same value, you will have to be able to store multiple values per key in the inversed Map. I recommend using a Set as a value for this.
Create a map that can save a Set of Strings as values
Iterate through the original map
If the country is not found in the new map, create an entry there
Add the city to the map
This should also work with Java 7 without dependencies.
static HashMap<String, Set<String>> inverseMap(HashMap<String,String> map) {
HashMap<String,Set<String>> inversed=new HashMap<>();
for(Map.Entry<String,String> entry:map.entrySet()){
if(!inversed.containsKey(entry.getValue())){
inversed.put(entry.getValue(),new HashSet<>());
}
inversed.get(entry.getValue()).add(entry.getKey());
}
return inversed;
}
{Manchester=UK,London=UK} would turn into {UK={Manchester,London}} (order may differ).
You can look at MultiMap. It allows mapping of a single key to multiple values.
This is something has been implemented in both Google Guava
https://guava.dev/releases/19.0/api/docs/com/google/common/collect/Multimap.html
ListMultimap<String, String> multimap = ArrayListMultimap.create();
for (President pres : US_PRESIDENTS_IN_ORDER) {
multimap.put(pres.firstName(), pres.lastName());
}
for (String firstName : multimap.keySet()) {
List<String> lastNames = multimap.get(firstName);
out.println(firstName + ": " + lastNames);
}
... produces output such as:
Zachary: [Taylor]
John: [Adams, Adams, Tyler, Kennedy] // Remember, Quincy!
George: [Washington, Bush, Bush]
Grover: [Cleveland, Cleveland] // Two, non-consecutive terms, rep'ing NJ!
As in Apache Commons Collections
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/MultiValuedMap.html
MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();
map.put(key, "A");
map.put(key, "B");
map.put(key, "C");
Collection<String> coll = map.get(key);
coll will be a collection containing "A", "B", "C".
You can iterate over the entry set of your original map and use the values (country code) as key and add each key (cities) to a list:
static HashMap<String, List<String>> inverseMap(HashMap<String, String> map) {
HashMap<String, List<String>> countryToCity = new HashMap<>();
for(Map.Entry<String,String> entry: map.entrySet()){
countryToCity.computeIfAbsent(entry.getValue(), k -> new ArrayList<>()).add(entry.getKey());
}
return countryToCity;
}
I have a nested map with key as Employee name and values as another map with key as company name and value as years of experience like below
Map<String, Map<String, Integer>> map = new HashMap<>();
Map<String, Integer> innerMap1 = new HashMap<>();
innerMap1.put("INfosys", 2);
innerMap1.put("Volvo", 2);
innerMap1.put("MH", 3);
innerMap1.put("Piterion", 1);
Map<String, Integer> innerMap2 = new HashMap<>();
innerMap2.put("Tata", 2);
innerMap2.put("Bosch", 1);
innerMap2.put("Amber", 1);
innerMap2.put("E2", 1);
map.put("Rahul", innerMap1);
map.put("Amrita", innerMap2);
Now my function should return a Map with the employee name as key and total experience as value. How can I do that using java streams (in a single stream)
public Map<String, Integer> getEmployeesWithExp(Map<String, Map<String, Integer>> map) {
map.entrySet().stream().
...
return null;
}
There probably are multiple ways but you could collect the entries into a new map and reduce the values of the inner maps to integers, e.g. like this:
Map<String, Integer> result =
map.entrySet().stream()
.collect(
Collectors.toMap(e -> e.getKey(), //or Map.Entry::getKey
e -> e.getValue().values().stream()
.reduce(0, Integer::sum)));
This is the first time I tried to use streams with maps, it was quite a good exerxcise, thanks.
I failed to do it in only one stream, though. This solution features one main stream and internal streams.
I used org.apache.commons.lang3.tuple.Pair, by the way.
Map<String, Integer> result = map.entrySet().stream()
.map(entry -> Pair.of(entry.getKey(), entry.getValue().values().stream().reduce(0, Integer::sum)))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
It answered
"Amrita" → 5
"Rahul" → 8
I believe it is correct. :D
This is simple for loops used for solution :-
Map<String, Integer> finalMap = new HashMap<>();
for (Entry<String, Map<String, Integer>> entry : map.entrySet()) {
Integer exp = 0;
for (Entry<String, Integer> entry2 : entry.getValue().entrySet()) {
exp += entry2.getValue();
}
finalMap.put(entry.getKey(), exp);
}
Output- {Amrita=5, Rahul=8}
Can be done in simple way:-
Function< Map<String, Integer>,Integer> sumOfValue = (x) -> x.values().stream().mapToInt(Integer::intValue).sum();
stringMapMap.entrySet().stream().collect(Collectors.toMap(
e-> e.getKey(), e-> sumOfValue.apply(e.getValue())
));
This question already has answers here:
Reverse HashMap keys and values in Java
(12 answers)
Closed 5 years ago.
I have a Map(map1) whose key is another map(map2) and value is string.
The value of map1 has several duplicate, so I must group them and set as key in another map3 whose value has to be map2.
eg:
map1 { [[a,b],xyz], [[c,d],wrt] , [[e,f],xyz] , [[r,m],xyz] }
output should be :
map3 { [ xyz,[ [a,b],[e,f],[r,m] ] ] , [ wrt,[ [c,d] ]
can i obtain like this ?
try MultiValueMap from (commons-collections) library
Map map1 = new HashMap<String[], String>();
map1.put(new String[]{"a", "b"}, "xyz");
map1.put(new String[]{"c", "d"}, "wrt");
map1.put(new String[]{"e", "f"}, "xyz");
map1.put(new String[]{"c", "d"}, "xyz");
MultiValueMap map2 = new MultiValueMap();
for(String[] key: map1.keySet()) {
String value = map1.get(key);
map2.add(value, key);
}
// now you got map2 as you want
NO, Though you can declare it but while using it, it may allow same keys(human readable). Because Map do not override Object's equals method your key comparison will be on JVM's object level (it may be different for objects with same values in them).
You can use the stream API to solve it:
Map<String, List<Map<String, String>>> map3 = map.entrySet().stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey,
Collectors.toList())));
Explanation:
The entries will be grouped by its values (groupingBy(Entry::getValue).
But the values should not be a list of entries so the downstream is necessary. This is the list of keys of the original map.
It is not clear from your post what do you mean by map2, but let's assume you would like to use each Map.Entry<String, String> entry from map2 for each key for map1.
The following code is Java 7, it's a bit verbose. It could be done shorter with Java 8 streams, I guess.
public class MapReverser {
private Map<Map.Entry<String, String>, String> map1 = new HashMap<>();
private Map<String, String> map2 = new LinkedHashMap<>();
private void prepareMaps() {
map2.put("a", "b");
map2.put("c", "d");
map2.put("e", "f");
map2.put("r", "m");
String[] valueArray = { "xyz", "wrt", "xyz", "xyz" };
int i = 0;
for (Map.Entry<String, String> entry : map2.entrySet()) {
map1.put(entry, valueArray[i]);
i++;
}
}
public Map<String, List<Map.Entry<String, String>>> reverse() {
Map<String, List<Map.Entry<String, String>>> reversedMap = new HashMap<>();
for (Map.Entry<Map.Entry<String, String>, String> entry : map1.entrySet()) {
List<Map.Entry<String, String>> reversedMapValue = reversedMap.get(entry.getValue());
if (reversedMapValue == null) {
reversedMapValue = new ArrayList<>();
}
reversedMapValue.add(entry.getKey());
reversedMap.put(entry.getValue(), reversedMapValue);
}
return reversedMap;
}
private void printResult(Map<String, List<Map.Entry<String, String>>> reversedMap) {
for (Map.Entry<String, List<Map.Entry<String, String>>> entry : reversedMap.entrySet()) {
System.out.println("Key: \n" + entry.getKey());
System.out.println("Values:");
List<Map.Entry<String, String>> valuesList = entry.getValue();
for (Map.Entry<String, String> value : valuesList) {
System.out.println(value );
}
System.out.println("\n");
}
}
public static void main(String[] args) {
MapReverser mapReverser = new MapReverser();
mapReverser.prepareMaps();
Map<String, List<Map.Entry<String, String>>> reversedMap = mapReverser.reverse();
mapReverser.printResult(reversedMap);
}
}
I have two maps:
Map<Integer, String> mapOne = {(1,"a"), (2, "b")};
Map<Integer, Double> mapTwo = {(1,10.0), (2,20.0)};
and I want to combine this maps into one by Integer value, so the result map is
Map<String, Double> mapResult = {("a",10.0), ("b",20.0)};
Is there any way to do this easier than iterate over entry set?
Assuming that the keys of the two maps match and that the maps have the same number of entries, with Java 8 you can write it in one line with:
Map<String, Double> map = mapOne.entrySet().stream()
.collect(toMap(e -> e.getValue(),
e -> mapTwo.get(e.getKey())));
So you start from the first map and create a new map where the keys are the values of mapOne and the values are the corresponding values in mapTwo.
Technically this is somewhat equivalent to iterating over the entry set of the first map though.
Note: requires import static java.util.stream.Collectors.toMap;
Looks like only iteration:
#Test
public void testCollection() {
Map<Integer, String> mapOne = new HashMap<Integer, String>();
mapOne.put(1, "a");
mapOne.put(2, "b");
Map<Integer, Double> mapTwo = new HashMap<Integer, Double>();
mapTwo.put(1, 10.0);
mapTwo.put(2, 20.0);
Map<String, Double> mapResult = new HashMap<String, Double>();
Set<Integer> keySet = mapOne.keySet();
keySet.retainAll(mapTwo.keySet());
for (Integer value : keySet) {
mapResult.put(mapOne.get(value), mapTwo.get(value));
}
System.out.println(mapResult);
}
If the maps were the same type, you could use a putAll(), but since you are changing the key value pairs, it looks like you are going to have to iterate over each integer, get() from each map, then put(mapOneVal,mapTwoVal)
for(int i=0;i<max;i++){
String key = mapOne.get(i);
Double val = maptwo.get(i);
if(key!=null && val!=null)
map3.put(key,val);
}
In Java, how does one get the values of a HashMap returned as a List?
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put (1, "Mark");
map.put (2, "Tarryn");
List<String> list = new ArrayList<String>(map.values());
for (String s : list) {
System.out.println(s);
}
Assuming you have:
HashMap<Key, Value> map; // Assigned or populated somehow.
For a list of values:
List<Value> values = new ArrayList<Value>(map.values());
For a list of keys:
List<Key> keys = new ArrayList<Key>(map.keySet());
Note that the order of the keys and values will be unreliable with a HashMap; use a LinkedHashMap if you need to preserve one-to-one correspondence of key and value positions in their respective lists.
Basically you should not mess the question with answer, because it is confusing.
Then you could specify what convert mean and pick one of this solution
List<Integer> keyList = Collections.list(Collections.enumeration(map.keySet()));
List<String> valueList = Collections.list(Collections.enumeration(map.values()));
Collection Interface has 3 views
keySet
values
entrySet
Other have answered to to convert Hashmap into two lists of key and value. Its perfectly correct
My addition: How to convert "key-value pair" (aka entrySet)into list.
Map m=new HashMap();
m.put(3, "dev2");
m.put(4, "dev3");
List<Entry> entryList = new ArrayList<Entry>(m.entrySet());
for (Entry s : entryList) {
System.out.println(s);
}
ArrayList has this constructor.
Solution using Java 8 and Stream Api:
private static <K, V> List<V> createListFromMapEntries (Map<K, V> map){
return map.values().stream().collect(Collectors.toList());
}
Usage:
public static void main (String[] args)
{
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
List<String> result = createListFromMapEntries(map);
result.forEach(System.out :: println);
}
If you only want it to iterate over your HashMap, no need for a list:
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put (1, "Mark");
map.put (2, "Tarryn");
for (String s : map.values()) {
System.out.println(s);
}
Of course, if you want to modify your map structurally (i.e. more than only changing the value for an existing key) while iterating, then you better use the "copy to ArrayList" method, since otherwise you'll get a ConcurrentModificationException. Or export as an array:
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put (1, "Mark");
map.put (2, "Tarryn");
for (String s : map.values().toArray(new String[]{})) {
System.out.println(s);
}
If you wanna maintain the same order in your list, say:
your Map looks like:
map.put(1, "msg1")
map.put(2, "msg2")
map.put(3, "msg3")
and you want your list looks like
["msg1", "msg2", "msg3"] // same order as the map
you will have to iterate through the Map:
// sort your map based on key, otherwise you will get IndexOutofBoundException
Map<String, String> treeMap = new TreeMap<String, String>(map)
List<String> list = new List<String>();
for (treeMap.Entry<Integer, String> entry : treeMap.entrySet()) {
list.add(entry.getKey(), entry.getValue());
}
I use usually map.values() to get values, then convert them to list
let say you have this Hashmap:
HashMap<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
map.put("three", 3);
You can get values from the map, then convert them to a list in one code line like that:
List<Integer> values = map.values().stream()
.collect(Collectors.toList());