Hashmap inside a hashmap not removing the keys using remove? - java

This is my list.
list = Stream.of(
"06|20|1",
"11|20|2",
"11|20|2",
"07|207|6",
"11|207|2",
"07|207|6",
).collect(Collectors.toList());
I have a hashmap such as:
HashMap<String, HashMap<String, String>> hashMap = new HashMap<>();
HashMap<String, String> newHash = new HashMap<>();
And my code is
for (String line : list) {
String key, value, priority;
key = line.split("\\|", -1)[1];
value = line.split("\\|", -1)[0];
priority = line.split("\\|", -1)[2];
if (hashMap.containsKey(key)) {
HashMap<String, String> getPriority = hashMap.get(key);
Map.Entry<String, String> entry = getPriority.entrySet().iterator().next();
String oldKey = entry.getKey();
String previousPrior = getPriority.get(oldKey);
if (Integer.parseInt(priority) > Integer.parseInt(previousPrior)) {
getPriority.remove(oldKey);
getPriority.put(value,priority);
hashMap.put(key, getPriority);
}
} else {
newHash.put(value, priority);
System.out.println(newhas);
hashMap.put(key, newhas);
}
}
I want to have the have the key with highest priority only such as:
{20={11=2},207={07=6}}
as 11 and 7 has the highest valaues in 20 and 207.
But i am getting all values in the inner hashmap.

How about using streams instead?
Map<String, Map<String, String>> map = list.stream()
.map(line -> line.split("\\|"))
.sorted(Comparator.comparingInt(line -> Integer.parseInt(line[2])))
.collect(Collectors.toMap(
line -> line[1],
line -> Map.of(line[0], line[2]),
(low, high) -> high));
Ideone Demo

For each new key that you insert in your Map variable in the else condition of your code you need to create new HashMap to insert it along with the new key.
What you are doing is using the same newhas variable for all the keys in your Map variable.
So change this
else {
newhas.put(value, priority);
System.out.println(newhas);
Map.put(key, newhas);
}
to
else {
newHas = new HashMap<>();
newhas.put(value, priority);
System.out.println(newhas);
Map.put(key, newhas);
}
Ideone Demo

Related

Streams for iterating through Map of List of Map in Java

I have a map that contains Integer as key and (List of Map of String as key and boolean as the value) as value. Map<Int, List<Map<String, Boolean>>>, I want to populate a set that has Int as key of the outer map based on condition.
MyService.java
public Set<Integer> getValue(String input){
Map<String, Boolean> in1 = new HashMap<>();
in1.put("test_1", true);
Map<String, Boolean> in2 = new HashMap<>();
in2.put("test_2", false);
Map<String, Boolean> in3 = new HashMap<>();
in2.put("test_3", false);
Map<String, Boolean> in4 = new HashMap<>();
in2.put("test_4", true);
List<Map<String, Boolean>> l1 = new ArrayList<>();
l1.add(in1);
l1.add(in2);
List<Map<String, Boolean>> l2 = new ArrayList<>();
l2.add(in3);
l2.add(in4);
Map<Integer, List<Map<String,Boolean>>> map = new HashMap();
map.put(123, l1);
map.put(345, l2);
Set<Integer> result = new HashSet<>();
for(Map.Entry<Integer, List<Map<String, Boolean>>> entry : map.entrySet()){
for(Map<String, Boolean> m: entry.getValue() ){
if(m.containsKey(input) && m.get(input) == true){
result.add(entry.getKey());
}
}
}
return result;
}
So, basically I want to iterate from first the exterior map to get the internal map and then iterate the internal map to check if the input is present and add it to a set. How can I do this using java 8 streams?
I tried with for loop, but I will like to replace it using Java streams.
This produced the same results as your code in a test that passed "test_1", etc. into it.
map.entrySet().stream()
.filter(entry -> entry.getValue().stream()
.anyMatch(m -> m.getOrDefault(input, false)))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());

Hashmap. Find key(s) which have same values between two hashmaps

