views:

1131

answers:

7
public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {

        public int compare(String s1, String s2) {
            return s1.compareToIgnoreCase(s2);
        }
    };

private Map< String, Animal > _animals = new TreeMap< String, Animal >(ID_IGN_CASE_COMP);

My problem is, how to use method get(id) ignoring the given comparator. I want the map to be order by Case Insensitive but, I want it to be case sensitive when I fetch the values by a given key.

+1  A: 

You'll have to use two separate TreeMaps for that, with the same contents but different comparators.

Tom Bartel
Thats just takes too much space... I would rather get a set of keys and compare them but was looking for an easiest way
d0pe
+1  A: 

maybe it'll do the job:

    new Comparator<String>(){
 public int compare(String s1, String s2)
 {
  String s1n = s1.toLowerCase();
  String s2n = s2.toLowerCase();

  if(s1n.equals(s2n))
  {
   return s1.compareTo(s2);
  }
  return s1n.compareTo(s2n);
 }
};
             }
Luno
+1 You have to do lowerCase for every character not the whole string. But direction is right. It will behave differently from the case insensitive comparator (keys "a", "A" are equal for the case insensitive comparator).
Thomas Jung
A: 

Use floorEntry and then higherEntry in a loop to find the entries case-insensitively; stop when you find the exact key match.

Andrew Duffy
How does this really work? I get the part where floorEntrry would return me the searched key but, if the key searched was "aa1" it could find "Aa1" or "AA1", higher entry giving me one of the other
d0pe
You're right - it won't work as you would need the map to use a case-sensitive comparator (not what you want) and starting at key.toLowerCase(Locale) wouldn't be reliable for different languages. The accepted answer is the way to get what you want.
Andrew Duffy
+4  A: 

In a TreeMap, adding two keys a and b (in that order) so that compare(a, b) returns 0 will result in that the latest added entry (b) will overwrite the first one (a).

In your case, this means that there will never be any use for case insensitive get(id).

quoting http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html

Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.

This is probably not what you want.

If the map is comparably small and you don't need to fetch the sorted entries very many times, a solution is to use a HashMap (or a TreeMap without explicitly setting the comparator), and sort the entries case-insensitively when you need them ordered.

Buhb
+1  A: 

you need a multimap: each entry of this multimap keeps the case insensitive keys and aanother map with the original keys as value.

There are many freely usable implementations of multimaps such as Common Collections, Google Collections, etc

dfa
+4  A: 

I think the answer is easy. Implement your own comparator that does a case insensitive sort but does NOT return 0 for "A" and "a"... sort them too.

The issue is that your comparator returns 0 for the compare( "A", "a" ) case which means it is the same key as far as the map is concerned.

Use a comparator like:

public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {

    public int compare(String s1, String s2) {
        int result = s1.compareToIgnoreCase(s2);
        if( result == 0 )
            result = s1.compareTo(s2);
        return result;
    }
};

Then all keys will go in regardless of case and "a" and "A" will still be sorted together.

In other words, get("a") will give you a different value from get("A")... and they will both show up in keySet() iterators. They will just be sorted together.

PSpeed
this sounds good, thank you. I will test it since I realized I also need to take this in consideration when I use the put method. Since I need to distinguish "A" from "a" too. I'm gonna test it and let you know.
d0pe
Thanks alot, it resolved both my problems. This was just the answer I wanted. Much thanks once again :)
d0pe
Glad it worked. Sometimes the simple answer is the right one. :)
PSpeed
With google collections: static final Comparator<String> ID_IGN_CASE_COMP = Ordering.from(String.CASE_INSENSITIVE_ORDER).compound(Ordering.natural());
Kevin Bourrillion
A: 

In addition to all the other answers and agreeing, that it is impossible to have a single TreeMap structure with different comparators:

From your question I understand that you have two requirements: the data model shall be case sensitive (you want the case sensitive values when you use get()), the presenter shall be case insensitive (you want an case sensitive ordering, presentation is just an assumption).

Let's assume, we populate the Map with the mappings (aa,obj1), (aA,obj2), (Aa,obj3), (AA,obj4). The iterator will provides the values in the order: (obj4, obj3, obj2, obj1)(*). Now which order do you expect if the map was ordered case-insensitive? All four keys would be equal and the order undefined. Or are you looking for a solution that would resolve the collection {obj1, obj2, obj3, obj4} for the key 'AA'? But that's a different approach.

SO encourages the community to be honest: therefore my advice at this point is to look at your requirement again :)

(*) not tested, assumed that 'A' < 'a' = true.

Andreas_D