Search code examples
javastringhashmapcase-insensitive

CaseInsensitive Map Key Java


I have a Map<String, Object> in which I need the String key to be case insensitive

Currently I am wrapping my String objects in a Wrapper class I've called CaseInsensitiveString the code for which looks like this:

    /**
    * A string wrapper that makes .equals a caseInsensitive match
    * <p>
    *     a collection that wraps a String mapping in CaseInsensitiveStrings will still accept a String but will now
    *     return a caseInsensitive match rather than a caseSensitive one
    * </p>
    */
public class CaseInsensitiveString {
    String str;

    private CaseInsensitiveString(String str) {
        this.str = str;
    }

    public static CaseInsensitiveString wrap(String str) {
        return new CaseInsensitiveString(str);
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null) return false;

        if(o.getClass() == getClass()) { //is another CaseInsensitiveString
            CaseInsensitiveString that = (CaseInsensitiveString) o;
            return (str != null) ? str.equalsIgnoreCase(that.str) : that.str == null;
        } else if (o.getClass() == String.class){ //is just a regular String
            String that = (String) o;
            return str.equalsIgnoreCase(that);
        } else {
        return false;
        }

    }

    @Override
    public int hashCode() {
        return (str != null) ? str.toUpperCase().hashCode() : 0;
    }

    @Override
    public String toString() {
        return str;
    }
}

I was hoping to be able to get a Map<CaseInsensitiveString, Object> to still accept a Map#get(String) and return the value without having to do Map#get(CaseInsensitiveString.wrap(String)). In my testing however, my HashMap has returned null whenever I have tried to do this but it does work if I wrap the String before calling get()

Is it possible to allow my HashMap to accept both String and CaseInsensitiveString parameters to the get method and work in a caseInsensitive fashion regardless of if the String is wrapped or not, and if so, what am I doing wrong?

for reference my test code looks like this:

    Map<CaseInsensitiveString, String> test = new HashMap<>();
    test.put(CaseInsensitiveString.wrap("TesT"), "value");
    System.out.println(test.get("test"));
    System.out.println(test.get(CaseInsensitiveString.wrap("test")));

and returns:

null
value

Solution

  • You can do it like this:

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

    See this question.

    However, note the performance implications of using a TreeMap instead of a HashMap, as mentioned by Boris in the comments.