Is there a way to clear all of the values, while retaining the keys?
My HashMap is <String,String>, so should I just loop through and replace each value with a null? Is there a better way to do it?
You can use Map#replaceAll if you are using Java 8+ :
map.replaceAll((k, v) -> null);
If not, looping over the Entrys will probably be the simplest way. From the link above the default implementation of Map#replaceAll is equivalent to:
for (Map.Entry<K, V> entry : map.entrySet())
entry.setValue(function.apply(entry.getKey(), entry.getValue()));
Where function is the parameter, so you could do this:
for (Map.Entry<K, V> entry : map.entrySet()) {
entry.setValue(null);
}
I would keep it simple and collect the headers in a list or set then create a new HashMap at each line by looping over the list instead of trying to recycle the same map.
for(String key : map.keySet()) {
map.put(key, null);
}
Related
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.
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()))
I know that collections shouldn't be modified during iteration. So we should have workaround.
I have a code:
Map<Key, Value> map = getMap(); // map generating is hidden
for (Key key : map.keySet()) {
if (isToRemove(key)) {
map.remove(key);
} else {
map.put(key, getNewValue());
}
}
Is it undefined behavior or valid code?
keySet documentation sais that changes of the map are reflected in returned set and vice-versa. Does it mean that previous code is unacceptable?
The answer from davidxxx is correct (+1) in pointing out that the view collections on the Map are linked to the map, and that modifications to the map while iterating a view collection may result in ConcurrentModificationException. The view collections on a map are provided by the entrySet, keySet, and values methods.
Thus, the original code:
Map<Key, Value> map = getMap();
for (Key key : map.keySet()) {
if (isToRemove(key)) {
map.remove(key);
} else {
map.add(key, getNewValue());
}
}
will most likely throw ConcurrentModificationException because it modifies the map during each iteration.
It's possible to remove entries from the map while iterating a view collection, if that view collection's iterator supports the remove operation. The iterators for HashMap's view collections do support this. It is also possible to set the value of a particular map entry (key-value pair) by using the setValue method of a Map.Entry instance obtained while iterating a map's entrySet. Thus, it's possible to do what you want to do within a single iteration, without using a temporary map. Here's the code to do that:
Map<Key, Value> map = getMap();
for (var entryIterator = map.entrySet().iterator(); entryIterator.hasNext(); ) {
var entry = entryIterator.next();
if (isToRemove(entry.getKey())) {
entryIterator.remove();
} else {
entry.setValue(getNewValue());
}
}
Note the use of Java 10's var construct. If you're not on Java 10, you have to write out the type declarations explicitly:
Map<Key, Value> map = getMap();
for (Iterator<Map.Entry<Key, Value>> entryIterator = map.entrySet().iterator(); entryIterator.hasNext(); ) {
Map.Entry<Key, Value> entry = entryIterator.next();
if (isToRemove(entry.getKey())) {
entryIterator.remove();
} else {
entry.setValue(getNewValue());
}
}
Finally, given that this is a moderately complicated map operation, it might be fruitful to use a stream to do the work. Note that this creates a new map instead of modifying an existing map in-place.
import java.util.Map.Entry;
import static java.util.Map.entry; // requires Java 9
Map<Key, Value> result =
getMap().entrySet().stream()
.filter(e -> ! isToRemove(e.getKey()))
.map(e -> entry(e.getKey(), getNewValue()))
.collect(toMap(Entry::getKey, Entry::getValue));
The HashMap.keySet() method states more precisely:
The set is backed by the map, so changes to the map are reflected in
the set, and vice-versa.
It means that the elements returned by keySet() and the keys of the Map refer to the same objects. So of course changing the state of any element of the Set (such as key.setFoo(new Foo());) will be reflected in the Map keys and reversely.
You should be cautious and prevent the map from being modified during the keyset() iteration :
If the map is modified while an iteration over the set is in progress
(except through the iterator's own remove operation), the results of
the iteration are undefined
You can remove entries of the map as :
The set supports element removal, which removes the corresponding
mapping from the map, via the Iterator.remove, Set.remove, removeAll,
retainAll, and clear operations.
But you cannot add entries in :
It does not support the add or addAll operations.
So in conclusion, during keySet() iterator use Set.remove() or more simply iterate with the Iterator of the keySet and invoke Iterator.remove() to remove elements from the map.
You can add new elements in a temporary Map that you will use after the iteration to populate the original Map.
For example :
Map<Key, Value> map = getMap(); // map generating is hidden
Map<Key, Value> tempMap = new HashMap<>();
for (Iterator<Key> keyIterator = map.keySet().iterator(); keyIterator.hasNext();) {
Key key = keyIterator.next();
if (isToRemove(key)) {
keyIterator.remove();
}
else {
tempMap.put(key, getNewValue());
}
}
map.putAll(tempMap);
Edit :
Note that as you want to modify existing entries of the map, you should use an Map.EntrySet as explained in the Stuart Marks answer.
In other cases, using an intermediary Map or a Stream that creates a new Map is required.
If you run your code you get a ConcurrentModificationException. Here is how you do it instead, using an iterator over the keys set or the equivalent Java8+ functional API:
Map<String, Object> bag = new LinkedHashMap<>();
bag.put("Foo", 1);
bag.put("Bar", "Hooray");
// Throws ConcurrentModificationException
for (Map.Entry<String, Object> e : bag.entrySet()) {
if (e.getKey().equals("Foo")) {
bag.remove(e.getKey());
}
}
// Since Java 8
bag.keySet().removeIf(key -> key.equals("Foo"));
// Until Java 7
Iterator<String> it = bag.keySet().iterator();
while (it.hasNext()) {
if (it.next().equals("Bar")) {
it.remove();
}
}
I want to loop through all entries of AbstractMap.SimpleEntry type and Map type using the same function. Is there a way to do it?
You cannot iterate through what you are asking. I am guessing you mean the following:
for(Map.Entry<K, V> entry : AbstractMap.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
}
This example will not run; you'll need to alter it to your concrete implementations of the Abstract classes and Key/Value definitions.
Oracle Java Docs
Another Example
I want to go through every items in a dictionary in java. to clarify what I want to do, this is the C# code
Dictionary<string, Label> LableList = new Dictionary<string, Label>();
foreach (KeyValuePair<string, Label> z in LabelList);
I don't know how to do this is java, for example I did this
for(Object z: dic)
but it says it's not iterable. Please advise......
I'm assuming you have a Map<String, Label> which is the Java built-in dictionary structure. Java doesn't let you iterate directly over a Map (i.e. it doesn't implement Iterable) because it would be ambiguous what you're actually iterating over.
It's just a matter of choosing to iterate through the keys, values or entries (both).
e.g.
Map<String, Label> map = new HashMap<String, Label>();
//...
for ( String key : map.keySet() ) {
}
for ( Label value : map.values() ) {
}
for ( Map.Entry<String, Label> entry : map.entrySet() ) {
String key = entry.getKey();
Label value = entry.getValue();
}
Your C# code seems to be the same as iterating over the entries (the last example).
java.util.Map is the Dictionary equvivalent and below is an example on how you can iterate through each entry
for(Map.Entry<K, V> e : map.entrySet())
{
System.out.println(e.getKey()+": "+e.getValue());
}
Your best bet is to use this:
for (String key : LableList.keys()) {
Label value = LableList.get(key);
// do what you wish with key and value here
}
In Java however, a better bet is to not use Dictionary as you would in .NET but to use one of the Map subclasses, e.g. HashMap. You can iterate through one of these like this:
for (Entry<String, Label> e : myMap.entrySet()) {
// Do what you wish with e.getKey() and e.getValue()
}
You are also advised against using Dictionary in the official javadoc.
I was trying to add the contents of one HashMap (a) into another HashMap (b).
I found it simple to iterate through HashMap a this way:
a.forEach((k, v) -> b.put(k, v));
You can manipulate my example to do what ever you want on the other side of "->".
Note that this is a Lambda expression, and that you would have to use Java 1.8 (Java 8) or later for this to work. :-)