Lets consider we have two hashmaps as below:
HashMap<String, Integer> map1 = new HashMap<>();
map1.put("vishal", 10);
map1.put("sachin", 30);
map1.put("vaibhav", 20);
HashMap<String, Integer> map2 = new HashMap<>();
map2.put("Raja", 10);
map2.put("John", 30);
map2.put("Krishna", 20);
The "vaibhav" from map1 and "krishna" from map2 have the same values.
I need to find the keys from both the maps, which have the same values. In this case, "vaibhav" and "Krishna".
Thanks.
Group by values and store keys in list:
Stream.of(map1.entrySet(), map2.entrySet())
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(
Map.Entry::getKey,
Collectors.toList()
)
));
It will create:
{20=[vaibhav, Krishna], 10=[vishal, Raja], 30=[sachin, John]}
UPDATE
Other approach
Map<Integer, List<String>> collect = new HashMap<>();
map1.entrySet().forEach(e -> collect
.computeIfAbsent(e.getValue(), k -> new ArrayList<>())
.add(e.getKey()));
map2.entrySet().forEach(e -> collect
.computeIfAbsent(e.getValue(), k -> new ArrayList<>())
.add(e.getKey()));
You can improve the time complexity to O(n + m) where n is the size of first map and m is the size of the second map.
We can achieve this by making values as keys and keys as values.
Steps:
Iterate over each map.
Store all current map values in a new map and collect all keys who have that value in a list and put the current value with this list in the new map.
Now, iterate over any of the new map collections and get the common keys and it's respective values for printing.
Snippet:
private static void showCommonValueKeys(HashMap<String, Integer> map1,HashMap<String, Integer> map2){
Map<Integer,List<String>> map1Collect = flipKeyValue(map1);
Map<Integer,List<String>> map2Collect = flipKeyValue(map2);
for(Map.Entry<Integer,List<String>> m : map1Collect.entrySet()){
int key = m.getKey();
if(map2Collect.containsKey(key)){
System.out.println("For value " + key);
System.out.println("First map keys: " + m.getValue().toString());
System.out.println("Second map keys: " + map2Collect.get(key).toString());
System.out.println();
}
}
}
private static Map<Integer,List<String>> flipKeyValue(HashMap<String, Integer> map){
Map<Integer,List<String>> mapCollect = new HashMap<>();
for(Map.Entry<String,Integer> m : map.entrySet()){
String key = m.getKey();
int val = m.getValue();
mapCollect.putIfAbsent(val,new ArrayList<>());
mapCollect.get(val).add(key);
}
return mapCollect;
}
Demo: https://onlinegdb.com/SJdcpbOXU
This can be achieved through two for loops with a complexity of n*m, where n.m is the size of each map.
Map<String, String> map1 = new HashMap<>();
map1.put("santhosh", "1");
map1.put("raja", "2");
map1.put("arun", "3");
Map<String, String> map2 = new HashMap<>();
map2.put("kumar", "1");
map2.put("mani", "1");
map2.put("tony", "3");
for (Map.Entry<String, String> entry1 : map1.entrySet()) {
String key1 = entry1.getKey();
String value1 = entry1.getValue();
for (Map.Entry<String, String> entry2 : map2.entrySet()) {
String key2 = entry2.getKey();
String value2 = entry2.getValue();
if (value1 == value2) {
System.out.println(key1 + " " + key2);
}
}
Thanks.

Reversing key value pairs in a HashMap

I have a data structure as follows:
Map<String,ArrayList<String>> graph = new HashMap<String,ArrayList<String>>();
This is essentially a hash map which puts string values as keys and stores array list of strings in the value for the keys.
Now I am trying to reverse the key value pattern to make value the key and key the value. The way I am doing it is as follows:
private Map<String,ArrayList<String>> reverseAdjList(Map<String,ArrayList<String>> adjList){
Map<String,ArrayList<String>> tGraph = new HashMap<String,ArrayList<String>>();
for (Map.Entry<String, ArrayList<String>> entry : adjList.entrySet()) {
String key = entry.getKey();
ArrayList<String> values = new ArrayList<>();
values.add(key);
ArrayList<String> value = entry.getValue();
for(String v:value){
if(tGraph.containsKey(v)){
values.addAll(tGraph.get(v));
}
tGraph.put(v, values);
}
}
return tGraph;
}
So this works for me in reversing the hash map keys values pattern for small data set however when I try it on a larger dataset I run into
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.addAll(ArrayList.java:579)
at GraphProcessor.reverseAdjList(GraphProcessor.java:67)
at GraphProcessor.SCC(GraphProcessor.java:135)
at GraphProcessor.<init>(GraphProcessor.java:50)
at GraphProcessor.main(GraphProcessor.java:250)
I know this is a very naïve and wrong approach to do it, what is a better and correct way to do it?
There's a bug in your code:
for (Map.Entry<String, ArrayList<String>> entry : adjList.entrySet()) {
String key = entry.getKey();
ArrayList<String> values = new ArrayList<>(); // Wrong place for this variable.
values.add(key);
ArrayList<String> value = entry.getValue();
for(String v:value){
if(tGraph.containsKey(v)){
values.addAll(tGraph.get(v));
}
tGraph.put(v, values);
}
}
The local variable values should be in the nested for loop, otherwise values are accumulated for all later new key v and will cost a lot of memory if your dataset is large, it should be:
private Map<String, ArrayList<String>> reverseAdjList(Map<String, List<String>> adjList) {
Map<String, ArrayList<String>> tGraph = new HashMap<>();
for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
String key = entry.getKey();
List<String> value = entry.getValue();
for (String v : value) {
ArrayList<String> values = new ArrayList<>();
values.add(key);
if (tGraph.containsKey(v)) {
values.addAll(tGraph.get(v));
}
tGraph.put(v, values);
}
}
return tGraph;
}
But actually you don't need to create a new List instance for each inner for step, try the following code with JDK 1.8:
private Map<String, List<String>> reverseMap(Map<String, List<String>> adjList) {
Map<String, List<String>> tGraph = new HashMap<>();
for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
for (String value : entry.getValue()) {
tGraph.computeIfAbsent(value, v -> new ArrayList<>()).add(entry.getKey()); // Updated according comment from #shmosel
}
}
return tGraph;
}
If you're using older version of jdk, you can try:
private Map<String, List<String>> reverseMap(Map<String, List<String>> adjList) {
Map<String, List<String>> tGraph = new HashMap<>();
for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
for (String value : entry.getValue()) {
List<String> newValues = tGraph.get(value);
if (newValues == null) {
newValues = new ArrayList<>();
tGraph.put(value, newValues);
}
newValues.add(entry.getKey());
}
}
return tGraph;
}
Hope this could be helpful :-)

