web-dev-qa-db-ja.com

大文字と小文字を区別しないコンパレータがTreeMapを壊す

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

私がやったと思うのは、大文字と小文字を区別せず、キーでソートされたマップを作成したことです。 2つの異なる要素には、等しくないキー(abcおよびABC)があり、それらの比較により0が返されます。 2つの要素のランダムな順序だけを期待しました。しかし、コマンド:

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

をもたらしました:

treeMap: {abc=Element2}

キーabcに値Element2が再割り当てされました!

これがどのように発生するのか、そしてそれが文書化されたTreeMapの有効な動作であるかどうかを誰かが説明できますか?

21
javaxian

これは、TreeMapa.compareTo(b) == 0の場合に要素が等しいと見なすために発生します。 TreeMapのJavaDoc (私の強調)に文書化されています。

ソートされたマップと同様に、ツリーマップによって維持される順序、および明示的なコンパレーターが提供されているかどうかにかかわらず、これがソートされている場合、equalsと一致している必要があります。 mapはMapインターフェースを正しく実装することです。 (Comparableとの整合性の正確な定義については、Comparatorまたはequalsを参照してください。)これは、Mapインターフェースがequals操作に関して定義されているためです。 、しかしソートされたマップはそのcompareTo(またはcompare)メソッドを使用してすべてのキー比較を実行するため、このメソッドで等しいと見なされる2つのキーは、ソートされたマップの観点から、equal。ソートされたマップの動作は、その順序がequalsと一致しなくても明確に定義されています。 Mapインターフェースの一般的な規約に従わないだけです。

あなたのコンパレーターはイコールと一致していません。

Not-equal-but-equal-ignoring-case要素を保持する場合は、2番目のレベルのチェックをコンパレーターに入れて、大文字と小文字を区別する順序を使用します。

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

        return o1.compareTo(o2);
    }
36
Andy Turner

Comparatorに渡すTreeMapは、Map内のキーの順序を決定するだけでなく、2つのキーが同一と見なされるかどうかも決定します(compare()0)を返します。

したがって、TreeMapでは、「abc」と「ABC」は同一のキーと見なされます。 Mapsは同一のキーを許可しないため、2番目の値Element2は最初の値Element1を上書きします。

12
Eran

そのマップの要素の同等性がコンパレータと一致していることを確認する必要があります。クラスのコメントからの引用:

ソートされたマップと同様に、ツリーマップによって維持される順序、および明示的なコンパレーターが提供されているかどうかは、このソートされたマップがインターフェースを正しく実装する場合、equalsと一致している必要があります。

4
michid

受け入れられた答えは技術的には正しいですが、問題の慣用的な解決策を見逃しています。

静的 _String.CASE_INSENSITIVE_ORDER_ コンパレータを使用するか、少なくとも String.compareToIgnoreCase() を使用して.equal()を検討する必要があります。

ロケールに依存する比較の場合は、 _Java.text.Collator_ から何かを使用する必要があります

1
user177800