Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 200
sestus Avatar asked Dec 19 '16 14:12

sestus


People also ask

How do I convert a lowercase key to a map in Java?

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.

Is map key case sensitive?

Map is one of the most common data structures in Java, and String is one of the most common types for a map's key. By default, a map of this sort has case-sensitive keys.

Do keys have to be unique in HashMap?

HashMap is a container that stores key-value pairs. Each key is associated with one value. Keys in a HashMap must be unique.


2 Answers

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?
like image 190
Kenster Avatar answered Oct 12 '22 08:10

Kenster


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());
    }
}
like image 3
loicmathieu Avatar answered Oct 12 '22 09:10

loicmathieu