使用 Comparator.comparing(HashMap::get) 作为比较器时的意外行为
Unexpected behavior when using Comparator.comparing(HashMap::get) as a comparator
在 https://java-programming.mooc.fi/part-10/2-interface-comparable 上做练习 'Literature' 我在尝试对 HashMap 中的键值对进行排序时发现了一个非常奇怪的行为,而没有将任何内容复制到 TreeMap。我应该通过制作一本书 class 并将它们添加到列表来添加书籍。但是我想尝试不创建新的 class,所以选择了 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.
任何人都可以在编译器级别解释这里发生了什么,或者以某种方式让我理解这一点吗?
发生这种情况是因为您仅使用 "key" 进行比较。你应该用 "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)"));
bookshelf.keySet().stream().sorted(Comparator.comparing(bookshelf::get))
从您示例的上述代码片段中,我们可以看到您正在尝试根据各自的值对 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)"));
构建Entry的比较器,使用Entry::getValue
和Entry::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)"));
在 https://java-programming.mooc.fi/part-10/2-interface-comparable 上做练习 'Literature' 我在尝试对 HashMap 中的键值对进行排序时发现了一个非常奇怪的行为,而没有将任何内容复制到 TreeMap。我应该通过制作一本书 class 并将它们添加到列表来添加书籍。但是我想尝试不创建新的 class,所以选择了 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.
任何人都可以在编译器级别解释这里发生了什么,或者以某种方式让我理解这一点吗?
发生这种情况是因为您仅使用 "key" 进行比较。你应该用 "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)"));
bookshelf.keySet().stream().sorted(Comparator.comparing(bookshelf::get))
从您示例的上述代码片段中,我们可以看到您正在尝试根据各自的值对 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)"));
构建Entry的比较器,使用Entry::getValue
和Entry::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)"));