不区分大小写的比较器打破了我的树图

2022-09-02 02:40:00

A 我在我的使用打破了我打算的行为。请看下面的代码:ComparatorTreeMapTreeMap

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

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

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

结果是:

treeMap: {abc=Element2}

该键已重新分配值 !abcElement2

任何人都可以解释这是如何发生的,如果这是一个有效的,有记录的行为?TreeMap


答案 1

发生这种情况是因为认为元素相等,如果 .它记录在JavaDoc for TreeMap中(强调我的):TreeMapa.compareTo(b) == 0

请注意,如果树状图要正确实现 Map 接口,则树状图维护的排序(与任何排序映射一样)以及是否提供显式比较器都必须equals 一致。(请参阅 或 了解 与 的精确定义。之所以如此,是因为 Map 接口是根据操作定义的,但排序映射使用其 (or ) 方法执行所有键比较,因此从排序映射的角度来看,此方法视为相等的两个键是相等的。排序映射的行为是明确定义的,即使它的顺序与 ;它只是不遵守Map接口的总合同。ComparableComparatorequalsequalscompareTocompareequals

您的比较器与等值不一致。

如果要保留不等于但等于忽略大小写元素,请在比较器中放入第二级检查,以使用区分大小写的排序:

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

        return o1.compareTo(o2);
    }

答案 2

传递给 a 的 U 不仅决定了 内键的顺序,还决定了两个键是否被视为相同(返回时它们被视为相同)。ComparatorTreeMapMapcompare()0

因此,在您的 中,“abc”和“ABC”被认为是相同的键。不允许使用相同的键,因此第二个值将覆盖第一个值 。TreeMapMapElement2Element1


推荐