简体   繁体   English

使用 Comparator.comparing(HashMap::get) 作为比较器时的意外行为

[英]Unexpected behavior when using Comparator.comparing(HashMap::get) as a comparator

Doing the exercise 'Literature' onhttps://java-programming.mooc.fi/part-10/2-interface-comparable I discovered a very strange behavior when trying to sort key-value pairs in a HashMap, without copying anything to a TreeMap.https://java-programming.mooc.fi/part-10/2-interface-comparable上进行“文学”练习时,我在尝试对 HashMap 中的键值对进行排序时发现了一个非常奇怪的行为,而没有将任何内容复制到树状图。 I was supposed to add books, by making a Book class and adding them to a List.我应该通过制作一本书 class 并将它们添加到列表中来添加书籍。 However I wanted to try without making a new class, so opted for the HashMap.但是我想尝试不制作新的 class,所以选择了 HashMap。 My code was as follows:我的代码如下:

public class MainProgram {

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);

    Map<String, Integer> bookshelf = new HashMap<>();
    while (true) {


        System.out.println("Input the name of the book, empty stops: ");
        String bookName = scanner.nextLine();
        if (bookName.equals("")) {
            break;
        }
        System.out.println("Input the age recommendation: ");
        int age = Integer.valueOf(scanner.nextLine());

        bookshelf.put(bookName, age);
    }

    System.out.println(bookshelf.size() + " book" + (bookshelf.size() > 1 ? "s" : "") + " in total.");

    System.out.println("Books:");

    bookshelf.keySet().stream().sorted(Comparator.comparing(bookshelf::get)).forEach((key) -> System.out.println(key + " (recommended for " + bookshelf.get(key) + " year-olds or older)"));
}

}

using .sorted(Comparator.comparing(bookshelf::get)) was my idea of sorting them by the recommended age, which worked.使用.sorted(Comparator.comparing(bookshelf::get))是我按照推荐的年龄对它们进行排序的想法,这很有效。

However, there exists an unexpected behavior that when the book's name is a single character ("A","b"), the program would also sort the keys alphabetically as though i made a comparator like Comparator.comparing(bookshelf::get).thenComparing(/*keys in keyset*/) but would sometimes also sort like aAbB但是,存在一种意外行为,即当书名是单个字符(“A”、“b”)时,程序还会按字母顺序对键进行排序,就好像我做了一个比较器,比如Comparator.comparing(bookshelf::get).thenComparing(/*keys in keyset*/)但有时也会像aAbB一样排序

AA bb give unsorted results
AAA bbb give semi-sorted results in one or two buckets
AAAA bbbb give semi- or completely sorted results
AAAAA bbbbb and onward give unsorted results.

在此处输入图像描述

Can anybody explain what is happening here, at compiler level or somehow let me make sense of this?任何人都可以解释这里发生了什么,在编译器级别或以某种方式让我理解这一点吗?

bookshelf.keySet().stream().sorted(Comparator.comparing(bookshelf::get))

From the above snippet in your example, we can see that you're trying to sort the keys of bookshelf by their respective value.从您示例中的上述代码段中,我们可以看到您正在尝试按各自的值对bookshelf的键进行排序。

The issue with this is that two book names could be mapped to the same age recommendation.这样做的问题是两个书名可以映射到相同的年龄推荐。 Because you only have a single Comparator and because HashMap does not specify a consistent ordering, you have a chance at ending up with different results for the same inputs.因为您只有一个Comparator器,并且因为HashMap没有指定一致的排序,所以您有机会最终获得相同输入的不同结果。

To ameliorate this, you can use thenComparing to handle the case when duplicate value-mappings are encountered:为了改善这种情况,您可以使用thenComparing来处理遇到重复值映射的情况:

bookshelf.entrySet()
         .stream()
         .sorted(Map.Entry.<String, Integer>comparingByValue().thenComparing(Map.Entry.comparingByKey()))
         .forEach(entry -> System.out.println(entry.getKey() + " (recommended for " + entry.getValue() + " year-olds or older)"));

Build the Comparator of Entry and use Entry::getValue and Entry::getKey to sort by value then by key构建 Entry 的 Comparator 并使用Entry::getValueEntry::getKey按值排序,然后按键排序

Comparator<Entry<String, Integer>> cmp = Comparator.comparing(Entry::getValue);

bookshelf.entrySet()
         .stream()
         .sorted(cmp.thenComparing(Entry::getKey))
         .forEach(entry -> System.out.println(entry.getKey() + " (recommended for " + entry.getValue() + " year-olds or older)"));

This is happening since you are only using "key" to compare.发生这种情况是因为您仅使用“键”进行比较。 You should compare them by both "key" and "value".您应该通过“键”和“值”来比较它们。 This should work fine:这应该可以正常工作:

bookshelf.entrySet()
        .stream()
        .sorted(Map.Entry.<String,Integer>comparingByValue()
                .thenComparing(Map.Entry.comparingByKey()))
        .map(e -> e.getKey())
        .forEach((key) -> System.out.println(key + " (recommended for " + bookshelf.get(key) + " year-olds or older)"));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM