簡體   English   中英

不區分大小寫的比較器會破壞我的TreeMap

[英]Case-insensitive Comparator breaks my TreeMap

我在TreeMap使用的Comparator破壞了我對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行為?

這是因為如果a.compareTo(b) == 0 TreeMap認為元素相等。 它在JavaDoc for TreeMap (強調我的)中有記錄:

請注意,如果此有序映射要正確實現Map接口,則樹映射維護的順序(如任何有序映射)以及是否提供顯式比較器必須與equals一致 (見ComparableComparator為一致的精確定義與equals )。這是因為Map接口在來定義equals的操作,但有序映射使用它的執行所有的鍵比較compareTo (或compare )方法,於是兩個從排序映射的角度來看, 通過此方法被視為相等的鍵是 相等的 即使排序與equals不一致,也可以很好地定義有序映射的行為。 它只是不遵守Map接口的一般合同。

你的比較器與equals不一致。

如果你想保持不等於但等於忽略大小的元素,那么對你的比較器進行第二級檢查,使用區分大小寫的順序:

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

        return o1.compareTo(o2);
    }

傳遞給TreeMapComparator不僅確定了Map內部鍵的順序,還確定兩個鍵是否被認為是相同的(當compare()返回0時它們被認為是相同的)。

因此,在您的TreeMap ,“abc”和“ABC”被視為相同的鍵。 Map s不允許相同的鍵,因此第二個值Element2覆蓋第一個值Element1

您需要確保該映射元素的相等性與比較器一致。 引用課堂評論:

請注意,如果此有序映射要正確實現接口,則樹映射維護的排序(如任何已排序的映射,以及是否提供顯式比較器)必須與equals一致。

接受的答案在技術上是正確的,但錯過了解決問題的慣用方法。

您應該使用提供的靜態String.CASE_INSENSITIVE_ORDER比較器或至少使用自己內部的String.compareToIgnoreCase()來考慮.equal()

對於區域設置敏感的比較,您應該使用java.text.Collator

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM