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
の有効な動作であるかどうかを誰かが説明できますか?
これは、TreeMap
がa.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);
}
Comparator
に渡すTreeMap
は、Map
内のキーの順序を決定するだけでなく、2つのキーが同一と見なされるかどうかも決定します(compare()
は0
)を返します。
したがって、TreeMap
では、「abc」と「ABC」は同一のキーと見なされます。 Map
sは同一のキーを許可しないため、2番目の値Element2
は最初の値Element1
を上書きします。
そのマップの要素の同等性がコンパレータと一致していることを確認する必要があります。クラスのコメントからの引用:
ソートされたマップと同様に、ツリーマップによって維持される順序、および明示的なコンパレーターが提供されているかどうかは、このソートされたマップがインターフェースを正しく実装する場合、equalsと一致している必要があります。
受け入れられた答えは技術的には正しいですが、問題の慣用的な解決策を見逃しています。
静的 _String.CASE_INSENSITIVE_ORDER
_ コンパレータを使用するか、少なくとも String.compareToIgnoreCase()
を使用して.equal()
を検討する必要があります。
ロケールに依存する比較の場合は、 _Java.text.Collator
_ から何かを使用する必要があります