How to effectively replace Java HashMap with boolean values - java

I'm interested how I can very quickly change the Boolean values into this hashmap:
HashMap<String, Boolean> selectedIds = new HashMap<>();
I want very quickly to replace the Boolean values all to be true. How I can do this?

The fastest way is this:
for (Map.Entry<String, Boolean> entry : selectedIds.entrySet()) {
entry.setValue(true);
}
This code avoids any lookups whatsoever, because it iterates though the entire map's entries and sets their values directly.
Note that whenever HashMap.put() is called, a key look up occurs in the internal Hashtable. While the code is highly optimized, it nevertheless requires work to calculate and compare hashcodes, then employ an algorithm to ultimately find the entry (if it exists). This is all "work", and consumes CPU cycles.
Java 8 update:
Java 8 introduced a new method replaceAll() for just such a purpose, making the code required even simpler:
selectedIds.replaceAll((k, v) -> true);

This will iterate through your map and replace all the old values with a true value for each key. HashMap put method
for(String s : selectedIds.keySet()) {
selectedIds.put(s, true);
}

Related

Is there an efficient way of checking if HashMap contains keys that map to the same value?

I basically need to know if my HashMap has different keys that map to the same value. I was wondering if there is a way other than checking each keys value against all other values in the map.
Update:
Just some more information that will hopefully clarify what I'm trying to accomplish. Consider a String "azza". Say that I'm iterating over this String and storing each character as a key, and it's corresponding value is some other String. Let's say I eventually get to the last occurrence of 'a' and the value is already be in the map.This would be fine if the key corresponding with the value that is already in the map is also 'a'. My issue occurs when 'a' and 'z' both map to the same value. Only if different keys map to the same value.
Sure, the fastest to both code and execute is:
boolean hasDupeValues = new HashSet<>(map.values()).size() != map.size();
which executes in O(n) time.
Sets don't allow duplicates, so the set will be smaller than the values list if there are dupes.
Very similar to EJP's and Bohemian's answer above but with streams:
boolean hasDupeValues = map.values().stream().distinct().count() != map.size();
You could create a HashMap that maps values to lists of keys. This would take more space and require (slightly) more complex code, but with the benefit of greatly higher efficiency (amortized O(1) vs. O(n) for the method of just looping all values).
For example, say you currently have HashMap<Key, Value> map1, and you want to know which keys have the same value. You create another map, HashMap<Value, List<Key>> map2.
Then you just modify map1 and map2 together.
map1.put(key, value);
if(!map2.containsKey(value)) {
map2.put(value, new ArrayList<Key>);
}
map2.get(value).add(key);
Then to get all keys that map to value, you just do map2.get(value).
If you need to put/remove in many different places, to make sure that you don't forget to use map2 you could create your own data structure (i.e. a separate class) that contains 2 maps and implement put/remove/get/etc. for that.
Edit: I may have misunderstood the question. If you don't need an actual list of keys, just a simple "yes/no" answer to "does the map already contain this value?", and you want something better than O(n), you could keep a separate HashMap<Value, Integer> that simply counts up how many times the value occurs in the map. This would take considerably less space than a map of lists.
You can check whether a map contains a value already by calling map.values().contains(value). This is not as efficient as looking up a key in the map, but still, it's O(n), and you don't need to create a new set just in order to count its elements.
However, what you seem to need is a BiMap. There is no such thing in the Java standard library, but you can build one relatively easily by using two HashMaps: one which maps keys to values and one which maps values to keys. Every time you map a key to a value, you can then check in amortized O(1) whether the value already is mapped to, and if it isn't, map the key to the value in the one map and the value to the key in the other.
If it is an option to create a new dependency for your project, some third-party libraries contain ready-made bimaps, such as Guava (BiMap) and Apache Commons (BidiMap).
You could iterate over the keys and save the current value in the Set.
But, before inserting that value in a Set, check if the Set already contains that value.
If this is true, it means that a previous key already contains the same value.
Map<Integer, String> map = new HashMap<>();
Set<String> values = new HashSet<>();
Set<Integter> keysWithSameValue = new HashSet<>();
for(Integer key : map.keySet()) {
if(values.contains(map.get(key))) {
keysWithSameValue.add(key);
}
values.add(map.get(key));
}

Lowercase all HashMap keys

