2016-02-25 36 views
1

這個問題與another有關,我發佈前不久。是否可以在java 8流的收集階段過濾/忽略對象?

我想出了一種按我想要的方式對數據進行分組的方法。但是由於我需要對集​​合/映射對象進行分組,所以在我需要應用某些過濾時,我已經在集合期間關閉了流。我有以下代碼:

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分貝的空行字符串作爲玩家的名字),我仍然希望在最後的Team s列表中有。它只會有一個空的球員名單。

有什麼辦法,我可以在收集過程中應用過濾器,以便空或空字符串不會被添加到列表中?

我已經能夠實現它與自定義收集器如下所示,但我只是想知道如果有一種方法,我可以做到這一點沒有自定義收集器?

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); 
    } 
} 

回答

2

看來你需要一個filtering收藏家,這是類似mapping,但執行過濾。在Java-9中它已經是implemented,但在Java-8中不存在。你可以把它變成了一些實用工具類項目:

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(),它在我的StreamEx庫中可用。