Введите TreeMap, возвращающий ноль - PullRequest
7 голосов
/ 24 февраля 2012

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

int i = 0;
        for (Map.Entry<String, Integer> es : sortedMap.entrySet()){
            if (i >= 10) {
                break;
            }

            if (sortedMap.containsKey(es.getKey())){
                System.out.println(es.getKey() + ":" + sortedMap.get(es.getKey()));
            } else {
                System.out.println("Key " + es.getKey() + " does not exist, yet...");
                System.out.println("This does work: " + es.getKey() + ":" + es.getValue());
                System.out.println("This does NOT work: " + es.getKey() + ":" + sortedMap.get(es.getKey()));
            }
            i++;
        }

и получил следующие результаты:

SOAP:967
'excerpt'::679
'type'::679
Key 'author_url': does not exist, yet...
This does work: 'author_url'::679
This does NOT work: 'author_url'::null
'date'::679
Android:437
TLS:295
message:283
server:230
monthly:215
<<<<<<<<<<<<<<<<<<<<DUMPING MAP!
{SOAP=967, 'excerpt':=679, 'type':=679, 'author_url':=679, 'date':=679, Android=437, TLS=295, message=283, server=230, monthly=215...

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

Итак, мой вопрос заключается в следующем: почему я получаю нулевое значение при использовании ключа для непосредственного получения (ключа) из TreeMap, но EntrySet возвращает правильный ключа стоимость?

Вот мой компаратор, так как я упорядочиваю по Integer:

class ValueComparator implements Comparator<Object> {

  Map<String, Integer> base;
  public ValueComparator(Map<String, Integer> base) {
      this.base = base;
  }

  public int compare(Object a, Object b) {

    if ((Integer) base.get(a) < (Integer) base.get(b)) {
      return 1;
    } else if ((Integer) base.get(a) == (Integer) base.get(b)) {
      return 0;
    } else {
      return -1;
    }
  }
}

И TreeMap построен следующим образом:

ValueComparator bvc =  new ValueComparator(allMatches);
TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>(bvc);
//Sort the HashMap
sortedMap.putAll(allMatches);

Где allMatches - это HashMap<String, Integer>

Ответы [ 4 ]

7 голосов
/ 25 февраля 2012

Из порядка итераций, которые показывает TreeMap, это определенно тот случай, когда вы использовали пользовательский Comparator. [В противном случае итерация была бы в лексикографическом порядке]

Обратите внимание, что согласно javadocs :

Разработчик должен убедиться, что sgn (сравнить (x, y)) == -sgn (сравнить (y, х)) для всех х и у. (Это означает, что сравнение (x, y) должно бросить исключение тогда и только тогда, когда сравнение (y, x) вызывает исключение.)

Разработчик также должен убедиться, что отношение является транзитивным: ((сравнение (x, y)> 0) && (сравнение (y, z)> 0)) подразумевает сравнение (x, z)> 0.

Наконец, разработчик должен убедиться, что сравнение (x, y) == 0 подразумевает что sgn (сравнить (x, z)) == sgn (сравнить (y, z)) для всех z.

Если ваш Comparator не применяет эти правила - поведение не определено, как могут показаться странные результаты - как вы видите.

РЕДАКТИРОВАТЬ: [как ответ на отредактированный вопрос]
Ваш разделитель использует идентификатор [operator==] для проверки двух целых чисел.
Обратите внимание, что Integer является объектом - и, следовательно, operator== вернет true, только если это тот же объект.
Вы должны использовать equals(), чтобы проверить, идентичны ли два целых числа - или даже лучше - используйте Integer.compareTo()

3 голосов
/ 25 февраля 2012

Ваша самая большая проблема в том, что использование == вместо .equals в компараторе значений разрушает вещи, потому что разные ключи отображаются на разные Integer объекты с одинаковым intValue(), что отбрасывает еще больше вещей непредсказуемо.

Но если вы исправите это, то ваш TreeMap не позволит вам вставить несколько ключей с одинаковым значением, что почти наверняка также приведет к легким поломкам.

Лучшим решением было бы что-то вроде this , но в основном вы должны заполнить карту без сортировки по значениям, отсортировать entrySet, а затем скопировать записи (по порядку) на карту как LinkedHashMap, который не нуждается в компараторе, а просто сохраняет записи в порядке вставки.

Вы могли бы изменить свой компаратор, чтобы при одинаковых значениях он также сравнивал ключи. Это, по крайней мере, позволит вам вставить несколько ключей с одним и тем же значением ... но это все еще действительно хакерское решение, которое намного более рискованно, чем решение на основе LinkedHashMap, как описано выше.

1 голос
/ 27 февраля 2012

Проблема решена:

class ValueComparator implements Comparator<Object> {

Map<String, Integer> base;

public ValueComparator(Map<String, Integer> base) {
    this.base = base;
}

public int compare(Object a, Object b) {

    if (((Integer) base.get(a)).intValue() < ((Integer) base.get(b)).intValue()) {
        return 1;
    } else if ( ((Integer) base.get(a)).intValue() == ((Integer) base.get(b)).intValue()) {
        return ((String)a).compareTo(((String)b));
    } else {
        return -1;
    }
}
}

Это связано с дополнительным преимуществом возврата ключей с одинаковым значением в алфавитном порядке.

0 голосов
/ 25 февраля 2012

У вас должно быть просто:

class ValueComparator implements Comparator<Integer> {


  public int compare(Integer a, Integer b) {
      return a.compareTo(b);
  }
}

Далее вам нужно инициализировать древовидную карту с помощью компаратора и добавить все ваши элементы:

Древовидная карта

...