Java 如何使用lambda表达式计算整数列表中的差异

Java 如何使用lambda表达式计算整数列表中的差异,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,假设我有以下数组:{1,2,3,4,6,7,8},它被放入流s=Stream.of(1,2,3,4,6,7,8) 如何在Java lambda表达式和流函数中计算每个元素与下一个元素之间的差异(在本例中为{1,1,1,2,1,1})? 这并不是真正的reduce操作,因为reduce将整个列表转换为1个元素;它也不是映射操作,因为它需要两个元素来计算差异,而不仅仅是一个元素。您可以循环索引而不是元素,例如 int s[] = {1, 2, 3, 4, 6, 7, 8}; IntStream di

假设我有以下数组:
{1,2,3,4,6,7,8}
,它被放入
流s=Stream.of(1,2,3,4,6,7,8)

如何在Java lambda表达式和流函数中计算每个元素与下一个元素之间的差异(在本例中为
{1,1,1,2,1,1}
)?
这并不是真正的reduce操作,因为reduce将整个列表转换为1个元素;它也不是映射操作,因为它需要两个元素来计算差异,而不仅仅是一个元素。

您可以循环索引而不是元素,例如

int s[] = {1, 2, 3, 4, 6, 7, 8};
IntStream differences = 
    IntStream.range(0, s.length - 1)
        .map(i -> s[i + 1] - s[i]);

另一个答案似乎已经被接受了,但我有一个想法,所以我还是发布了它。您可以制作一个
收集器
,将其直接收集到另一个

然后你可以这样写:

s.collect(intDifferences()).forEach(d -> System.out.print(d + ","));
下面是我编写的一个实现:

public static Collector<Integer, List<Integer>, Stream<Integer>> intDifferences() {

    return new Collector<Integer, List<Integer>, Stream<Integer>>() {

        @Override
        public BiConsumer<List<Integer>, Integer> accumulator() {
            return List::add;
        }

        @Override
        public Set<Collector.Characteristics> characteristics() {
            return EnumSet.noneOf(Collector.Characteristics.class);
        }

        @Override
        public BinaryOperator<List<Integer>> combiner() {
            return (left, right) -> {
                left.addAll(right);
                return left;
            };
        }

        @Override
        public Function<List<Integer>, Stream<Integer>> finisher() {
            return list -> {
                List<Integer> differences = new ArrayList<>();
                for (int i = 1; i < list.size(); i++) {
                    differences.add(list.get(i) - list.get(i - 1));
                }
                return differences.stream();
            };
        }

        @Override
        public Supplier<List<Integer>> supplier() {
            return ArrayList::new;
        }
    };
}
公共静态收集器intDifferences(){
返回新收集器(){
@凌驾
公共双消费者累加器(){
返回列表::添加;
}
@凌驾
公共集特征(){
返回EnumSet.noneOf(Collector.Characteristics.class);
}
@凌驾
公共二进制运算符组合器(){
返回(左、右)->{
左。addAll(右);
左转;
};
}
@凌驾
公共函数完成器(){
返回列表->{
列表差异=新建ArrayList();
对于(int i=1;i
如果允许映射功能产生副作用,即可以在流中存储对上一个对象的引用,则可以使用
map()

Stream.of(1, 2, 3, 4, 6, 7, 8)
    .map(new Function<Integer, Optional<Integer>>() {
        Optional<Integer> previousValue = Optional.empty();
        @Override
        public Optional<Integer> apply(Integer current) {
            Optional<Integer> value = previousValue.map(previous -> current - previous);
            previousValue = Optional.of(current);
            return value;
        }
    })
    .filter(Optional::isPresent)
    .map(Optional::get)
    .forEach(System.out::println);
Stream.of(1,2,3,4,6,7,8)
.map(新函数(){
可选的previousValue=可选的.empty();
@凌驾
公共可选应用(当前整数){
可选值=previousValue.map(上一个->当前-上一个);
previousValue=可选。of(当前);
返回值;
}
})
.filter(可选::isPresent)
.map(可选::get)
.forEach(System.out::println);
请注意,此实现不是一个简单的示例,因此您必须小心如何使用它。上面的示例是有效的,因为函数每次都被实例化(特别是,
previousValue
将针对每次使用重置)。但是,如果对代码进行重构,使函数存储在局部变量中,则该函数将不起作用,因为第二次使用该函数时,
previousValue
将不会为空。因此,通常不鼓励这种实现。

您可以使用my library,它有一种专门针对这种情况的方法:

StreamEx<Integer> s = StreamEx.of(1,2,3,4,6,7,8);
s = s.pairMap((a, b) -> b-a);
s.forEach(System.out::println);

不是纯函数,因为它改变了AtomicInteger的状态

OP要求
s
成为一个流(我认为使用
toArray()
不是一个好办法)。您创建一个数组,然后创建一个流,只需使用流在数组上迭代即可。在我看来,它是多余的。输入确实是一个数组,还是您已经得到了一个您必须使用的
IntStream
?如果以数组的形式获取值,对我来说似乎是合理的。出于好奇,使用
reduce
:函数仅实例化一次似乎不可行。毕竟它是有状态的
IntStreamEx s = IntStreamEx.of(1,2,3,4,6,7,8);
s = s.pairMap((a, b) -> b-a);
s.forEach(System.out::println);
public int[] diff(int... input) {
    AtomicInteger aInt = new AtomicInteger();
    return IntStream.of(input).map(v -> v - aInt.getAndSet(v)).skip(1).toArray();
}