I 've run into a scenario where I want to lowercase all the keys of a HashMap (don't ask why, I just have to do this). The HashMap has some millions of entries.
At first, I thought I 'd just create a new Map, iterate over the entries of the map that is to be lowercased, and add the respective values. This task should run only once per day or something like that, so I thought I could bare this.
Map<String, Long> lowerCaseMap = new HashMap<>(myMap.size());
for (Map.Entry<String, Long> entry : myMap.entrySet()) {
lowerCaseMap.put(entry.getKey().toLowerCase(), entry.getValue());
}
this, however, caused some OutOfMemory errors when my server was overloaded during this one time that I was about to copy the Map.
Now my question is, how can I accomplish this task with the smallest memory footprint?
Would removing each key after lowercased - added to the new Map help?
Could I utilize java8 streams to make this faster? (e.g something like this)
Map<String, Long> lowerCaseMap = myMap.entrySet().parallelStream().collect(Collectors.toMap(entry -> entry.getKey().toLowerCase(), Map.Entry::getValue));
Update
It seems that it's a Collections.unmodifiableMap so I don't have the option of
removing each key after lowercased - added to the new Map
Instead of using HashMap, you could try using a TreeMap with case-insensitive ordering. This would avoid the need to create a lower-case version of each key:
Map<String, Long> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
map.putAll(myMap);
Once you've constructed this map, put() and get() will behave case-insensitively, so you can save and fetch values using all-lowercase keys. Iterating over keys will return them in their original, possibly upper-case forms.
Here are some similar questions:
Case insensitive string as HashMap key
Is there a good way to have a Map<String, ?> get and put ignoring case?
You cannot remove the entry while iterating over the map. You will have a ConcurentModificationException if you try to do this.
As the issue is an OutOfMemoryError, not a performance error, using parallel stream will not help either.
Despite some task on the Stream API will be done lately, this will still lead to have two maps in memory at some point so you will still have the issue.
To workaround it, I only saw two ways :
Give more memory to your process (by increasing -Xmx on the Java command line). Memory is cheap these days ;)
Split the map and work in chunks : for example you divide the size of the map by ten and you process one chunck at a time and delete the processed entries before processing the new chunk. By this instead of having two times the map in memory you will just have 1.1 times the map.
For the split algorithm, you can try someting like this using the Stream API :
Map<String, String> toMap = new HashMap<>();
int chunk = fromMap.size() / 10;
for(int i = 1; i<= 10; i++){
//process the chunk
List<Entry<String, String>> subEntries = fromMap.entrySet().stream().limit(chunk)
.collect(Collectors.toList());
for(Entry<String, String> entry : subEntries){
toMap.put(entry.getKey().toLowerCase(), entry.getValue());
fromMap.remove(entry.getKey());
}
}
the concerns in the above answers are correct and you might need to reconsider changing the data structure you are using.
for me, I had a simple map I needed to change its keys to lower case
take a look at my snippet, its a trivial solution and bad at performance
private void convertAllFilterKeysToLowerCase() {
HashSet keysToRemove = new HashSet();
getFilters().keySet().forEach(o -> {
if(!o.equals(((String) o).toLowerCase()))
keysToRemove.add(o);
});
keysToRemove.forEach(o -> getFilters().put(((String) o).toLowerCase(), getFilters().remove(o)));
}
Not sure about the memory footprint. If using Kotlin, you can try the following.
val lowerCaseMap = myMap.mapKeys { it.key.toLowerCase() }
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map-keys.html

Efficient way to exclude a specific value from a TreeMap

I am trying to achieve the best performance for my app. At some point in the code, i want to retrieve all the values from a map except one that corresponds to a specific key.
Now, if i wanted to retrieve all the values i would use this:
map.values();
and assuming that the TreeMap class is created efficiently, the 'values()' method is just returning a refference so --> O(1).
In my case though i want to exclude the value of a specific key. This code:
Set<String> set = new ...
for (String key: map.keySet()) {
if (!key.equals("badKey")) {
set.add(map.get(key));
}
}
has a complexity of N*(logN) which is much slower than the initial O(1) and this is caused by the need of removing only one value.
Is there a better way to do this?
You can use entrySet instead of keySet. This way it would take O(1) to find out if a given value belongs to the key you wish to exclude.
You can call entrySet any time you need to iterate over the values, and exclude the bad key while iterating over them. This would give you the same complexity as iterating over the values() Collection would.
How about this?
map.entrySet().stream()
.filter(e -> !e.getKey().equals(keyToFilter))
.map(Map.Entry::getValue);
Finish with either forEach or toCollection(Collectors.TO_SET), or simply return the stream.
Sorry if the code doesn't compile exactly, it's from memory and I haven't touched the java 8 APIs in a few months, but you should get the drift. ;)
You can create set from map.values() and after it remove "badKey" value from this set.
Set<String> set = new HashSet<String>(map.values());
String badValue = map.get("badKey");
set.remove(badValue);

How to check whether a HashMap has all the elements of an ArrayList?

