5

I need a multi map which keys are case insensitive. is there such implementation in google collections?

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
oshai
  • 14,865
  • 26
  • 84
  • 140

4 Answers4

9

Here is a case insensitive version of a ForwardingMap:

public class CaseInsensitiveForwardingMap<V> extends ForwardingMap<String, V>
    implements Serializable{

    private static final long serialVersionUID = -7741335486707072323L;

    // default constructor
    public CaseInsensitiveForwardingMap(){
        this(new HashMap<String, V>());
    }

    // constructor with a supplied map    
    public CaseInsensitiveForwardingMap(final Map<String, V> inner){
        this.inner = inner;
    }

    private final Map<String, V> inner;
    @Override
    protected Map<String, V> delegate(){
        return inner;
    }

    // convert keys to lower case Strings, preserve null keys
    private static String lower(final Object key){
        return key == null ? null : key.toString().toLowerCase();
    }

    @Override
    public V get(final Object key){ return inner.get(lower(key)); }
    @Override
    public void putAll(final Map<? extends String, ? extends V> map){
        if(map == null || map.isEmpty()){  inner.putAll(map); }
        else{
            for(final Entry<? extends String, ? extends V> entry :
                map.entrySet()){
                    inner.put(lower(entry.getKey()), entry.getValue());
            }
        }
    }
    @Override
    public V remove(final Object object){ return inner.remove(lower(object)); }
    @Override
    public boolean containsKey(final Object key){
        return inner.containsKey(lower(key));
    }
    @Override
    public V put(final String key, final V value){
        return inner.put(lower(key), value);
    }
}

Using this map, you can create the MultiMap using the Supplier methods in MultiMaps.

Example:

Map<String, Collection<String>> map = 
    new CaseInsensitiveForwardingMap<Collection<String>>();
Multimap<String, String> caseInsensitiveMultiMap = 
    Multimaps.newMultimap(map, new Supplier<Collection<String>>(){

      @Override
      public Collection<String> get(){ return Sets.newHashSet(); }

  });

Caveat: keySet() will return lowercase values only, regardless how the keys were entered.

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • 3
    Note that it's better to use the String.toUpperCase instead of String.toLowerCase for this, since some characters are mapped to several characters while being uppercased, but not while being lowercased. For instance, the character `\u00df` is mapped to the uppercase `\u0053\u0053`, whereas `\u0053\u0053` is mapped to the lowercase `\u0073\u0073`. But obviously `\u00df` isn't considered the the same as `\u0073\u0073` whereas it semantically is. – Olivier Grégoire Jan 21 '11 at 14:58
  • 1
    @OlivierGrégoire if you look into the source-code of `String.equalsIgnoreCase()` you will discover that you need to compare *both* the uppercase and lowercase form of a character to ignore case to be safe across all locales. Checking only one form will fail for some languages. – Gili Sep 30 '18 at 01:46
5

Couldn't you use a Map<String,List<Payload>> and give it a Comparator<String> which did a case-insensitive compare?

It appears that neither Google Collections nor Apache Collection frameworks have a multimap that accepts a Comparator for evaluating key equality.

David Bullock
  • 6,112
  • 3
  • 33
  • 43
  • 3
    Use [MultiMaps.newListMultiMap()](http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Multimaps.html#newListMultimap%28java.util.Map%2C%20com.google.common.base.Supplier%29) and supply a `TreeMap` with a custom comparator – Sean Patrick Floyd Jan 04 '11 at 11:19
  • the standard java equalsIgnoreCase calls toLowerCase internally – sds Jul 10 '12 at 16:47
3

You could define a case-insensitive String Comparator using a Collator. Then create a TreeMultimap with keys sorted by that Comparator.

Jared Levy
  • 1,986
  • 13
  • 12
1

No, but presumably you're using String keys? If so, why not just normalise all access to a regular multimap? For the 80% case, that'll be making all calls puts and gets lowercase the key.

For a full discussion of the issues with case-insensitive multimaps, see this google group discussion

GaryF
  • 23,950
  • 10
  • 60
  • 73