issue with hashmap getting concurrent modification exception - java

I am getting the below error while using map and performing some remove.How to avoid this ?
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$EntryIterator.next(HashMap.java:834)
at java.util.HashMap$EntryIterator.next(HashMap.java:832)
Map<FormField, Object> ItemMap = domainItem.getValues();
for (Map.Entry<FormField, Object> ValMap : ItemMap.entrySet()) {
List<Field> groupIdList = Mapper.getGroupId(groupFieldId);
for (Field field : groupIdList) {
ItemMap.put(new FormField(field), domainItem.getDomainItemLinkId());
}
ItemMap.remove(ValMap.getKey());
}

Update:
Use Iterator and ConcurrentHashMap to avoid this scenario
Following won't throw exception
Map<Integer, String> map = new ConcurrentHashMap<Integer, String>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");
map.put(4, "d");
for (Iterator<Integer> keys = map.keySet().iterator(); keys.hasNext();) {
Integer key = keys.next();
String val = map.get(key);
map.remove(key);
}
or use another map while iterating and at the end copy it to source
for example:
Map<Integer, String> dummy = new HashMap<Integer, String>();
map.put(1, "a");
map.put(2, "b");
map.put(3, "c");
map.put(4, "d");
dummy.putAll(map);
for (Iterator<Integer> keys = dummy.keySet().iterator(); keys.hasNext();) {
Integer key = keys.next();
String val = map.get(key);
map.remove(key);
}
System.out.println(map);

A Map is sorted by the keys in the key-value pairs. When you add or remove elements from the Map while you are iterating through them, the program essentially loses track of "where" in the Map it is.
To get around this, try making a separate, temporary transfer Map. There is also a class called Iterator which might suit your needs.

One way to avoid this issue is to iterate over a copy.
for (Map.Entry<FormField, Object> ValMap :
new HashMap<FormField, Object>(ItemMap).entrySet()) {

can be done without a copy of the map, execute this example and take a look at the code:
public static void main(String[] args)
{
System.out.println("creating map ...");
Map<String, String> dummyMap = new HashMap<>();
while (dummyMap.size() < 10)
{ dummyMap.put(String.valueOf(new Random().nextInt()), String.valueOf(new Random().nextInt())); }
System.out.println("start, map size: " + dummyMap.size() + ", keys=" + dummyMap.keySet());
System.out.print("going to remove: ");
for (Iterator<String> keys = dummyMap.keySet().iterator(); keys.hasNext(); )
{
final String key = keys.next();
// delete map entries per random
if(new Random().nextInt(3)>1)
{
System.out.print(key+" ");
keys.remove();
}
}
System.out.print("\n");
System.out.println("done, map size: " + dummyMap.size() + ", keys=" + dummyMap.keySet());
}
and take a look at this similar question.
HTH,

Related

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.

update existing key of HashMap

I have HashMap < Integer,String> of length 3:
1=>"Value1"
2=>"Value2"
3=>"Value3"
Now I want to decrease all keys by 1(if key>1):
Output:
1=>"Value2"
2=>"Value3"
What I am trying
for (e in hashMap.entries) {
val entry = e as Map.Entry<*, *>
var keyPos = (entry.key as Int)
if (keyPos != -1) {
if (keyPos > 1) {
keyPos = keyPos - 1
if (keyPos != -1) {
hashMap.put(keyPos, entry.value as String?)
}
}
}
}
But its not giving required output.
How to make it work without Concurrency exception.
An alternative is to use mapKeys extension function, which allows you to redefine the key for a map entry:
fun main() {
val originalMap = mapOf(1 to "value1", 2 to "value2", 3 to "value3")
val updatedMap = originalMap
.mapKeys {
if (it.key > 1) {
it.key - 1
} else {
it.key
}
}
println(updatedMap) // prints: {1=value2, 2=value3}
}
Note that this will not update the map in-place, but it will create a new one. Also note that:
In case if any two entries are mapped to the equal keys, the value of the latter one will overwrite the value associated with the former one.
This means that in case two keys are conflicting, in general you can't know which one will "win" (unless you're using a LinkedHashMap, which preserves insertion order).
A more general approach would be to:
decrement all keys
filter out all non-positive keys
This will require 2 full iterations, though, (unless you use Sequences, which are lazily evaluated):
fun main() {
val originalMap = mapOf(1 to "value1", 2 to "value2", 3 to "value3")
val updatedMap = originalMap
.mapKeys {
it.key - 1
}.filter {
it.key > 0
}
println(updatedMap)
}
EDIT: here is the same with Java 7 compatible code (without streams)
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "test1");
hashMap.put(2, "test2");
hashMap.put(3, "test3");
Map<Integer, String> yourNewHashMap = new HashMap<>();
for (final Map.Entry<Integer, String> entry : hashMap.entrySet()) {
if (entry.getKey() != 1) { // make sure index 1 is omitted
yourNewHashMap.put(entry.getKey() - 1, entry.getValue()); // decrease the index for each key/value pair and add it to the new map
}
}
Old answer with streams:
As a new Map Object is okay for you, I would go with the following stream:
comments are inline
HashMap<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "test1");
hashMap.put(2, "test2");
hashMap.put(3, "test3");
// use this
Map<Integer, String> yourNewHashMap = hashMap.entrySet().stream()
.filter(es -> es.getKey() != 1) // make sure index 1 is omitted
.map(es -> new AbstractMap.SimpleEntry<Integer, String>(es.getKey() - 1, es.getValue())) // decrease the index for each key/value pair
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)); // create a new map
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
// Populate the HashMap
map.put(1, "Value1");
map.put(2, "Value2");
map.put(3, "Value3");
System.out.println("Original HashMap: "
+ map);
decreaseAllKeysByOne(map);
}
private static void decreaseAllKeysByOne(HashMap<Integer, String> map) {
// Add your condition (if key>1)
HashMap<Integer, String> newMap = new HashMap<>();
map.remove(1);
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
int i = 1;
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
newMap.put(i, entry.getValue());
i++;
}
System.out.println("Modified HashMap: "
+ newMap);
}
Output :
Original HashMap: {1=Value1, 2=Value2, 3=Value3}
Modified HashMap: {1=Value2, 2=Value3}

