【问题标题】:Java8 Stream : Collect elements after a condition is metJava8 Stream:满足条件后收集元素
【发布时间】:2018-09-11 05:49:24
【问题描述】:

我的POJO如下

class EventUser {
  private id;
  private userId;
  private eventId;
}

我检索 EventUser 对象如下:

List<EventUser> eventUsers = eventUserRepository.findByUserId(userId);

假设“eventUsers”如下:

[
{"id":"id200","userId":"001","eventId":"1010"},
{"id":"id101","userId":"001","eventId":"4212"},
{"id":"id402","userId":"001","eventId":"1221"},
{"id":"id301","userId":"001","eventId":"2423"},
{"id":"id701","userId":"001","eventId":"5423"},
{"id":"id601","userId":"001","eventId":"7423"}
]

使用流式传输,并且不使用任何中间变量,如何在给定 EventUser.id 之后过滤和收集事件: 例如:

List<EventUser> filteredByOffSet = eventUsers.stream.SOMEFILTER_AND_COLLECT("id301");

结果应该是:

[{"id":"id301","userId":"001","eventId":"2423"},
{"id":"id701","userId":"001","eventId":"5423"},
{"id":"id601","userId":"001","eventId":"7423"}]

【问题讨论】:

  • @ernest_k 结​​果集合应该在“id301”之后有元素(而不是之前)
  • 输入列表(以及初始流)是否总是按eventId 排序(该示例留下了这样的印象)?
  • @JanusVarmarken 未按 eventId 排序..

标签: java java-8 java-stream


【解决方案1】:

在 Java 8 中,您需要一个有状态的过滤器

public static <T> Predicate<T> from(Predicate<T> test) {
    boolean[] found = { false };
    // once found, always true
    return t -> found[0] || (found[0] = test.test(t));
}

注意:这只对单线程流有意义。

List<EventUser> filteredByOffSet = 
     eventUsers.stream()
               .filter(from(e -> "id301".equals(e.getId()))
               .collect(Collectors.toList());

【讨论】:

  • 我喜欢这个——它比your answer on this question简洁多了
  • @PeterLawrey 是的,我更喜欢这个!
  • @PeterLawrey 这是一个真的坏主意,我至少会在这里强制sequential
  • 这不像书面的那样工作。 lambda 中的变量必须是 final 或有效的最终变量。 found 变量不能在 lambda 中修改。它需要用boolean[1]AtomicBoolean 或类似名称包装。
【解决方案2】:

使用 Java 9 中的“dropWhile”。

【讨论】:

  • @AshikaUmangaUmagiliya 感谢所有强大的 Holger,这在 java-8 中也是可能的。德米特里只需将其链接到您的答案,这应该是公认的 IMO。 here is the link
  • 请在您的答案中添加一个使用dropWhile 的示例。现在这是一个仅链接的答案,有被删除的风险:)
  • @DmitryGorkovets 考虑在使用此方法时应按顺序添加流。如果它是无序的,则行为是不确定的。
  • @AshikaUmangaUmagiliya 如果您坚持使用 Java8,我推荐 StreamEx 库,它提供 dropWhile(以及许多其他库)。
【解决方案3】:

先找到搜索项的索引:

int asInt = IntStream.range(0, list.size())
    .filter(userInd-> list.get(userInd).equals(<criteria>))
    .findFirst()
    .getAsInt();

获取索引上和索引之后的项目:

list.stream().skip(asInt).collect(Collectors.toList());

【讨论】:

  • 这应该是公认的答案,尽管我会使用 List.subList(from, to) 而不是跳过
  • @FedericoPeraltaSchaffner 既然可以跳过,为什么还要创建子列表?
  • @TimB subList 不会创建新列表,它只是原始列表范围的视图
  • @FedericoPeraltaSchaffner 视图是一个对象。我同意这不像复制列表那么工作,它仍然大于零。
  • @TimB 也许我不够清楚。我的意思是 subList 而不是流,跳过并收集到一个新列表
【解决方案4】:

如果不使用任何中间变量,您将无法做到这一点。找到位置并将其迭代到最后(请参阅下面的这个问题更准确地回答它) enter link description here

【讨论】:

    猜你喜欢
    • 2021-12-14
    • 2021-10-28
    • 2017-08-09
    • 2021-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多