使用Java 8流 API 和lambda表达式来操作一个map

3
我有一个整数映射表,需要遍历所有的映射项并将它们添加到一个字符串中,但首先,我需要限制结果数量,然后根据映射表的值对它们进行排序(这比先排序再限制更有效)。
因此,为了实现这一点,我使用了Java 8的Stream API和Lambda表达式。以下是我编写的代码:
Map<Integer, Integer> allHighScoresPerLevel=highScores.get(levelId);
Set<Map.Entry<Integer, Integer>> sortedHighScores=allHighScoresPerLevel.entrySet()
               .stream().limit(Configuration.MAX_HIGHSCORES_DISPLAYED)
               .map((e)->e).sorted((o1,o2)->(o1.getValue().compareTo(o2.getValue())))
               .collect(Collectors.toCollection(TreeSet::new));

但我遇到了以下异常:
java.util.concurrent.ConcurrentHashMap$MapEntry cannot be cast to java.lang.Comparable

我明白,一个Map条目(Entry)没有扩展Comparable接口,因此没有自然的方法来对这些条目进行排序。但是,我在sorted方法中提供了一个比较器(lambda)来指示如何对这些条目进行排序。

那么我有两个问题:

1)正确的做法是什么?我做错了什么吗?

2)流(Stream)是线程安全的吗?我正在多线程环境下工作,所以我应该使用返回线程安全集合的收集器吗?


TreeSet 也是有序的,你需要在构造函数中传递一个比较器。 - Jorn Vernee
但是最后我使用了 Collectors.toCollection(TreeSet::new),这会创建一个已经排序好的新 TreeSet,对吗? - fgonzalez
不,TreeSet 也会在插入元素时尝试对其进行排序。您可以使用 List 替代,它保证元素的顺序被维护。 - Jorn Vernee
你能否添加一个有效输入和预期输出的示例? - k1133
1
.map((e)->e) 步骤的意义是什么?而且你不应该插入过时的大括号。在这里,已经有很多必要的嵌套大括号,额外的大括号并不能提高可读性。” - Holger
1个回答

9
你正在过度复杂化事情:TreeSet需要一个比较器,除非对象是可比的(Map.Entry不可比),并且排序将由TreeSet完成。
因此,可以采用以下方法(使用静态导入):
Set<Entry<Integer, Integer>> sortedHighScores =  allHighScoresPerLevel.entrySet().stream()
            .limit(Configuration.MAX_HIGHSCORES_DISPLAYED)
            .collect(toCollection(() -> new TreeSet<> (Entry.comparingByValue())));

您没有提供足够的信息来确定代码是否线程安全 - 但如果原始映射是线程安全的,那么您应该没问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接