在流列表中收集具有最大值的对象

时间:2019-07-04 13:02:08

标签: java java-stream

我几乎完成了大学的扑克项目,但是我仍然在Java流方面苦苦挣扎。 我编写了一个HandEvaluator类,该类计算每个玩家手牌的强度并将其分配给玩家。现在,我尝试将具有最高手分数的一个或多个玩家(如果一个以上的玩家具有相同的得分/平分底池)添加到列表中,以计算出胜负。

我对流的语法有疑问。我正在尝试这样的事情:

playerList.stream().max(Comparator.comparing(Player::getHandScore)).get();

这将返回得分最高的播放器,但是如果有多个得分相同的玩家怎么办?以及如何将它们添加到列表中?

5 个答案:

答案 0 :(得分:3)

我做了这样的事情。我按分数分组,然后找到具有最大键值的分数。它将返回Optional中的Map.Entry。它既包含最大值,又包含拥有它的玩家。然后,我可以使用getValue()方法获得球员名单。

List<Player> value = playerList.stream()
        .collect(groupingBy(Player::getScore))
        .entrySet()
        .stream()
        .max(Comparator.comparing(Map.Entry::getKey))
        .get()
        .getValue();

答案 1 :(得分:2)

一种易于理解的解决方案如下:

int maxHighScore = playerList.stream()
                             .map(player -> player.getHandScore())
                             .max()
                             .orElse(-1);

List<Player> highestHandScores = playerList.stream()
                                           .filter(player -> player.getHandScore() == maxHighScore)
                                           .collect(Collectors.toList());

第一步,我们获得maxHighScore,第二步,我们过滤玩家以仅保留得分最高的玩家。

答案 2 :(得分:1)

    int max = playerList.stream()
            .max(Comparator.comparing(Player::getHandScore))
            .get()
            .getHandScore();

    List<Player> playerLists = playerList
            .stream()
            .filter(m -> m.getHandScore() == max)
            .collect(Collectors.toList());

答案 3 :(得分:1)

我对其他一些答案的问题是,他们两次遍历球员名单-一次用于按球员得分分组,另一次用来获取得分最高的球员。在正常情况下,这可能微不足道,但是当玩家人数较多时,可能会成为问题。

为了避免两次遍历列表,要做的一件事是使用SortedMap。一旦我们按照玩家的得分将他们分组,我们可以简单地调用lastKey()一次获得最高的键:

SortedMap<Integer, List<Player>> topPlayers = playerList.stream()
    .collect(Collectors.groupingBy(Player::getScore, TreeMap::new, Collectors.toList()));
topPlayers.get(topPlayers.lastKey());

或者,正如Holger在评论中所说,如果您使用NavigableMap,则可以保存另一个地图查找:

NavigableMap<Integer, List<Player>> topPlayers = playerList.stream()
    .collect(Collectors.groupingBy(Player::getScore, TreeMap::new, Collectors.toList()));
topPlayers.lastEntry().getValue();

但是,在我看来,链接文章的answer given by Stuart Marks更好,因为并非所有元素都已存储(分组到存储桶中),但是,如果发现它们不属于所有元素,则会立即将其删除达到最大程度。

这可能节省内存。

答案 4 :(得分:1)

我的答案将是带有SortedMap的{​​{3}}版本,但是此版本将在找到得分较高的单个Player时立即丢弃“较低”的组。此版本适用于确实存在很多元素的情况,其中保留所有可见元素可能会成为问题。例如,当从非常大的文件中读取流式项目时,该项目就无法放入内存中。

整个解决方案如下:

List<Player> topScore = playersStream().collect(
    topGroup(Comparator.comparingInt(Player::getHandScore))
);

要使其正常工作,您将需要一个带有状态容器的自定义收集器来保留组。我不确定JDK中是否有类似的东西(无论如何都不是8),但是您可能可以在其中的一个库中找到它。面向外的方法如下:

static <T> Collector<T, ?, List<T>> topGroup(final Comparator<? super T> comparator) {
    Objects.requireNonNull(comparator, "comparator");
    return Collector.of(
        () -> new Group<>(comparator),
        // My local compiler can't infer type properly, I had to help it.
        // Your experience may be different
        (BiConsumer<Group<T>, T>) Group::accept,
        Group::merge,
        Group::asList
   );
}

最重要的部分是有状态的Group<T>。目的是作为外部比较器认为顺序最高的元素的容器。一旦遇到较高顺序的元素,该组就会丢弃其所有先前的内容。示例实现为:

private static class Group<T> {
    private final Comparator<? super T> comparator;
    T sample;
    List<T> more;

    public Group(Comparator<? super T> comparator) {
        this.comparator = comparator;
    }

    public void accept(T el) {
        if (sample == null) {
            sample = el;
        }
        else {
            int order = comparator.compare(sample, el);
            if (order == 0) {
                more().add(el);
            }
            else if (order > 0) {
                // element of a higher order, discard everything and make it a sample
                sample = el;
                more = null;
            }
            // else {element of a lower order, ignore}
        }
    }

    public Group<T> merge(Group<T> other) {
        if (this.comparator != other.comparator) {
            throw new IllegalArgumentException("Cannot merge groups with different orders");
        }
        if (sample == null) {
            return other; // we're empty
        }
        int order = comparator.compare(this.sample, other.sample);
        if (order >= 0) {
            if (order == 0) {
                // merge with other group
                more().addAll(other.asList());
            }
            return this;
        }
        else {
            // other group is higher than us
            return other;
        }
    }

    public List<T> asList() {
        List<T> result = new ArrayList<>();
        if (sample != null) {
            result.add(sample);
        }
        if (more != null) {
            result.addAll(more);
        }
        return result;
    }
}

此实现也是解决找到“与关系排名靠前的N个问题”(其中我的实现为“与关系排名靠前的1个问题”)的门户。