【问题标题】:Replace while loop with break with Java streams and lambda expression用 Java 流和 lambda 表达式替换 while 循环
【发布时间】:2020-11-09 09:33:04
【问题描述】:

我想用 Java 流和 lambda 表达式将以下 while 替换为 break 条件。我无法找到解决办法。 我使用 Java 8。

假设输入 Arraylist(result) 是否具有排序值:[1,2,3,4,5,10,12,30,40,50]

输出应该包含值[1,2,3,4,5,10]

List<String> finalResult = new ArrayList();
    while (index < maxSize) {
        if (inputList.get(index) > 10)) {
            finalResult.add(inputList.get(index));
        } else {            
                break;              
        }
        index++;
    }

如果我尝试使用 findFirst() 打破条件,那么我无法收集列表。

Optional<String> result =
inputList.stream().filter(obj -> some_condition_met).findFirst();

【问题讨论】:

  • 你为什么要那个?有什么意义?
  • 我只是想找到一种方法来执行此 java 流,就像单个语句一样。有可能吗?
  • 你自己试过什么?你看,那里有很多教程一步步解释如何使用流......这应该是你自己解决这个问题的第一个冲动,只有在你自己尝试之后才来这里。
  • 我已经更新了问题,我看到 findFirst() 可以在不循环所有元素的情况下打破循环,但我无法收集元素。
  • 您提供的代码在修复了一些编译问题(例如为inputfinalResult 混合字符串和整数)后,没有返回预期的输出[1,2,3,4,5,10],而是返回一个空列表[],因为它突破列表的第一个值。

标签: java java-8 java-stream


【解决方案1】:

如果您使用的是 java 9,则可能是您正在寻找的东西:

List<Integer> myList = List.of(1,2,3,4,5,10,12,30,40,50);
List<Integer> under5 = myList.stream()
                             .takeWhile(i -> i <= 5)
                             .collect(Collectors.toList());
System.out.println(under5);

【讨论】:

  • 这是一个很高兴知道的事情。我们在 Java8 中是否有类似的东西,因为我不能同时使用 Colllect 和 findFirst。
  • @SSK Takewhile 和 dropwhile 仅适用于 java 9。如果您仍然使用 java 8,则必须自己实现。我不知道这是否值得,但你可以看看下面的帖子,看看它是如何做到的:limit-a-stream-by-a-predicate
  • @Eritrean 这是一个很好的例子。干杯
【解决方案2】:
  1. 对于提供的输入[1,2,3,4,5,10,12,30,40,50],预期的输出[1,2,3,4,5,10]应该由简单的过滤器返回:
static List<Integer> findBelow(List<Integer> inputList, Predicate<Integer> condition) {
    return inputList.stream()
                    .filter(condition::test)
                    .collect(Collectors.toList());
}
  1. 如果需要指定索引范围,应该使用IntStream::range实现:
static List<Integer> findBelow(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
    start = Math.max(0, start);
    end = Math.min(inputList.size(), end);
    return IntStream.range(start, end)
                    .filter(i -> condition.test(inputList.get(i)))
                    .mapToObj(inputList::get)
                    .collect(Collectors.toList());
}

显然,这些实现不会 break 退出循环,Java 8 Stream API 没有相应的功能。

可以提供以下解决方法:

  1. 使用findFirst检测第一个不匹配的索引,然后限制索引的范围:
static List<Integer> findWithFirst(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
    start = Math.max(0, start);
    end = Math.min(inputList.size(), end);
    int firstNonMatch = IntStream.range(start, end)
                                 .filter(i -> !condition.test(inputList.get(i)))
                                 .findFirst()
                                 .orElse(maxSize);

    return IntStream.range(start, firstNonMatch) // no filter needed any longer
                    .mapToObj(inputList::get)
                    .collect(Collectors.toList());
}
  1. 在过滤输入列表时使用流外部的布尔标志检查是否满足倒置的break 条件:
// using AtomicBoolean
static List<Integer> findAndBreak2(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
    AtomicBoolean breakFound = new AtomicBoolean(false);
    return IntStream.range(start, Math.min(inputList.size(), end))
                    .filter(i -> { 
                        breakFound.set(
                            breakFound.get() || !condition.test(inputList.get(i))
                        ); 
                        return !breakFound.get(); 
                    })
                    .mapToObj(inputList::get)
                    .collect(Collectors.toList());
}
// using static class field
static boolean found = false;
static List<Integer> findAndBreak(List<Integer> inputList, int start, int end, Predicate<Integer> condition) {
    found = false;
    return IntStream.range(start, Math.min(inputList.size(), end))
                    .filter(i -> { found |= !condition.test(inputList.get(i)); return !found; })
                    .mapToObj(inputList::get)
                    .collect(Collectors.toList());
}

测试:

条件i &lt;= 10 是微不足道的,因为它适用于所有情况。 反转条件i &gt; 10,如 OP 代码所示,break 生成空列表。

    Predicate<Integer> condition = (i) -> i > 10;
        
    List<Integer> inputList = Arrays.asList(1,2,3,4,5,10,12,30,40,50);
    List<Integer> finalResult = new ArrayList<>();
    int index = 0, maxSize = 10;
    while (index < maxSize) {
        if (condition.test(inputList.get(index))) {
            finalResult.add(inputList.get(index));
        } else {            
            break;              
        }
        index++;
    }
    
    System.out.println("loop: " + finalResult);

    System.out.println("plain filter: " + findBelow(inputList, condition));
    
    System.out.println("filter and range limit: " + findBelow(inputList, 0, maxSize, condition));
    
    System.out.println("filter and break static: " + findAndBreak(inputList, 0, maxSize, condition));

    System.out.println("filter and break atomic: " + findAndBreak2(inputList, 0, maxSize, condition));

    System.out.println("filter and findFirst: " + findWithFirst(inputList, 0, maxSize, condition));

输出:

loop: []
plain filter: [12, 30, 40, 50]
filter and range limit: [12, 30, 40, 50]
filter and break static: []
filter and break atomic: []
filter and findFirst: []

【讨论】:

    【解决方案3】:

    据我了解您的问题。你为什么要尝试使用流来完成这项工作。一个简单的循环就可以了,不要尝试使事情复杂化。如果唯一的问题是代码太长,请将其提取到单独的私有方法中。重点是在这种情况下,流不是您要寻找的。希望这有帮助

    【讨论】:

    • 感谢您的回答,我正在学习 java,只是想知道我们是否在流中有一些东西可以实现这一点。
    • 玩转这些功能很不错。除此之外,想象流就像一条流动的河流,数据作为流动的物体,你可以找到一个漂浮的物体或用网过滤它,但试图结束流动的河流是不可能的(很容易就是: D)。我知道这不是一个很好的类比,但在这种情况下,您正在寻找水坝或池塘,并试图用流动的河流来实现它。对不起我的英语不好,希望这是有道理的。 :D
    猜你喜欢
    • 1970-01-01
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多