Java 8嵌套流:在最后一个流中返回一个值

8
这个问题可以看作是基于 Java 8嵌套流 的。
假设我有一个包含BasketItemBatch
public class Batch {
    private List<Basket> baskets;
}

public class Basket {
    private List<Item> items; 
}

public class Item {
    private String property;
    private int value;
}

我希望使用Java 8流重写此方法。

public class SomeService {
    public int findValueInBatch(Batch batch) {
        for (Basket basket : batch.getBaskets()) {
            for (Item item : basket.getItems()) {
                if (item.getProperty().equals("someValue") {
                    return item.getValue();
                }
            }
        }
        return 0;
    }
}

我该如何做?
我想去的第一步:
public int findValueInBatch(Batch batch) {
    for (Basket basket : batch.getBaskets()) {
        basket.getItems().stream()
            .filter(item -> item.getProperty.equals("someValue") 
            .findFirst()
            .get();
            // there I should 'break'
    }
}

非常感谢。

你的原始方法无法编译。 - shmosel
@shmosel,没错,我漏掉了括号 item.getProperty() 和如果没有找到元素的返回值。但目标是展示原则。已修复。 - Dapangma
3个回答

10
baskets.stream()
            .flatMap(basket -> basket.getItems().stream())
            .filter(item -> item.equals("someValue"))
            .findAny()
            .orElseThrow(NoSuchElementException::new);

使用findAny而不是findFirst的优势在于,findFirst不能与并行流一起使用。因此,如果您想对上述操作进行并行化,只需将stream()方法替换为parallel()即可。


所有的答案都很合适,感谢大家。我选择这个答案是因为它提供了findAny的提示。与其抛出异常,我们可以返回一个Optional,然后测试结果是否存在(isPresent())。 - Dapangma

5
  1. 使用flatMap消除嵌套列表,提取每个List<Item>并将它们合并成一个 Stream<Item>,像所有子流已经合并在一起一样。
  2. 使用filter忽略不匹配的元素。
  3. 使用findFirst只获取第一个匹配项并停止处理。
  4. 使用orElseThrow如果没有找到someValue的任何匹配项则抛出异常。
public class SomeService {
    public int findValueInBatch(Batch batch) {
        return batch.getBaskets().stream()
            .flatMap(basket -> basket.getItems().stream())
            .filter(item -> item.getProperty.equals("someValue"))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("value not found"));
    }
}

感谢对flatMap的清晰解释,真的帮了我很多。一个细节:IllegalArgumentException在这里并不是很合适(不是问题的核心)。 - Dapangma
没问题,我把它用作占位符,但你可以使用适合你需求的一个。甚至可以使用 orElse(a default int value),但如果我是你,我会更喜欢使用 orElseThrow。 - Anthony Raymond

4
为了消除这两个循环,您可以使用flatMap来生成所有Basket中的所有ItemStream<Item>
return batch.getBaskets()
            .stream()
            .flatMap(b -> b.getItems().stream())
            .filter(item -> item.getProperty.equals("someValue"))
            .findFirst()
            .orElse(some default value); // using .get() would throw an exception
                                         // if no match is found

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接