Java merge two maps - java

I need to merge two maps in the first one by the following rules:
I need to remove all of the keys from map1 which are not present in the map2.
I need to update all keys in the map1 with the appropriate values which are present in map2 under these keys.
This is my current code:
Set<String> keysToRemove = new HashSet<>();
map1.forEach((k, v) -> {
if (!map2.containsKey(k)) {
keysToRemove.add(k);
} else {
map1.put(k, map2.get(k));
}
});
for (String k : keysToRemove) {
map1.remove(k);
}
I'm not sure my code is optimal and can be improved. Could you please show the way how to implement this task more effectively?

You can achieve it in two lines
This solution is based on the comment (which gave an impression as the OP wanted map1 to be an exact copy of map2)
[...]I'm trying to keep the same referense on the original map1 and do not substitute it with the new map.[sic]
//Retains only those keys that are in map2
map1.keySet().retainAll(map2.keySet());
//(Possibly) Overwrite value for each key in map2 into map1
map2.forEach(map1::put);
I don't believe it would help you improve the performance though.
EDIT:
As suggested by Jacob G.# you can have map1.putAll(map2) for the last line
EDIT2:
If we consider the OP (and not the comments), if there are any keys in map2 that is not there in map1, it should not end up in map1 and hence the last statement becomes
map1.forEach((key, value) -> map1.put(key, map2.get(key)));

I think you can remove the second loop by using Iterator
Iterator<Map.Entry<K,V>> iter = map1.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<K,V> entry = iter.next();
if(not map2 contain k){
iter.remove();
} else {
entry.put new data
}
}
The key here is, you can't update the map while loop over Map.entrySet(), it will raise ConcurrentModificationException, but you can do it with Iterator.

Another approach could be filtering only with the keys available in map2, and finally using map to replace existing values with the ones on map2. Something similar to this might do the trick:
map1.entrySet().stream().
filter(e -> map2.containsKey(e.getKey())).
map(e -> map2.get(e.getKey()))

Related

How to get the specific <K,V> entry based on the given key in Java HashMap?

