【问题标题】:filter a stream using collect() instead of filter()使用 collect() 而不是 filter() 过滤流
【发布时间】:2019-06-22 09:11:23
【问题描述】:

我的列表包含如下所示的数据

game_id | user_id | status
   1         1       STARTED
   2         1       FINISHED
   1         2       STARTED
   2         2       FINISHED

我想将此列表收集到两个地图中(或者如果可能的话,在单个地图中)

一张地图应该将user_id 映射到用户玩过的游戏数量——基本上是finished 状态的游戏

user_id | finished_games
   1            1
   2            1

另一张地图应将user_id 存储到所有游戏计数中。它看起来像这样

user_id  | all_games
   1            2
   2            2

我这样做(我删除了game_id 字段,因为它实际上并不重要)

import java.util.*;
import java.util.stream.*;

public class MyClass {
    public static void main(String args[]) {
        List<Game> games = Arrays.asList(
            create("user-1", "STARTED"),
            create("user-2", "STARTED"),
            create("user-1", "FINISHED"),
            create("user-2", "FINISHED"),
            create("user-1", "FINISHED"),
            create("user-2", "FINISHED")
        );

        // expect user-1: all_games (3), finished_games (2)

        Map<String, Long> allGames = games
            .stream()
            .collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));
        System.out.println(allGames);

        Map<String, Long> finishedGames = games
            .stream()
            .filter(game -> game.battleStatus.equals("FINISHED"))
            .collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));
        System.out.println(finishedGames);

    }

    private static Game create(String id, String status) {
        Game game = new Game();
        game.userId = id;
        game.battleStatus = status;
        return game;
    }

    private static class Game {
        String userId;
        String battleStatus;

        public String getUserId() {
            return userId;
        }
    }
}

它似乎工作正常。但是当我计算带有状态的游戏时,我会进行过滤以便只留下带有FINISHED 状态的项目。

Map<String, Long> finishedGames = games
    .stream()
    .filter(game -> game.battleStatus.equals("FINISHED"))
    .collect(Collectors.groupingBy(Game::getUserId, Collectors.counting()));

有没有办法在 collect 块中实现这一点,而不是使用 filter

【问题讨论】:

  • 你有什么理由试图避免filter
  • 这个真的不清楚,你不是说你自己算FINISHED和所有游戏都算什么吗?
  • 你可以在 Java 9 中使用 Collectors.filtering(),但你为什么要这样做超出了我的理解。
  • @Eugene 是的。我需要数他们两个。但我将其存储在单独的地图中。
  • 所以您想使用单个stream 操作来实现这一目标?顺便说一句,您的代码看起来非常好

标签: java collections java-8 hashmap java-stream


【解决方案1】:

您可以使用多个groupingBy 来获取此信息,例如:

Map<String, Map<String, Long>> gameStatuses = games
   .stream()
   .collect(Collectors.groupingBy(Game::getUserId, 
   Collectors.groupingBy(Game::getBattleStatus, Collectors.counting())));
System.out.println(gameStatuses);

为此,您需要为battleStatusGame 类添加一个getter,例如:

private static class Game {
    String userId;
    String battleStatus;

    public String getUserId() {
        return userId;
    }

    public String getBattleStatus() {
        return battleStatus;
    }
}

这将为您提供以下输出:

{user-1={STARTED=1, FINISHED=2}, user-2={STARTED=1, FINISHED=2}}

您可以使用嵌套的map.get 来获取任何用户和任何状态的计数。

【讨论】:

    【解决方案2】:

    如果您需要计算已完成和未完成的游戏,那么另一个groupingBy 可以帮助您:

    Function<Game, Boolean> isFinished = game -> "FINISHED".equals(game.battleStatus);
    
    Map<String, Map<Boolean, Long>> groupedGames = 
        games.stream()
             .collect(groupingBy(Game::getUserId, 
                          groupingBy(isFinished, counting())));
    

    或者您可能想按battleStatus 本身进行分组:

    Map<String, Map<String, Long>> groupedGames = 
        games.stream()
             .collect(groupingBy(Game::getUserId, 
                          groupingBy(game -> game.battleStatus, counting())));
    

    我什至会创建一个getter getBattleStatus(),然后将game -&gt; game.battleStatus 替换为方法引用Game::getBattleStatus

    Map<String, Map<String, Long>> groupedGames = 
        games.stream()
             .collect(groupingBy(Game::getUserId, 
                          groupingBy(Game::getBattleStatus, counting())));
    

    【讨论】:

    • 这并不是那么糟糕的 IMO,但需要注意的是 OP 说 我想将此列表收集到两个地图中;如果我们不从字面上理解,我想这看起来不错。 1+
    • @Eugene 哦!我不知道可以分组到同一个地图中,因为我没有考虑类“真/假”键
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    • 2011-02-04
    • 2018-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多