我已经练习了一段时间的java 8流和函数样式。有时候,我只想用流来解决一些编程难题。在这段时间里,我发现了一类任务,我不知道如何用流来解决,只能用经典的方法。
这类任务的一个例子是:给定一个数字数组,查找元素的索引,这将使数组的左边部分之和低于零。例如,对于数组[1, 2, 3, -1, 3, -10, 9]
,答案将是5
我的第一个想法是使用IntStream.generate(0, arr.length)...
,但是我不知道如何同时积累值和知道索引。
因此,问题是:
发布于 2015-12-21 13:39:38
我怀疑你的任务是否适合溪流。您要寻找的是一个典型的扫描左操作,这本质上是一个顺序操作。
例如,想象一下管道中的以下元素:[1, 2, -4, 5]
。并行执行可以将其分为两个子部分,即[1, 2]
和[-4, 5]
。那你会拿他们怎么办?您不能独立地将它们相加,因为它将产生[3]
和[1]
,然后您就失去了1 + 2 - 4 < 0
受到尊重的事实。
因此,即使您编写了一个跟踪索引和和的收集器,它也无法并行执行(我怀疑您甚至无法从中受益),但是您可以想象这样的收集器可以连续使用:
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]
);
}
还有一个简单的用法:
int index = IntStream.of(arr).boxed().collect(indexSumLeft(0));
这仍将贯穿管道的所有元素,因此效率不高。
此外,如果数据源是数组,则可以考虑使用Arrays.parallelPrefix
。只需计算其上的部分和,然后使用流来查找第一个索引,其中和低于限制。
Arrays.parallelPrefix(arr, Integer::sum);
int index = IntStream.range(0, arr.length)
.filter(i -> arr[i] < limit)
.findFirst()
.orElse(-1);
这里也计算了所有的部分和(但并行)。
简而言之,我会使用一个简单的for-循环。
发布于 2015-12-21 13:28:46
我可以使用我的StreamEx库(它为Stream提供额外的函数)提出一个解决方案,但是我不太喜欢这样的解决方案:
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
操作将处理整个输入,并创建一个与输入长度相同的中间数组,这在以命令式解决相同问题时是完全不必要的。
发布于 2016-01-24 08:32:34
在我的headTail
库中使用新的StreamEx方法,它可能会创建惰性解决方案,它可以很好地工作在非常长的或无限的流中。首先,我们可以定义一个新的中间scanLeft
操作:
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
:它将给定的函数应用于head
和tail
流的第一个元素,然后在head
前面添加。现在您可以使用这个scanLeft
scanLeft(StreamEx.of(1, 2, 3, -1, 3, -10, 9), Integer::sum).indexOf(x -> x < 0);
这同样适用于无限流(例如随机数流):
StreamEx<Integer> ints = IntStreamEx.of(new Random(), -100, 100)
.peek(System.out::println).boxed();
int idx = scanLeft(ints, Integer::sum).indexOf(x -> x < 0);
这将一直运行到累积和变为负值,并返回相应元素的索引。
https://stackoverflow.com/questions/34395943
复制相似问题