【问题标题】:Retrieve fixed number of entries around an entry in a map sorted by values在按值排序的映射中检索条目周围的固定数量的条目
【发布时间】:2018-02-20 16:56:15
【问题描述】:

POJO 即。入口.java 代表排行榜中的一个条目。 Position 是排行榜中的位置,1是得分最高的用户

public class Entry {

    private String uid;
    private int score;
    private int position;

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + score;
        result = prime * result + ((uid == null) ? 0 : uid.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;

        if (!(obj instanceof Entry))
            return false;

        Entry other = (Entry) obj;
        if (score != other.score)
            return false;
        if (uid == null) {
            if (other.uid != null)
                return false;
        } else if (!uid.equals(other.uid))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Entry [uid=" + uid + ", score=" + score + ", position=" + position + "]";
    }
}

这些条目存储在一个类中的 Map 中,如下所示:

public class GameDefault {

Map<String, Entry> leaderBoardUserEntryMap;

public void submitScore(String uid, int score) {

        Entry newEntry = new Entry(uid, score);
        leaderBoardUserEntryMap.put(uid, newEntry);
    }

public List<Entry> getLeaderBoard(String uid) {

    /* Option-3 : A Map of uid-Entry */
    leaderBoardUserEntryMap.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.comparing(Entry::getScore, Integer::compare).reversed()))
                .filter(/*What to put here*/);

        return null;
    }
}

getLeaderBoard() 方法应该返回

最多两个条目得分大于用户(用户 在排行榜中的用户上方),用户自己的 entry 和 max 两个紧跟在用户之后的条目 排行榜

.

我不知道使用谓词来准确返回 5 个条目,包括正在搜索的条目。另一个方面是性能,因为排行榜可以有数十万个条目。

**********Edit-1**********

@nullpointer 提供的以下 sn-p 可以解决问题,但我有一些想法

List<GameEntry> selectedEntries =  leaderBoardUserEntryMap.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Comparator.comparing(GameEntry::getScore, Integer::compare)
                    .reversed())).map(Map.Entry::getValue).collect(Collectors.toList());

int indexOfnewEntry = selectedEntries.indexOf(leaderBoardUserEntryMap.get(uid));
return  selectedEntries.subList(indexOfnewEntry-2,indexOfnewEntry+2);

注意:leaderBoardUserEntryMap 可以有数百万个条目

  • indexOfnewEntry 和 +- 2 会导致IndexOutOfBoundsException,防范起来似乎有点繁琐,这里有什么最佳方法吗?

  • 使用 parallelStream() 会导致问题吗?

    列表条目 = leaderBoardUserEntryMap.entrySet().parallelStream().sorted(Map.Entry.comparingByValue(Comparator.comparing(Entry::getScore, Integer::compare).reversed())).parallel()。 map(Map.Entry::getValue).collect(Collectors.toList());

【问题讨论】:

  • 如果我对您的理解正确,您正在尝试使用您的自定义比较器返回顶级玩家的 5 个元素的列表!您是否在流中尝试过.limit(5) 方法
  • P.s 你不需要过滤器,因为你返回的是一个排序的流!您只需要将检索到的元素数量限制为 5,然后使用收集器返回一个列表。如果你明白了,请告诉我;如果没有,我可以添加答案;欢呼
  • 不,它不是前 5 个条目。虽然集合应该按分数降序排序,但找到一个与 uid 相关的条目,然后检索上面的 2 个和下面的 2 个并返回所有 5 个。检查答案上的 @yogesh 的 cmets

标签: java collections java-8 java-stream


【解决方案1】:

Stream#limit 将帮助您限制在您创建的反向列表中查找顶级 N(5) 用户,您还可以使用这些值映射 List> 并最终从中收集 List&lt;Entry&gt;

return leaderBoardUserEntryMap.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Comparator.comparing(Entry::getScore, Integer::compare).reversed()))
            .limit(5).map(Map.Entry::getValue).collect(Collectors.toList());

编辑:感谢@Yogesh 提供的用例

假设有 100 个用户,正在搜索的用户是 93。 列表应返回 91、92、93、94、95。此解决方案将返回 1、2、 3、4、5

由于用例是在当前条目周围有一个subList,因此可以将其修改为:

List<GameEntry> selectedEntries =  leaderBoardUserEntryMap.entrySet().stream()
            .sorted(Map.Entry.comparingByValue(Comparator.comparing(GameEntry::getScore, Integer::compare)
                    .reversed())).map(Map.Entry::getValue).collect(Collectors.toList());

int indexOfnewEntry = selectedEntries.indexOf(leaderBoardUserEntryMap.get(uid));
return  selectedEntries.subList(indexOfnewEntry-2,indexOfnewEntry+2);

编辑 2

indexOfnewEntry 和 +- 2 会导致 IndexOutOfBoundsException, 防范似乎有点乏味,这里有什么最佳方法吗?

由于条目的index 可能因分数而异,并且subList 访问还进一步依赖于它之前/之后所需的输出数量。防守应该是比任何其他更好的选择。另外可以考虑的是customSubList 实现,它可以在内部检查您的集合类型。 How to use subList() 以最高票数的答案解释了这一点。不过我特别喜欢这个:

dataList.subList(Math.max(0, first), Math.min(dataList.size(), last) );

使用parallelStream() 会导致问题吗?

除非有任何 synchronized 块被执行,这些块可能会改变并并发更新流,否则不会导致任何问题。

但是你应该知道什么时候使用并行流 - Should I always use a parallel stream when possible?

【讨论】:

  • 这只会给前 5 个人。他想要的是 5 个用户的列表,当前用户将在 3 处。
  • @YogeshBadke 我相信已经根据分数进行了排序。不是吗?然后也反转以获得最高分数..
  • 如果说有 100 个用户,并且正在搜索的用户是 93。列表应该返回 91、92、93、94、95。这个解决方案将返回 1、2、3, 4、5。
  • 我需要@YogeshBadke 所解释的内容 :)
  • @KaliyugAntagonist 更新了答案。虽然守卫是你无法避免的,因为你的用例足够灵活,可以在列表的任何随机索引处使用 newEntry,然后尝试访问它周围的 +- 值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-30
  • 1970-01-01
  • 2016-09-29
  • 2013-10-30
  • 1970-01-01
  • 2019-07-02
相关资源
最近更新 更多