选取列表的元素,直到 Java 8 Lambdas 满足条件

2022-09-02 03:14:44

我试图改变我的思想来思考功能方式,最近遇到了一种情况,我需要从列表中选取元素,直到满足条件,我找不到一种简单的自然方法来实现这一目标。显然我还在学习。

假设我有这个列表:

List<String> tokens = Arrays.asList("pick me", "Pick me", "pick Me",
    "PICK ME", "pick me and STOP", "pick me", "pick me and Stop", "pick me");

// In a non lambdas was you would do it like below
List<String> myTokens = new ArrayList<>();
for (String token : tokens) {
    myTokens.add(token);
    if (token.toUpperCase().endsWith("STOP")) {
        break;
    }
}

提前感谢您的输入

注意:在发布此内容之前,我阅读了“通过谓词限制流”,但我无法看到如何将答案适应我的问题。任何帮助将不胜感激, 谢谢。


答案 1

如果您确实必须使用 Streams API,请保持简单并使用索引流:

int lastIdx = IntStream.range(0, tokens.size())
        .filter(i -> tokens.get(i).toUpperCase().endsWith("STOP"))
        .findFirst()
        .orElse(-1);

List<String> myTokens = tokens.subList(0, lastIdx + 1);

或者,如果您想要一个不受原始列表支持的独立副本,则可以从子列表中创建一个新副本。List


答案 2

在JDK9中,将有一个名为takeWhile的新操作,它执行类似于您需要的事情。我将此操作向后移植到我的 StreamEx 库,因此您甚至可以在 Java-8 中使用它:Stream

List<String> list = StreamEx.of(tokens)
                            .takeWhile(t -> !t.toUpperCase().endsWith("STOP"))
                            .toList();

不幸的是,它不采用元素本身,因此需要手动添加第二遍:"STOP"

list.add(StreamEx.of(tokens).findFirst(t -> t.toUpperCase().endsWith("STOP")).get());

请注意,两者都是短路操作(如果没有必要,它们不会处理整个输入流),因此您可以将它们用于非常长甚至无限的流。takeWhilefindFirst

但是,使用StreamEx,您可以使用groupRuns的技巧在单次通过中解决它。该方法根据提供的谓词将相邻的 Stream 元素分组到 ,该谓词指示是否应对两个给定的相邻元素进行分组。我们可以认为该组以包含 的元素结尾。然后我们只需要取第一组:groupRunsList"STOP"

List<String> list = StreamEx.of(tokens)
                            .groupRuns((a, b) -> !a.toUpperCase().endsWith("STOP"))
                            .findFirst().get();

当第一组完成时,此解决方案也不会执行额外的工作。


推荐