Printing HashMap of HashMaps : Map.Entry or java8

I have a method which returns out hashmap of hashmaps
HashMap<String, HashMap<String, String>> mapofmaps = abcd(<String>, <Integer>);
I am trying to print the the outer hashmap using the following code
for (Entry<String, HashMap<String, String>> entry : mapofmaps.entrySet()) {
String key = entry.getKey();
System.out.println(key);
HashMap<String, String> value = entry.getValue();
System.out.println(key + "\t" + value);
}
I would like to iterate through the inner map. What would be the entryset variable there (??? in the code).
for (Entry<String, HashMap<String, String>> entry : mapofmaps.entrySet()) {
String key = entry.getKey();
System.out.println(key);
for(Entry<String, HashMap<String, String>> entry : ????.entrySet()){
HashMap<String, String> value = entry.getValue();
System.out.println(key + "\t" + value);
}}
Is my logic for printing the hashmaps correct? or is there a better way to do that?
It will be entry.getValue().entrySet()
so
for(Entry<String, String> innerEntry : entry.getValue().entrySet()){
then you can use
String key = innerEntry.getKey();
String value = innerEntry.getValue();
It is worth mentioning that, this can also be done Using java 8 Streams and lambda expressions
HashMap<String, HashMap<String, String>> mapofmaps = new HashMap<>();
HashMap<String,String> map1 = new HashMap<>();
map1.put("map1_key1", "map1_value1");
HashMap<String,String> map2 = new HashMap<>();
map2.put("map2_key1", "map2_value1");
mapofmaps.put("map1", map1);
mapofmaps.put("map2", map2);
// To print the keys and values
mapofmaps.forEach((K,V)->{ // mapofmaps entries
V.forEach((X,Y)->{ // inner Hashmap enteries
System.out.println(X+" "+Y); // print key and value of inner Hashmap
});
});
mapofmaps.forEach((K,V) : This expects a lambda expressions which takes two inputs i.e Key (String) and Value (HashMap)
V.forEach((X,Y)->{ : As this is applied on inner (V: fetched through previous foreach) hashmap so both Key and Value will be strings
Reference for further reading :
Lambda Expressions
Map foreach description
A Straight forward example with data
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 10);
map.put("Motorolla", 20);
map.put("RealMe", 30);
map.put("Oppo", 40);
map.put("Sony", 50);
map.put("OnePlus", 60);
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ", Stock : " + entry.getValue());
}
Using lambda expression
map.forEach((K,V) -> System.out.println(K + ", Stock : " + V));

Get value of HashMap within TreeMap

I've got a TreeMap that stores a HashMap inside of it. I feel like I should be able to find this, but I just can't seem to find it on Google.
I've got a TreeMap with a HashMap stored inside of it, I iterate over it like so:
while (i.hasNext()) {
Map.Entry me = (Map.Entry) i.next();
System.out.print(me.getKey() + ": ");
System.out.println(me.getValue());
}
That will print out an output (example line):
I/System.outīš• 32: {walks=32, pic=http://****/images/walkers/chase.png, name=Chase, dist=6096.8589024135445}
I'm wondering how to now grab pic, name, dist from this HashMap.
Edit: I'm not understanding where people missed the point. I put a HashMap into the TreeMap. Inside of the TreeMap is a HashMap. I guess I can show you what a HashMap is, but you guys know that already!
TreeMap dist_mp=new TreeMap();
Map<String, String> mp1 = new HashMap<String,String>();
mp1.put("dist", distanceInMiles + "");
mp1.put("name", obj.getString("first_name"));
mp1.put("pic", obj.getString("pic"));
mp1.put("walks", obj.getString("walks"));
dist_mp.put(distanceInMiles, mp1);
All you need is a cast to the TreeMap values to a Map again:
while (i.hasNext()) {
Map.Entry me = (Map.Entry) i.next();
System.out.print(me.getKey() + ": ");
// Cast to a Map again
Map<String, String> mp = (Map<String, String>) me.getValue();
// get() works now
System.out.print("name = " + mp.get("name"));
System.out.print("pic = " + mp.get("pic"));
System.out.println("dist = " + mp.get("dist"));
}
Need to iterate twice, one for TreeMap and then for HashMap
public static void main(String[] args) {
TreeMap<String, Map<String, String>> dist_mp = new TreeMap<String, Map<String, String>>();
Map<String, String> mp1 = new HashMap<String, String>();
mp1.put("dist", "6096.8589024135445");
mp1.put("name", "Chase");
mp1.put("pic", "http://****/images/walkers/chase.png");
mp1.put("walks", "32");
dist_mp.put("32", mp1);
for (Map.Entry<String, Map<String, String>> entry : dist_mp.entrySet()) {
String key = entry.getKey();
System.out.println(key);
Map<String, String> myMap = entry.getValue();
for (Map.Entry<String, String> entry1 : myMap.entrySet()) {
System.out.println(entry1.getKey() + " => " + entry1.getValue());
}
}
}
output
32
walks => 32
name => Chase
pic => http://****/images/walkers/chase.png
dist => 6096.8589024135445
Your HashMap seems to be holding an object of some class, which is depicted here:
{walks=32, pic=http://****/images/walkers/chase.png, name=Chase, dist=6096.8589024135445}
Identify the class, and if getter methods are available for pic, name, dist, then use them.
I think you are just asking how to get the value associated with a key:
map.get("pic");
You want me.getValue().get("pic"), me.getValue().get("name") and me.getValue().get("dist").
This assumes that you're using generics, that your TreeMap is declared as a Map<Integer, HashMap<String, String>> and that your Map.Entry that you iterate with is declared as a Map.Entry<Integer, HashMap<String, String>>.
Also, you could iterate more easily with a for-each loop.
Map<Integer, HashMap<String, String>> theTreeMap = new TreeMap<>();
// Populate the map here.
for (Map.Entry<Integer, HashMap<String, String>> me : theTreeMap.entrySet()) {
System.out.println(me.getValue().get("pic"));
System.out.println(me.getValue().get("name"));
System.out.println(me.getValue().get("dist"));
}

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