简体   繁体   中英

Java TreeMap (comparator) and get method ignoring the comparator

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.

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.

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.

您必须使用两个单独的TreeMaps,具有相同的内容但不同的比较器。

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);
    }
};
                                                    }

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM