使用collect()而不是filter()过滤流

时间:2019-01-29 09:27:21

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

我有一个包含看起来像这样的数据的列表

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

2 个答案:

答案 0 :(得分:3)

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

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

为此,您需要为Game的{​​{1}}类添加一个吸气剂,例如:

battleStatus

这将为您提供以下输出:

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}} 获取任何用户和任何状态的计数。

答案 1 :(得分:1)

如果您需要同时计算完成和未完成的游戏,那么另外一个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())));

我什至会创建一个吸气剂getBattleStatus(),然后将game -> game.battleStatus替换为方法引用Game::getBattleStatus

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