是否可以在收集阶段使用 java 8 个流处理 filter/ignore 个对象?

Is it possible to filter/ignore objects during the collection phase with java 8 streams?

这个问题与我不久前发布的 another 有关。

我找到了一种按照我想要的方式对数据进行分组的方法。但是由于我需要 group/map 对象,所以当我需要应用一些过滤时,我已经在收集期间关闭了流。我有以下代码:

final Map<TeamDetails, List<Player>> teamDetailsPlayerListMap  = 
        dbRows.stream().map(row -> mapDbRowToTeamPlayerPair(row))
                .collect(Collectors.groupingBy(TeamPlayerPair::getKey, 
                      Collectors.mapping(TeamPlayerPair::getValue, Collectors.toList())));

final List<Team> teams = teamDetailsPlayerListMap.entrySet().stream()
        .map(teamPlayerList -> mapTeamPlayerListToTeam(teamPlayerList))
        .collect(Collectors.toList());

我遇到的问题是某些 dbRows 的 playerName 为空或空 String。我不想在收集之前进行过滤,就好像 Team 没有玩家(例如只有 1 db 行,空字符串作为玩家名称),我仍然希望在我的 [=17 列表中有它=]s 在最后。它只会有一个空的玩家列表。

有什么方法可以在收集期间应用过滤器,从而不会将空字符串或 null 字符串添加到列表中???

我已经能够使用自定义收集器实现它,如下所示,但我只是想知道是否有一种方法可以在没有自定义收集器的情况下实现???

Function<Player, Boolean> emptyPlayerNameFilter = new Function<Player, Boolean>() {
    @Override
    public Boolean apply(Player player) {
         return player != null && player.getName() != null && !"".equals(player.getName());
    }
};

final Map<TeamDetails, List<Player>> teamDetailsPlayerListMap  = 
        dbRows.stream().map(row -> mapDbRowToTeamPlayerPair(row))
        .collect(Collectors.groupingBy(TeamPlayerPair::getKey, 
                Collectors.mapping(TeamPlayerPair::getValue, 
                        MyCollectors.toFilteredLinkedList(emptyPlayerNameFilter))));

final List<Team> finalTeams = teamDetailsPlayerListMap.entrySet().stream()
        .map(teamPlayerList -> mapTeamPlayerListToTeam(teamPlayerList))
        .collect(Collectors.toList());

其中 MyCollectors.toFilteredLinkedList() 是:

public class MyCollectors {

    public static <T, A extends Collection<T>> Collector<T, ?, A> toFilteredCollection(Supplier<A> collectionFactory, Function<T, Boolean> filter) {
        return Collector.of(
                collectionFactory,
                (acc, entry) -> {
                    if (filter.apply(entry)) {
                        acc.add(entry);
                    }
                },
                (left, right) -> { left.addAll(right); return left; }
        );
    }

    public static <T> Collector<T, ?, List<T>> toFilteredLinkedList(Function<T, Boolean> filter) {
        return toFilteredCollection(LinkedList<T>::new, filter);
    }
}

似乎您需要一个 filtering 收集器,它类似于 mapping,但执行过滤。在 Java-9 中已经 implemented,但在 Java-8 中不存在。您可以将它放入项目中的一些实用程序 class:

public static <T, A, R> Collector<T, A, R> filtering(
        Predicate<? super T> filter, Collector<T, A, R> downstream) {
    BiConsumer<A, T> accumulator = downstream.accumulator();
    Set<Characteristics> characteristics = downstream.characteristics();
    return Collector.of(downstream.supplier(), (acc, t) -> {
        if(filter.test(t)) accumulator.accept(acc, t);
    }, downstream.combiner(), downstream.finisher(), 
        characteristics.toArray(new Collector.Characteristics[0]));
}

并像这样使用:

final Map<TeamDetails, List<Player>> teamDetailsPlayerListMap  = 
    dbRows.stream()
          .map(row -> mapDbRowToTeamPlayerPair(row))
          .collect(Collectors.groupingBy(TeamPlayerPair::getKey, 
                Collectors.mapping(TeamPlayerPair::getValue, 
                    filtering(player -> player != null && player.getName() != null 
                                 && !player.getName().isEmpty(), Collectors.toList()))));

如果您不喜欢手动将 filtering 添加到您的项目中,您可以使用一些提供此类收集器的第三方库,例如 MoreCollectors.filtering(), which is available in my StreamEx 库。