Подтвердить что ты не робот

Компаратор, нечувствительный к регистру, разбивает мою TreeMap

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

То, что я думаю, что я сделал, это то, что я создал карту, которая сортируется по ее ключам, без учета регистра. Два разных элемента имеют не равные ключи (abc и abc), сравнение которых вернет 0. Я ожидал просто случайного упорядочения двух элементов. Тем не менее, команда:

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

привело к:

treeMap: {abc=Element2}

Ключ abc был переназначен значением Element2!

Может ли кто-нибудь объяснить, как это могло произойти, и если это действительное документированное поведение TreeMap?

4b9b3361

Ответ 1

Это происходит потому, что TreeMap считает элементы равными, если a.compareTo(b) == 0. Он документально подтвержден в JavaDoc для TreeMap (выделено мной):

Обратите внимание, что упорядочение, поддерживаемое древовидной картой, как и любая сортированная карта, а также наличие явного компаратора, должен соответствовать equals, если эта отсортированная карта должна правильно реализовать интерфейс карты. (См. Comparable или Comparator для точного определения, согласующегося с equals.) Это связано с тем, что интерфейс Map определен в терминах операции equals, но отсортированная карта выполняет все сопоставления ключей, используя ее compareTo (или compare), поэтому два ключа, которые по этому методу считаются равными, с точки зрения отсортированной карты равно. Поведение сортированного отображения хорошо определено, даже если его порядок не согласуется с equals; он просто не соблюдает общий контракт интерфейса карты.

Ваш компаратор не согласуется с равными.

Если вы хотите сохранить элементы с не равным, но равным-игнорируемому случаю, добавьте второй уровень проверки в ваш компаратор, чтобы использовать регистр, чувствительный к регистру:

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

        return o1.compareTo(o2);
    }

Ответ 2

Comparator, который вы передаете в TreeMap, определяет не только порядок ключей внутри Map, но также определяет, считаются ли две клавиши одинаковыми (они считаются идентичными, когда compare() возвращает 0).

Поэтому в ваших TreeMap, "abc" и "ABC" считаются идентичными клавишами. Map не разрешать идентичные ключи, поэтому второе значение Element2 перезаписывает первое значение Element1.

Ответ 3

Вам нужно убедиться, что равенство этих элементов карты согласуется с компаратором. Цитата из комментария к классу:

Обратите внимание, что упорядочение, поддерживаемое древовидной картой, как любая сортированная карта, и должен ли явный компаратор быть установленным, должно быть согласуется с равенствами, если эта отсортированная карта правильно реализовать интерфейс.

Ответ 4

Принятый ответ технически корректен, но не учитывает идиоматическое решение проблемы.

Вы должны использовать статический String.CASE_INSENSITIVE_ORDER, предоставленный компаратором или, по крайней мере, используя String.compareToIgnoreCase() внутри вашего собственного, чтобы рассмотреть, что такое .equal().

Для локальных чувствительных сравнений вы должны использовать что-то из java.text.Collator