不区分大小写的比较器会破坏我的TreeMap

时间:2017-10-24 13:41:11

标签: java comparator

我在Comparator中使用的TreeMap打破了我为TreeMap所做的行为。请查看以下代码:

TreeMap<String, String> treeMap = new TreeMap<>(new Comparator<String>() {
    public int compare(String o1, String o2) {
        return o1.toLowerCase().compareTo(o2.toLowerCase());
    }
});
treeMap.put("abc", "Element1");
treeMap.put("ABC", "Element2");

我认为我所做的是我创建了一个按键排序的地图,不区分大小写。两个不同的元素具有不相等的键(abcABC),其比较将返回0。我期待这两个元素的随机排序。然而,命令:

System.out.println("treeMap: " + treeMap);

导致:

treeMap: {abc=Element2}

abc已重新分配值Element2

任何人都可以解释这是如何发生的,以及它是TreeMap的有效记录行为吗?

4 个答案:

答案 0 :(得分:35)

这是因为TreeMap认为a.compareTo(b) == 0时元素相等。它记录在the JavaDoc for TreeMap(强调我的)中:

  

请注意,树形图维护的顺序,如任何有序地图,以及是否提供显式比较器,必须与equals 一致,如果此有序地图是正确实现Map接口。 (请参阅ComparableComparator以获得与equals一致的精确定义。)这是因为Map接口是根据equals操作定义的,但是已排序map使用其compareTo(或compare)方法执行所有关键比较,因此从排序后的地图的角度来看,此方法认为相同的两个键 <强>等于即可。即使排序与equals不一致,也可以很好地定义有序映射的行为。它只是没有遵守Map接口的一般合同。

你的比较器与equals不一致。

如果你想保持不等于但等于忽略大小的元素,那么对你的比较器进行第二级检查,使用区分大小写的顺序:

    public int compare(String o1, String o2) {
        int cmp = o1.toLowerCase().compareTo(o2.toLowerCase());
        if (cmp != 0) return cmp;

        return o1.compareTo(o2);
    }

答案 1 :(得分:12)

您传递给Comparator的{​​{1}}不仅会确定TreeMap内的密钥排序,还会确定两个密钥是否相同(Map时它们被视为相同{1}}返回compare())。

因此,在0,&#34; abc&#34;和&#34; ABC&#34;被认为是相同的键。 TreeMap不允许使用相同的密钥,因此第二个值Map会覆盖第一个值Element2

答案 2 :(得分:4)

您需要确保该映射元素的相等性与比较器一致。引用课堂评论:

  

请注意,树图保存的顺序与任何有序地图一样,   以及是否提供明确的比较器必须是   如果这个有序映射是正确的,则与equals一致   实现界面。

答案 3 :(得分:1)

接受的答案在技术上是正确的,但是错过了解决问题的惯用方法。

您应该使用提供的静态String.CASE_INSENSITIVE_ORDER比较器,或者至少使用自己内部的String.compareToIgnoreCase()来考虑.equal()

对于区域设置敏感的比较,您应该使用java.text.Collator

中的内容
相关问题