首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >用索引迭代Java 8函数样式

用索引迭代Java 8函数样式
EN

Stack Overflow用户
提问于 2015-12-21 12:40:44
回答 3查看 902关注 0票数 6

我已经练习了一段时间的java 8流和函数样式。有时候,我只想用流来解决一些编程难题。在这段时间里,我发现了一类任务,我不知道如何用流来解决,只能用经典的方法。

这类任务的一个例子是:给定一个数字数组,查找元素的索引,这将使数组的左边部分之和低于零。例如,对于数组[1, 2, 3, -1, 3, -10, 9],答案将是5

我的第一个想法是使用IntStream.generate(0, arr.length)...,但是我不知道如何同时积累值和知道索引。

因此,问题是:

  • 是否有可能在流上积累价值,然后进行有条件的退出?
  • 那么并行执行又是什么呢?这不适合在需要知道元素顺序的情况下找到索引的问题。
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-12-21 13:39:38

我怀疑你的任务是否适合溪流。您要寻找的是一个典型的扫描左操作,这本质上是一个顺序操作。

例如,想象一下管道中的以下元素:[1, 2, -4, 5]。并行执行可以将其分为两个子部分,即[1, 2][-4, 5]。那你会拿他们怎么办?您不能独立地将它们相加,因为它将产生[3][1],然后您就失去了1 + 2 - 4 < 0受到尊重的事实。

因此,即使您编写了一个跟踪索引和和的收集器,它也无法并行执行(我怀疑您甚至无法从中受益),但是您可以想象这样的收集器可以连续使用:

代码语言:javascript
运行
复制
public static Collector<Integer, ?, Integer> indexSumLeft(int limit) {
        return Collector.of(
                () -> new int[]{-1, 0, 0},
                (arr, elem) -> {
                    if(arr[2] == 0) {
                        arr[1] += elem;
                        arr[0]++;
                    }
                    if(arr[1] < limit) {
                        arr[2] = 1;
                    }

                },
                (arr1, arr2) -> {throw new UnsupportedOperationException("Cannot run in parallel");},
                arr -> arr[0]

        );
    }

还有一个简单的用法:

代码语言:javascript
运行
复制
int index = IntStream.of(arr).boxed().collect(indexSumLeft(0));

这仍将贯穿管道的所有元素,因此效率不高。

此外,如果数据源是数组,则可以考虑使用Arrays.parallelPrefix。只需计算其上的部分和,然后使用流来查找第一个索引,其中和低于限制。

代码语言:javascript
运行
复制
Arrays.parallelPrefix(arr, Integer::sum);
int index = IntStream.range(0, arr.length)
                     .filter(i -> arr[i] < limit)
                     .findFirst()
                     .orElse(-1);

这里也计算了所有的部分和(但并行)。

简而言之,我会使用一个简单的for-循环。

票数 6
EN

Stack Overflow用户

发布于 2015-12-21 13:28:46

我可以使用我的StreamEx库(它为Stream提供额外的函数)提出一个解决方案,但是我不太喜欢这样的解决方案:

代码语言:javascript
运行
复制
int[] input = {1, 2, 3, -1, 3, -10, 9};
System.out.println(IntStreamEx.of(
    IntStreamEx.of(input).scanLeft(Integer::sum)).indexOf(x -> x < 0));
// prints OptionalLong[5]

它使用IntStreamEx.scanLeft操作计算前缀和数组,然后使用IntStreamEx.indexOf操作搜索该数组。当indexOf短路时,scanLeft操作将处理整个输入,并创建一个与输入长度相同的中间数组,这在以命令式解决相同问题时是完全不必要的。

票数 4
EN

Stack Overflow用户

发布于 2016-01-24 08:32:34

在我的headTail库中使用新的StreamEx方法,它可能会创建惰性解决方案,它可以很好地工作在非常长的或无限的流中。首先,我们可以定义一个新的中间scanLeft操作:

代码语言:javascript
运行
复制
public static <T> StreamEx<T> scanLeft(StreamEx<T> input, BinaryOperator<T> operator) {
    return input.headTail((head, tail) -> 
               scanLeft(tail.mapFirst(cur -> operator.apply(head, cur)), operator)
                   .prepend(head));
}

这使用headTail定义了一个惰性的headTail:它将给定的函数应用于headtail流的第一个元素,然后在head前面添加。现在您可以使用这个scanLeft

代码语言:javascript
运行
复制
scanLeft(StreamEx.of(1, 2, 3, -1, 3, -10, 9), Integer::sum).indexOf(x -> x < 0);

这同样适用于无限流(例如随机数流):

代码语言:javascript
运行
复制
StreamEx<Integer> ints = IntStreamEx.of(new Random(), -100, 100)
                                    .peek(System.out::println).boxed();
int idx = scanLeft(ints, Integer::sum).indexOf(x -> x < 0);

这将一直运行到累积和变为负值,并返回相应元素的索引。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34395943

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档