仅当allMatch过滤器和流程在Java中流一次时才收集流

时间:2018-04-18 00:45:10

标签: java java-stream

我有以下流代码:

List<Data> results = items.stream()
   .map(item -> requestDataForItem(item))
   .filter(data -> data.isValid())
   .collect(Collectors.toList());

Data requestDataForItem(Item item) {
   // call another service here
}

问题是我想打电话 仅当流中的所有元素都有效时才requestDataForItem。 例如, 如果第一项无效,我不会调用流中的任何元素。

流API中有.allMatch, 但它返回boolean。 我想和.allMatch做同样的事情 .collect当一切都匹配时的结果。 另外,我想只处理一次流, 有两个循环很容易。

这是否可以使用Java Streams API?

4 个答案:

答案 0 :(得分:3)

这将是Java 9的一项工作:

List<Data> results = items.stream()
    .map(item -> requestDataForItem(item))
    .takeWhile(data -> data.isValid())
    .collect(Collectors.toList());

此操作将在第一个无效元素处停止。在顺序执行中,这意味着不会进行后续的requestDataForItem调用。在并行执行中,一些额外的元素可能会在操作停止之前同时处理,但这是高效并行处理的代价。

在任何一种情况下,结果列表只包含第一个遇到无效元素之前的元素,您可以使用results.size() == items.size()轻松检查所有元素是否有效。

在Java 8中,没有这样简单的方法,并且使用额外的库或推出自己的takeWhile实现将无法考虑非流解决方案的简单性

List<Data> results = new ArrayList<>();
for(Item item: items) {
    Data data = requestDataForItem(item);
    if(!data.isValid()) break;
    results.add(data);
}

答案 1 :(得分:1)

实施两步流程:

  1. 测试allMatch是否返回true。
  2. 如果确实返回true,请使用第二个流进行收集。

答案 2 :(得分:1)

理论上你可以使用.allMatch然后收集如果.allMatch返回true,但是那么你将处理该集合两次。你无法直接使用流API进行操作。

您可以创建一个方法来为您执行此操作,只需将您的集合传递给它,而不是使用流API。这比使用流API稍微不那么优雅,但是因为它只处理集合一次效率更高。

List<Data> results = getAllIfValid(
         items.stream().map(item -> 
             requestDataForItem(item).collect(Collectors.toList())
);

public List<Data> getAllIfValid(List<Data> items) {
    List<Data> results = new ArrayList<>();
    for (Data d : items) {
        if (!d.isValid()) {
            return new ArrayList<>();
        }
        results.add(d);
    }
    return results;
}

如果每个元素都通过,它将返回所有结果,并且只处理items集合一次。如果isValid()检查失败,它将返回一个空列表,因为你想要全部或全部。只需检查返回的集合是否为空,以查看是否所有项目都通过了isValid()检查。

答案 3 :(得分:0)

试试这个。

List<Data> result = new ArrayList<>();
boolean allValid = items.stream()
    .map(item -> requestDataForItem(item))
    .allMatch(data -> data.isValid() && result.add(data));
if (!allValid)
    result.clear();