how to merge more than one hashmaps also sum the values of same key in java

ı am trying to merge more than one hashmaps also sum the values of same key,
ı want to explain my problem with toy example as follows
HashMap<String, Integer> m = new HashMap<>();
HashMap<String, Integer> m2 = new HashMap<>();
m.put("apple", 2);
m.put("pear", 3);
m2.put("apple", 9);
m2.put("banana", 6);
ı tried putall
m.putAll(m2);
output is as follows
{banana=6, apple=9, pear=3}
but its result is not true for this problem.
ı want to output as
{banana=6, apple=11, pear=3}
how can ı get this result in java?
If you are using Java 8, you can use the new merge method of Map.
m2.forEach((k, v) -> m.merge(k, v, (v1, v2) -> v1 + v2));
This is a very nice use case for Java 8 streams. You can concatentate the streams of entries and then collect them in a new map:
Map<String, Integer> combinedMap = Stream.concat(m1.entrySet().stream(), m2.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.summingInt(Map.Entry::getValue)));
There are lots of nice things about this solution, including being able to make it parallel, expanding to as many maps as you want and being able to trivial filter the maps if required. It also does not require the orginal maps to be mutable.
This method should do it (in Java 5+)
public static <K> Map<K, Integer> mergeAndAdd(Map<K, Integer>... maps) {
Map<K, Integer> result = new HashMap<>();
for (Map<K, Integer> map : maps) {
for (Map.Entry<K, Integer> entry : map.entrySet()) {
K key = entry.getKey();
Integer current = result.get(key);
result.put(key, current == null ? entry.getValue() : entry.getValue() + current);
}
}
return result;
}
Here's my quick and dirty implementation:
import java.util.HashMap;
import java.util.Map;
public class MapMerger {
public static void main(String[] args) {
HashMap<String, Integer> m = new HashMap<>();
HashMap<String, Integer> m2 = new HashMap<>();
m.put("apple", 2);
m.put("pear", 3);
m2.put("apple", 9);
m2.put("banana", 6);
final Map<String, Integer> result = (new MapMerger()).mergeSumOfMaps(m, m2);
System.out.println(result);
}
public Map<String, Integer> mergeSumOfMaps(Map<String, Integer>... maps) {
final Map<String, Integer> resultMap = new HashMap<>();
for (final Map<String, Integer> map : maps) {
for (final String key : map.keySet()) {
final int value;
if (resultMap.containsKey(key)) {
final int existingValue = resultMap.get(key);
value = map.get(key) + existingValue;
}
else {
value = map.get(key);
}
resultMap.put(key, value);
}
}
return resultMap;
}
}
Output:
{banana=6, apple=11, pear=3}
There are some things you should do (like null checking), and I'm not sure if it's the fastest. Also, this is specific to integers. I attempted to make one using generics of the Number class, but you'd need this method for each type (byte, int, short, longer, etc)
ı improve Lucas Ross's code. in stead of enter map by one by in function ı give all maps one times to function with arraylist of hashmap like that
public HashMap<String, Integer> mergeAndAdd(ArrayList<HashMap<String, Integer>> maplist) {
HashMap<String, Integer> result = new HashMap<>();
for (HashMap<String, Integer> map : maplist) {
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer current = result.get(key);
result.put(key, current == null ? entry.getValue() : entry.getValue() + current);
}
}
return result;
}
}
it works too. thanks to everbody
Assume that you have many HashMaps: Map<String,Integer> map1, map2, map3;
Then you can use Java 8 streams:
Map<String,Integer> combinedMap = Stream.of(map1, map2, map3)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.summingInt(Map.Entry::getValue)));
If the key exists, add to it's value. If not insert.
Here is a simple example which merges one map into another:
Foo oldVal = map.get(key);
if oldVal == null
{
map2.put(key, newVal);
}
else
{
map2.put(key, newVal + oldVal);
}
Obviously you have to loop over the first map so you can process all of it's entries but that's trivial.
Something like this should work:
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String map1_key = entry.getKey();
int map1_value = entry.getValue();
//check:
if(map2.get(map1_key)!=null){
int map2_value = map2.get(map1_key);
//merge:
map3.put(map1_key,map1_value+map2_value);
}else{
map3.put(map1_key,map1_value);
}
}
for (Map.Entry<String, Integer> entry2 : map2.entrySet()) {
String map2_key = entry2.getKey();
int map2_value = entry2.getValue();
//check:
if(map1.get(map2_key)!=null){
int map1_value = map1.get(map2_key);
//merge:
map3.put(map2_key,map1_value+map2_value);
}else{
map3.put(map2_key,map2_value);
}
}

