При сортировке карты по значению некоторые значения отсутствуют. Чем вызвано такое странное поведение?


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

public class WordFrequency {
    public static String sentence = "one three two two three three four four four";
    public static Map<String, Integer> map;

    public static void main(String[] args) {
        map = new HashMap<>();
        String[] words = sentence.split("\s");

        for (String word : words) {
            Integer count = map.get(word);
            if (count == null) {
                count = 1;
            } else {
                ++count;
            }
            map.put(word, count);
        }

        Comparator<String> myComparator = new Comparator<String>() {

            @Override
            public int compare(String s1, String s2) {
                if (map.get(s1) < map.get(s2)) {
                    return -1;
                } else if (map.get(s1) > map.get(s2)) {
                    return 1;
                } else {
                    return 0;
                }
            }

        };
        SortedMap<String, Integer> sortedMap = new TreeMap<String, Integer>(myComparator);
        System.out.println("Before sorting: " + map);
        sortedMap.putAll(map);
        System.out.println("After Sorting based on value:" + sortedMap);

    }
}

Вывод:

Before sorting: {two=2, one=1, three=3, four=3}
After sorting based on value:{one=1, two=2, three=3}

Ожидаемый Результат:

{one=1, two=2, four=3,three=3}
3 6

3 ответа:

Ваш метод compare не подчиняется контракту интерфейса Map, так как он сравнивает значения вместо ключей. Ваша реализация приводит к тому, что два ключа с одинаковым значением считаются одним и тем же ключом. Поэтому ваш sortedMap не содержит ключа "четыре", который имеет то же значение, что и ключ" три".

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

Ссылка на карту дерева

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

    Comparator<String> myComparator = new Comparator<String>() {

        @Override
        public int compare(String s1, String s2) {
            if (map.get(s1) < map.get(s2)) {
                return -1;
            } else if (map.get(s1) > map.get(s2)) {
                return 1;
            } else {
                return s1.compareTo(s2);
            }
        }

    };

Это должно дать вам результат:

After sorting based on value:{one=1, two=2, four=3, three=3}

Так как four<three основано на естественном порядке строк.

Из-за вашего compare() стоит рассматривать значения только в Map. Тогда three=3, four=3 имеет то же значение 3. Тогда те считают дубликатами, когда они добавляют к TreeMap.

Это потому, что ваша реализация говорит TreeMap, что map[три] и map[четыре] по сути являются одним и тем же элементом, потому что они "равны" друг другу в соответствии с вашим компаратором.

Измените "return 0" в компараторе на " return s1.compareTo (s2)", и у вас будет

Before sorting: {two=2, one=1, three=3, four=3}
After Sorting based on value:{one=1, two=2, four=3, three=3}

(я думаю, вы можете понять, почему "четыре" в этом случае стоит перед "три")