提问者:小点点

使用comparator.Comparing(HashMap::Get)作为比较器时出现意外行为


在https://java-programming.mooc.fi/part-10/2-interface-comparable上做练习‘文学’时,我发现了一个非常奇怪的行为:试图在HashMap中对键值对进行排序,而不将任何内容复制到树映射中。 我应该添加书籍,通过创建一个书籍类并将它们添加到一个列表中。 然而,我想尝试不创建一个新类,所以选择了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.comparate(bookshelf::get))是我的想法,它可以按照推荐的年龄对它们进行排序。

但是,存在一个意外的行为,即当书的名称是单个字符(“a”,“b”)时,程序也会按照字母顺序对键进行排序,就像我做了一个比较器一样,例如比较器。comparator.comparator(Bookshelf::Get)。thencomparator(/*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.

有人能解释一下这里发生了什么吗,在编译器级别,或者让我理解一下这个?


共3个答案

匿名用户

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

从上面的示例片段中,我们可以看到您正在尝试按bookshelf的键各自的值对它们进行排序。

这样做的问题是,两个书名可能映射到相同的年代推荐。 因为只有一个comparator,而且hashmap没有指定一致的顺序,所以对于相同的输入,您有可能得到不同的结果。

要改善这一点,您可以使用thencomparating来处理遇到重复值映射时的情况:

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

匿名用户

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

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

匿名用户

构建Entry的比较器,并使用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)"));