Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Case insensitive string as HashMap key

People also ask

Can we use string as key in HashMap?

String is as a key of the HashMap When you pass the key to retrieve its value, the hash code is calculated again, and the value in the position represented by the hash code is fetched (if both hash codes are equal).

Are map keys 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.

Is HashSet case sensitive Java?

In this example, we will show you how to check HashSet contains element case insensitive in Java. contains() method of Collection interface returns true if this set contains the specified element. But the problem is contains() method only check the equality of element (case sensitive).


Map<String, String> nodeMap = 
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

That's really all you need.


As suggested by Guido García in their answer here:

import java.util.HashMap;

public class CaseInsensitiveMap extends HashMap<String, String> {

    @Override
    public String put(String key, String value) {
       return super.put(key.toLowerCase(), value);
    }

    // not @Override because that would require the key parameter to be of type Object
    public String get(String key) {
       return super.get(key.toLowerCase());
    }
}

Or

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html


One approach is to create a custom subclass of the Apache Commons AbstractHashedMap class, overriding the hash and isEqualKeys methods to perform case insensitive hashing and comparison of keys. (Note - I've never tried this myself ...)

This avoids the overhead of creating new objects each time you need to do a map lookup or update. And the common Map operations should O(1) ... just like a regular HashMap.

And if you are prepared to accept the implementation choices they have made, the Apache Commons CaseInsensitiveMap does the work of customizing / specializing AbstractHashedMap for you.


But if O(logN) get and put operations are acceptable, a TreeMap with a case insensitive string comparator is an option; e.g. using String.CASE_INSENSITIVE_ORDER.

And if you don't mind creating a new temporary String object each time you do a put or get, then Vishal's answer is just fine. (Though, I note that you wouldn't be preserving the original case of the keys if you did that ...)


Subclass HashMap and create a version that lower-cases the key on put and get (and probably the other key-oriented methods).

Or composite a HashMap into the new class and delegate everything to the map, but translate the keys.

If you need to keep the original key you could either maintain dual maps, or store the original key along with the value.


Two choices come to my mind:

  1. You could use directly the s.toUpperCase().hashCode(); as the key of the Map.
  2. You could use a TreeMap<String> with a custom Comparator that ignore the case.

Otherwise, if you prefer your solution, instead of defining a new kind of String, I would rather implement a new Map with the required case insensibility functionality.


Wouldn't it be better to "wrap" the String in order to memorize the hashCode. In the normal String class hashCode() is O(N) the first time and then it is O(1) since it is kept for future use.

public class HashWrap {
    private final String value;
    private final int hash;

    public String get() {
        return value;
    }

    public HashWrap(String value) {
        this.value = value;
        String lc = value.toLowerCase();
        this.hash = lc.hashCode();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o instanceof HashWrap) {
            HashWrap that = (HashWrap) o;
            return value.equalsIgnoreCase(that.value);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return this.hash;
    }

    //might want to implement compare too if you want to use with SortedMaps/Sets.
}

This would allow you to use any implementation of Hashtable in java and to have O(1) hasCode().