当使用Comparator.compararing(HashMap::get)作为比较器时,出现了意外的行为。

问题描述 投票:2回答:1

做 "文学 "练习 https:/java-programming.mooc.fipart-102-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

谁能解释一下这里发生了什么,在编译器层面,或者让我理解一下?

java sorting hashmap java-stream method-reference
1个回答
8
投票
bookshelf.keySet().stream().sorted(Comparator.comparing(bookshelf::get))

从上面的示例片段中,我们可以看到,你正试图对HashMap中的键值进行排序,而不需要......。bookshelf 由它们各自的值。

这样做的问题是,两个书名可以被映射到同一个年龄段的推荐上。因为您只有一个 Comparator 又因为 HashMap 没有指定一个一致的顺序,您有可能对相同的输入产生不同的结果。

为了改善这种情况,您可以使用 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)"));

6
投票

建立条目比较器并使用 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)"));

4
投票

这是因为你只用 "键 "来比较。你应该同时用 "key "和 "value "来比较。这样应该就可以了。

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)"));
© www.soinside.com 2019 - 2024. All rights reserved.