What's the best way to sum two Map<String,String>?

I have the following maps.
Map<String,String> map1= new HashMap<String, String>(){{
put("no1","123"); put("no2","5434"); put("no5","234");}};
Map<String,String> map1 = new HashMap<String, String>(){{
put("no1","523"); put("no2","234"); put("no3","234");}};
sum(map1, map2);
I want to join them to one, summing up similar keyed values together. What;s the best way I could do it using java 7 or guava libraries ?
expected output
Map<String, String> output = { { "no1" ,"646"}, { "no2", "5668"}, {"no5","234"}, {"no3","234" } }
private static Map<String, String> sum(Map<String, String> map1, Map<String, String> map2) {
Map<String, String> result = new HashMap<String, String>();
result.putAll(map1);
for (String key : map2.keySet()) {
String value = result.get(key);
if (value != null) {
Integer newValue = Integer.valueOf(value) + Integer.valueOf(map2.get(key));
result.put(key, newValue.toString());
} else {
result.put(key, map2.get(key));
}
}
return result;
}
try this
Map<String, List<String>> map3 = new HashMap<String, List<String>>();
for (Entry<String, String> e : map1.entrySet()) {
List<String> list = new ArrayList<String>();
list.add(e.getValue());
String v2 = map2.remove(e.getKey());
if (v2 != null) {
list.add(v2);
}
map3.put(e.getKey(), list);
}
for (Entry<String, String> e : map2.entrySet()) {
map3.put(e.getKey(), new ArrayList<String>(Arrays.asList(e.getValue())));
}
Java 8 introduces Map.merge(K, V, BiFunction), which makes this easy if not particularly concise:
Map<String, String> result = new HashMap<>(map1);
//or just merge into map1 if mutating it is okay
map2.forEach((k, v) -> result.merge(k, v, (a, b) ->
Integer.toString(Integer.parseInt(a) + Integer.parseInt(b))));
If you're doing this repeatedly, you're going to be parsing and creating a lot of strings. If you're generating the maps one at a time, you're best off maintaining a list of strings and only parsing and summing once.
Map<String, List<String>> deferredSum = new HashMap<>();
//for each map
mapN.forEach((k, v) ->
deferredSum.computeIfAbsent(k, x -> new ArrayList<String>()).add(v));
//when you're done
Map<String, String> result = new HashMap<>();
deferredSum.forEach((k, v) -> result.put(k,
Long.toString(v.stream().mapToInt(Integer::parseInt).sum())));
If this summing is a common operation, consider whether using Integer as your value type makes more sense; you can use Integer::sum as the merge function in that case, and maintaining lists of deferred sums would no longer be necessary.
Try this
Map<String,String> map1= new HashMap<String, String>(){{
put("no1","123"); put("no2","5434"); put("no5","234");}};
Map<String,String> map2 = new HashMap<String, String>(){{
put("no1","523"); put("no2","234"); put("no3","234");}};
Map<String,String> newMap=map1;
for(String a:map2.keySet()){
if(newMap.keySet().contains(a)){
newMap.put(a,""+(Integer.parseInt(newMap.get(a))+Integer.parseInt(map2.get(a))));
}
else{
newMap.put(a,map2.get(a));
}
}
for(String k : newMap.keySet()){
System.out.println("key : "+ k + " value : " + newMap.get(k));
}

Categories

Resources