Ex. I have the following HashMap, and how to get the entry 'b'=6 if I know the key 'b'. Is there any way to do it?
Map<Character, Integer> map=new HashMap<>();
map.put('a',7);
map.put('b',6);
map.put('c',5);`
By the way, I want to do this is because all those entries are in a priority queue. I have to remove that entry from the priority queue and re-insert it to make sure it is in order.
Thanks.
If you know the key, simply get the value using Map#get(Object). As long as you know both, you have the entry. The Map interface doesn't provide a specific method that returns a certain entry.
Map<Character, Integer> map = ...
Character key = 'b';
Integer value = map.get(key);
// now with the 'key' and 'value' that make TOGETHER an entry.
If you really somehow need an Entry<Character, Integer> construct it like this:
Map.Entry<Character, Integer> entry = new SimpleEntry<>(key, value);
There is no better way. One would say you can use Stream API to iterate through the entries and return the first one found, however, you lose the main benefit of the HashMap which is the constant-time look-up.
// DON'T DO THIS!
Entry<Character, Integer> entry = map.entrySet().stream()
.filter(e -> key.equals(e.getKey()))
.findFirst()
.orElse(null);
You could do something like this
public Entry<Character, Integer> entry_return(Map<Character, Integer> map) {
for(Map.Entry<Character, Integer> entry : map.entrySet()) {
if(entry.getKey() == 'b')
return entry;
}
}
or use a stream API if you really need the entry but i don't know if that is very common/usefull
Since Java 9, you can use static method Map.entry​(K k, V v), which:
returns an unmodifiable Map.Entry containing the given key and value.
So, you can obtain your Entry<K, V> instance, as:
Map.Entry<Character, Integer> entry = Map.entry(key, map.get(key));
where map stores a reference to your Map<K, V> instance.

How to replace null values in map with length of key using java 8 stream

I have a Map<String, Integer>, which has some keys and values. I want to associate all keys with the values as the key's length.
I have been able to solve this in pure java and java-8, but somehow I don't think that appending a terminal operation at the end like .collect(Collectors.toList()); which is not required for me in my code.
My code: ( Java ) works fine
Map<String, Integer> nameLength = new HashMap<>();
nameLength.put("John", null);
nameLength.put("Antony", 6);
nameLength.put("Yassir", 6);
nameLength.put("Karein", 6);
nameLength.put("Smith", null);
nameLength.put("JackeyLent",null);
for(Entry<String, Integer> length: nameLength.entrySet()){
if(length.getValue() == null){
nameLength.put(length.getKey(),length.getKey().length());
}
}
Java-8 also works fine but the terminal operation is useless, how I avoid it without using .foreach().
nameLength.entrySet().stream().map(s->{
if(s.getValue() == null){
nameLength.put(s.getKey(),s.getKey().length());
}
return nameLength;
}).collect(Collectors.toList());
System.out.println(nameLength);
Any other way in which I can do the above logic in Java-8 and above??
If you're going to use streams then you should avoid side effects. Functional programming is all about pure operations where the output depends only on the input and functions have no side effects. In other words, create a new map instead of modifying the existing one.
If you do that you might as well just throw away the partially-filled-out map and recompute everything from scratch. Calling String.length() is cheap and it's not really worth the effort to figure out which values are null and which aren't. Recompute all the lengths.
Map<String, Integer> newMap = nameLength.keySet().stream()
.collect(Collectors.toMap(
name -> name,
name -> name.length()
));
On the other hand if you just want to patch up your current map streams don't really buy you anything. I'd just modify it in place without involving streams.
for (Map.Entry<String, Integer> entry: nameLength.entrySet()) {
if (entry.getValue() == null) {
entry.setValue(entry.getKey().length());
}
}
Or, as discussed above, you could simplify matters by replacing all of the lengths:
nameLength.replaceAll((name, __) -> name.length());
(__ signifies a variable that isn't used and so doesn't get a meaningful name.)
You almost there, just use the filter to identify the entries with null values and then use Collectors.toMap to collect them into Map with key length as value
Map<String, Integer> nameLengths = nameLength.entrySet()
.stream()
.filter(entry->entry.getValue()==null)
.collect(Collectors.toMap(Map.Entry::getKey, entry->entry.getKey().length()));
Or more simpler way you have that check in Collectors.toMap
Map<String, Integer> nameLengths = nameLength.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry->entry.getValue() == null ? entry.getKey().length() : entry.getValue()));

How to search efficiently return all the values for keys which are in a arraylist

I am having an arraylist which contains a list of numbers. I want to get all the values from the HashMap which has the keys which are in the array list.
For example say the array list contains 1,2,3,4,5,6,7,8,9 list
I want to get all the values for the keys 1,2,3,4,5,6,7,8,9 map
So currently I am implementing
for (i=0;i<list.size;i++){
map_new.put(list.get(),map.get(list.get()))
}
Is there any efficient way to do this?
Your code basically assumes that map.get(list.get()) always returns a value, you can try the following code which first filters the not null values from the list object and then adds to the new Map:
Map<String, Integer> newMap = list.stream().
filter(key -> (map.get(key) != null)).//filter values not present in Map
collect(Collectors.toMap(t -> t, t -> map.get(t)));//now collect to a new Map
In case, if map.get(list.get()) returns null, your code creates a Map with null values in it for which you might end up doing null checks, which is not good, rather you can ensure that your newly created Map always contains a value for each key.
Assuming the signature of list and the map are as following
List<Integer> list;
Map<Integer, Integer> map;
You can use following
for(int a : list){
Integer b = map.get(a);
if(b != null)
// b is your desired value you can store in another collection
}
Which is similar to the procedure you have already used.
As you can access the map in O(1) so the complexity of this code will be O(listsize)
There is not much you can do for efficiency. Still couple of small things you can do considering code example you have given above:
1) Change your for loop to
for(Long num : list)
instead of iterating using index, this will reduce you get calls over list.
2) You can update the existing map , so that you even do not need to iterate.
map.keySet().retainAll(list);
for(Long key: map.keySet()) {
System.out.println(map.get(key));
}
With this existing map will contain only those data whose keys are present in list, but you should use it carefully depending upon rest of the code logic.
You can capitalize on the fact that the keyset of a map is backed by the map itself and modifications to the keyset will reflect back to the map itself. This way, you can use the retainAll() method of the Set interface to reduce the map with a single line of code. Here is an example:
final Map<Integer, String> m = new HashMap<Integer, String>();
m.put(1, "A");
m.put(2, "B");
m.put(3, "C");
m.put(4, "D");
m.put(5, "E");
final List<Integer> al = Arrays.asList(new Integer[] { 2, 4, 5 });
System.out.println(m);
m.keySet().retainAll(al);
System.out.println(m);
This will output:
{1=A, 2=B, 3=C, 4=D, 5=E}
{2=B, 4=D, 5=E}

Iterating LinkedHashMap values from the key

I'm struggling with a little bit of code, and I just cannot figure out what the correct syntax would be. Here is an example of the problem:
for (Integer key : map1.keySet()) {
for (Integer value : map2.values()) {
if (value == key) {
// ++ the corresponding value of the looping map1 key.
// tried statements like map1.values()++
// but this is an invalid operation.
}
}
}
I am trying to loop map1 keys through map2 values and if within this process a map1 key matches a map2 value, I want to add one to the value of the corresponding map1 key.
Should be a simple problem but Eclipse keeps telling me that I have the syntax wrong, can anyone suggest what statement I may need to iterate the values?
Here is how you can do it with a very small modification to your code:
for (Map.Entry<Integer,Integer> entry : map1.entrySet()) {
for (Integer value : map2.values()) {
if (value.equals(entry.getKey())) {
entry.setValue(entry.getValue()+1);
}
}
}
EDIT : Since map2 could contain the same value more than once, the other solution that uses a hash set to speed things up will not work as expected.
How you init yours maps? Like Map<Integer, Integer> map1=new LinkedHashMap<Integer, Integer>(); or Map map1=new LinkedHashMap(); If the second way the key will be Object not Integer. And in all cases compare two Integer using == bad practise. Use equals
I'm not sure why Eclipse report a syntax error, but in any case this loop does not make much sense, consider changing it to:
for (Integer value : map2.values())
if (map1.containsKey(value))
// bla bla
it will run in O(n) rather than the former O(n*m)
when n is the size of map2 and m is the size of map1 (as notified by #dasblinkenlight)
You can use containsKey method like this:
for (Integer value : map2.values()){
if (map1.containsKey(value))
map1.put(key, value);
...
}

How to delete another item(not the one you are holding) while Iterating the HashMap

I've tried to search over the internet to find a solution of deleting another item but not the one you are visiting. Unfortunately, there is not a way to do it.
Here is the problem.
Assume I have a hashmap and
the items are <0,10> <1,20> <2,30>
Map<Integer,Integer> map = new HashMap<Integer, Integer>() ;
Iterator<Map.Entry<Integer, Integer> >entries = map.entrySet().iterator();
while (entries.hasNext()) {
Entry<Integer, Integer> entry = entries.next();
int temp = entry.getValue();
if (temp==0){
map.remove(2); //2 is the key of 3th item
}
}
Then the problem occours.
Really appreciate the suggestions.
Do it in two passes:
iterate through the entries, and collect the keys to delete in a Set<Integer>
iterate over the set, and remove all the keys it contains from the map. (Or call map.keySet().removeAll(keysToRemove))
Let me guess, you're getting a ConcurrentModificationException.
That's baked in. The javadocs say it may be thrown if you do what you're doing. You can either follow #JBNizet's or you can restart iterating each time you remove an element. Which you choose will depend upon your specific situation.
A 3rd option is to create a copy of the entry set and iterate over that. This one works best if restarting the iteration is expensive and you need to remove quickly.
Iterator<Map.Entry<Integer, Integer> >entries = new HashSet<Map.Entry<Integer, Integer>>(map.entrySet()).iterator();
Do it in 2 passes, 1st accumulate keys to remove, then perform actual removal:
List<Integer> keysToRemove = ...
while (entries.hasNext()) {
Entry<Integer, Integer> entry = entries.next();
int temp = entry.getValue();
if (temp==0){
keysToRemove.add(2);
}
}
for (Integer key : keysToRemove)
map.remove(key);
You cannot modify a HashMap while iterating through it. Instead, you could for example collect a list of keys to remove while iterating through the map, and then remove the items in the list from the map after you have completed the iterating.

Categories