使用 Comparator.compareing(HashMap::get) 作为比较器时出现意外行为

做关于 https://java-programming.mooc.fi/part-10/2-interface-comparable 的“文献”练习时,我发现在尝试在HashMap中对键值对进行排序时,没有将任何内容复制到TreeMap时,发现了一个非常奇怪的行为。我应该通过创建一个Book类并将它们添加到List来添加书籍。但是,我想在不创建新类的情况下尝试,因此选择了HashMap。我的代码如下:

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

}

使用是我的想法,按推荐的年龄对它们进行排序,这很有效。.sorted(Comparator.comparing(bookshelf::get))

但是,存在一种意外的行为,即当书名是单个字符(“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.

enter image description here

任何人都可以解释一下这里正在发生的事情,在编译器级别,或者以某种方式让我理解这一点?


答案 1
bookshelf.keySet().stream().sorted(Comparator.comparing(bookshelf::get))

从示例中的上述代码段中,我们可以看到您正在尝试按键的相应值对 键进行排序。bookshelf

这样做的问题是,两个书名可以映射到相同的年龄推荐。由于您只有一个,并且因为 没有指定一致的顺序,因此您有可能为相同的输入获得不同的结果。ComparatorHashMap

为了改善这种情况,您可以使用 来处理遇到重复值映射的情况: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)"));

答案 2

发生这种情况是因为您仅使用“键”进行比较。您应该通过“键”和“值”来比较它们。这应该工作正常:

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

推荐