Java TreeMap (компаратор) и метод get, игнорирующий компаратор

public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {

        public int compare(String s1, String s2) {
            return s1.compareToIgnoreCase(s2);
        }
    };

private Map< String, Animal > _animals = new TreeMap< String, Animal >(ID_IGN_CASE_COMP);

Моя проблема в том, как использовать метод get(id), игнорируя данный компаратор. Я хочу, чтобы карта была упорядочена без учета регистра, но я хочу, чтобы она была чувствительна к регистру, когда я извлекаю значения по заданному ключу.


person d0pe    schedule 23.11.2009    source источник
comment
Как это должно работать? TreeMap должен будет представлять два разных порядка. Это невозможно.   -  person jitter    schedule 23.11.2009
comment
короткий комментарий: вам не нужно создавать этот Comparator‹String›, он уже существует в классе java.lang.String: String.CASE_INSENSITIVE_ORDER   -  person Pierre    schedule 23.11.2009
comment
Я не вижу проблемы. Он получит значение с помощью клавиши String с учетом регистра.   -  person EJB    schedule 23.11.2009
comment
Разве это невозможно сделать, переопределив метод get?   -  person d0pe    schedule 23.11.2009


Ответы (7)


Я думаю, что ответ прост. Реализуйте свой собственный компаратор, который выполняет сортировку без учета регистра, но НЕ возвращает 0 для "A" и "a"... отсортируйте и их тоже.

Проблема в том, что ваш компаратор возвращает 0 для случая сравнения ("A", "a" ), что означает, что это тот же ключ, что и карта.

Используйте компаратор, например:

public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {

    public int compare(String s1, String s2) {
        int result = s1.compareToIgnoreCase(s2);
        if( result == 0 )
            result = s1.compareTo(s2);
        return result;
    }
};

Тогда все ключи будут введены независимо от регистра, а "a" и "A" все равно будут отсортированы вместе.

Другими словами, get("a") даст вам значение, отличное от get("A")... и они оба будут отображаться в итераторах keySet(). Они будут просто отсортированы вместе.

person PSpeed    schedule 23.11.2009
comment
это звучит хорошо, спасибо. Я проверю это, так как понял, что мне также нужно учитывать это, когда я использую метод put. Так как мне тоже нужно отличать А от а. Я собираюсь протестировать его и дам вам знать. - person d0pe; 23.11.2009
comment
Большое спасибо, это решило обе мои проблемы. Это был как раз тот ответ, который я хотел. Большое спасибо еще раз :) - person d0pe; 23.11.2009
comment
Рад, что это сработало. Иногда простой ответ является правильным. :) - person PSpeed; 23.11.2009
comment
С коллекциями Google: static final Comparator‹String› ID_IGN_CASE_COMP = Ordering.from(String.CASE_INSENSITIVE_ORDER).compound(Ordering.natural()); - person Kevin Bourrillion; 23.11.2009

В TreeMap добавление двух ключей a и b (в таком порядке), чтобы сравнение (a, b) возвращало 0, приведет к тому, что последняя добавленная запись (b) перезапишет первую (a).

В вашем случае это означает, что нечувствительный к регистру get(id) никогда не будет использован.

цитирование http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html

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

Это, вероятно, не то, что вы хотите.

Если карта сравнительно мала и вам не нужно много раз извлекать отсортированные записи, решение состоит в том, чтобы использовать HashMap (или TreeMap без явной установки компаратора) и сортировать записи без учета регистра, когда они вам нужны. упорядоченный.

person Buhb    schedule 23.11.2009

Для этого вам придется использовать два отдельных TreeMaps с одинаковым содержимым, но разными компараторами.

person Tom Bartel    schedule 23.11.2009
comment
Это просто занимает слишком много места ... Я бы лучше взял набор ключей и сравнил их, но искал самый простой способ - person d0pe; 23.11.2009

может быть, это сделает работу:

    new Comparator<String>(){
    public int compare(String s1, String s2)
    {
        String s1n = s1.toLowerCase();
        String s2n = s2.toLowerCase();

        if(s1n.equals(s2n))
        {
            return s1.compareTo(s2);
        }
        return s1n.compareTo(s2n);
    }
};
                                                    }
person Mirek Pluta    schedule 23.11.2009
comment
+1 Вы должны использовать нижний регистр для каждого символа, а не для всей строки. Но направление верное. Он будет вести себя иначе, чем компаратор, нечувствительный к регистру (клавиши a, A равны для компаратора, нечувствительного к регистру). - person Thomas Jung; 23.11.2009

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

Существует множество свободно используемых реализаций мультикарт, таких как Общие коллекции, Коллекции Google и т. д.

person dfa    schedule 23.11.2009

В дополнение ко всем остальным ответам и согласию с тем, что невозможно иметь единую структуру TreeMap с разными компараторами:

Из вашего вопроса я понимаю, что у вас есть два требования: модель данных должна быть чувствительной к регистру (вам нужны значения с учетом регистра при использовании get()), ведущий должен быть нечувствительным к регистру (вам нужен порядок с учетом регистра, представление - это просто предположение ).

Предположим, мы заполняем карту отображениями (aa,obj1), (aA,obj2), (Aa,obj3), (AA,obj4). Итератор предоставит значения в следующем порядке: (obj4, obj3, obj2, obj1)(*). Какой порядок вы ожидаете, если карта была заказана без учета регистра? Все четыре ключа будут равны, а порядок не определен. Или вы ищете решение, которое разрешало бы набор {obj1, obj2, obj3, obj4} для ключа «AA»? Но это другой подход.

SO призывает сообщество быть честным: поэтому мой совет на данный момент — еще раз взглянуть на ваше требование :)

(*) не проверено, предполагается, что 'A' ‹ 'a' = true.

person Andreas Dolk    schedule 23.11.2009

Используйте floorEntry и затем higherEntry в цикл для поиска записей без учета регистра; остановитесь, когда найдете точное совпадение ключей.

person Andrew Duffy    schedule 23.11.2009
comment
Как это работает на самом деле? Я получаю часть, где floorEntrry вернет мне искомый ключ, но если искомый ключ был aa1, он может найти Aa1 или AA1, более высокая запись дает мне один из других - person d0pe; 23.11.2009
comment
Вы правы - это не сработает, так как вам понадобится карта для использования компаратора с учетом регистра (не то, что вы хотите), а запуск с key.toLowerCase(Locale) не будет надежным для разных языков. Принятый ответ - это способ получить то, что вы хотите. - person Andrew Duffy; 23.11.2009