If I have a HashMap hashM, an ArrayList arrayL. If I would like to use an if statement to check whether hashM has all the elements in arrayL, how can I do that?
I cm currently using something like
if (hashM.values().containsAll(arrayL.getPreReqs()))
However it doesn't work properly.
Dear all thanks for the answers!
Actually containsAll works however the way I structure the my codes is wrong so that I got wrong outcomes. Now it has been fixed.
Cheers!
Given
Map<?,?> map = new HashMap<?,?>();
List<?> list = new ArrayList<?>();
The approach you tried (well, nearly, as pointed out by Marko Topolnik) is indeed correct:
if (map.values().containsAll(list)) { ... }
(Or map.keySet().containsAll(list) if you were interested in the map keys instead of values.)
For this to work as expected for custom types, you of course must have implemented equals() and hashcode() correctly for them. (See e.g. this question or better yet, read Item 9 in Effective Java.)
By the way, when working with Java Collections, it is good practice to define fields and variables using the interfaces (such as List, Set, Map), not implementation types (e.g. ArrayList, HashSet, HashMap). For example:
List<String> list = new ArrayList<String>();
Map<Integer, String> map = new HashMap<Integer, String>();
Similarly, a more "correct" or fluent title for your question would have been "How to check whether a Map has all the elements of a List?". Check out the Java Collections tutorial for more info.
Your code is correct except..
if (hashM.values().containsAll(arrayL)) {....}
[EDIT]
You can use HashMap.containsValue(Object value)
public boolean containsList(HashMap<K, V> map, List<V> list) {
for(V value : list) {
if(!map.containsValue(value)) {
return false;
}
}
return true;
}
Your code should work - but will not be particularly efficient. You need to compare every element in the list with every element in the map.
If (and only if) you can easily extract the key of the map from the elements then you would be better off looping through your List and for each element do map.containsKey(getKey(elem)), this will be much faster.
If you are doing this sort of comparison a lot and you cannot map from element to key then it may be worth keeping a HashSet of the values for this purpose.
I agree with JoniK. This can be done in a single line like this.
if(hashM.values().containsAll(arrayL)) {// put your code here that will be returned}

Is it possible/required to speed up HashMap operations on same entry?

Suppose I wish to check HashMap entry and then replace it:
if( check( hashMap.get(key) ) ) {
hashMap.put(key, newValue);
}
this will cause search procedure inside HashMap to run two times: once while get and another one while put. This looks ineffective. Is it possible to modify value of already found entry of Map?
UPDATE
I know I can make a wrapper and I know I have problems to mutate entry. But the question is WHY? May be HashMap remembers last search to improve repeated one? Why there are no methods to do such operation?
EDIT: I've just discovered that you can modify the entry, via Map.Entry.setValue (and the HashMap implementation is mutable). It's a pain to get the entry for a particular key though, and I can't remember ever seeing anyone do this. You can get a set of the entries, but you can't get the entry for a single key, as far as I can tell.
There's one evil way of doing it - declare your own subclass of HashMap within the java.util package, and create a public method which just delegates to the package-private existing method:
package java.util;
// Please don't actually do this...
public class BadMap<K, V> extends HashMap<K, V> {
public Map.Entry<K, V> getEntryPublic(K key) {
return getEntry(key);
}
}
That's pretty nasty though.
You wouldn't normally modify the entry - but of course you can change data within the value, if that's a mutable type.
I very much doubt that this is actually a performance bottleneck though, unless you're doing this a heck of a lot. You should profile your application to prove to yourself that this is a real problem before you start trying to fine-tune something which is probably not an issue.
If it does turn out to be an issue, you could change (say) a Map<Integer, String> into a Map<Integer, AtomicReference<String>> and use the AtomicReference<T> as a simple mutable wrapper type.
Too much information for a comment on your question. Check the documentation for Hashmap.
This implementation provides constant-time performance for the basic
operations (get and put), assuming the hash function disperses the
elements properly among the buckets. Iteration over collection views
requires time proportional to the "capacity" of the HashMap instance
(the number of buckets) plus its size (the number of key-value
mappings). Thus, it's very important not to set the initial capacity
too high (or the load factor too low) if iteration performance is
important.
Constant time means that it always requires the same amount of time to do the get and put operations [O(1)]. The amount of time that is going to be required is going to be linear based on how many times you need to loop [O(n)].
You can change the entry if it is mutable. One example of where you might do this is
private final Map<String, List<String>> map = new LinkedHashMap<>();
public void put(String key, String value) {
List<String> list = map.get(key);
if (list == null)
map.put(key, list = new ArrayList<>());
list.add(value);
}
This allows you to update a value, but you can't find and replace a value in one operation.
Take a look at trove ( http://trove4j.sourceforge.net/ ), their maps do have several methods that might be what you want:
adjustOrPut
putIfAbsent
I don't know how this is implemented internally, but i would guess that since trove is made to be highly performant, there will be only one lookup.

Categories

Resources