带有状态的Java Streams - 一项简单的练习

时间:2016-02-27 23:52:31

标签: java java-8 java-stream fold

在获得Java 8流程的过程中,以下练习阻止了我。

  

鉴于IntStream.range(0, 6)。在下面生成字符串流:

"0, 1"
"1, 2"
"2, 3"
"3, 4"
"4, 5"

我想过使用Collectors.collectAndThen将它传递给好的旧列表或数组并循环以构造字符串列表,如下所示:

List<String> strgs = new ArrayList<>();
String prev = String.valueOf(nums[0]);
for (int i = 1; i < nums.length; i++) {
    strgs.add(prev+", "+String.valueOf(nums[i]));
    prev = String.valueOf(nums[i]);  
}

但它没有使用流的力量。我觉得Venkat Subramaniam说“我之后想洗个澡”。 我想知道如何应用功能技术,所以我可以在编码后跳过洗澡!

另外,我想避免像StreamEx或JavaRx这样的库,我想坚持使用普通的Java 8 API。

编辑: @Tunaki,谢谢你在我的问题中指出不清楚的措辞。它是由Stream的两个连续元素组成的对。更具体的是,像[1, 3, 5, 7, 9, ...]这样的流将是

"1, 3"
"3, 5"
"5, 7"
...

修改2

在向所有答案致敬之后,虽然我的问题与Tunaki指出的另一个问题重复。我想扩展一个社区讨论,以寻找Bohemian提供的答案。虽然他的回答不被一些人所厌恶,但它提出了一个严重的问题,即减少手术的副作用。我对社区的要求是为该问题提供合理的反有效技术。因此,我想重用波希米亚语的答案如下:

  

给定输入:nums = new int [] {1,3,5,7,9}

请考虑以下代码段:

List<CharSequence> stringList = new ArrayList<>();
IntBinaryOperator reductionWithSideEffect = (int left, int right) -> {
        stringList.add(new StringBuilder().append(left).append(", ").append(right));
        return right;
};
Arrays.stream(nums)
        .reduce(reductionWithSideEffect);
System.out.println(String.join(", ", stringList));

4 个答案:

答案 0 :(得分:7)

在我看来,解决这个问题最干净的方法是编写一个自定义分裂器并在其上创建一个Stream。如果你不需要绝对最大的性能并且不关心并行处理(并行流将工作,但效率低下),这并不是很难。像这样的东西会起作用:

public static <T, R> Stream<R> pairMap(BaseStream<T, ?> source,
        BiFunction<? super T, ? super T, ? extends R> mapper) {
    Spliterator<T> spltr = source.spliterator();
    long sourceSize = spltr.estimateSize();
    Spliterator<R> result = new Spliterators.AbstractSpliterator<R>(
            sourceSize > 0 && sourceSize < Long.MAX_VALUE ? sourceSize - 1 : sourceSize,
            spltr.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) {
        T prev;
        boolean started;

        @Override
        public boolean tryAdvance(Consumer<? super R> action) {
            if (!started) {
                if (!spltr.tryAdvance(t -> prev = t))
                    return false;
                started = true;
            }
            return spltr.tryAdvance(t -> action.accept(mapper.apply(prev, prev = t)));
        }
    };
    return StreamSupport.stream(result, source.isParallel()).onClose(source::close);
}

这里mapper是根据输入流的一对相邻元素创建新流元素的函数。

用法示例:

pairMap(IntStream.range(0, 6), (a, b) -> a + ", " + b).forEach(System.out::println);

输出:

0, 1
1, 2
2, 3
3, 4
4, 5

答案 1 :(得分:0)

正如Tunaki在评论中提到的,Stream API并不适合这个用例。我能做的最好的事情就是编写一个有状态的映射函数来记住最后遇到的元素是什么。

注意 - 正如Tagir Valeev在评论中指出的那样,如果调用者使用BaseStream.parallel()将结果流恢复为并行模式,则此解决方案会中断。

public class StreamExercise {
    public static void main(String[] args) {
        f(IntStream.range(0, 6))
            .forEach(s -> System.out.println(s));

        System.out.println();

        f(IntStream.of(1,3,5,7,9))
            .forEach(s -> System.out.println(s));
    }

    private static Stream<String> f(IntStream input) {
        IntFunction<String> mapper = new IntFunction<String>() {
            private int last = -1;

            @Override
            public String apply(int value) {
                String result = last + ", " + value;
                last = value;
                return result;
            }

        };

        return input.sequential().mapToObj(mapper).skip(1);
    }
}

输出:

0, 1
1, 2
2, 3
3, 4
4, 5

1, 3
3, 5
5, 7
7, 9

答案 2 :(得分:0)

简单易行:

public static void main(String[] args) {
    generatePairs(Arrays.asList(1, 3, 5, 7, 9)).forEach(System.out::println);
}

public static <E> List<Pair<E>> generatePairs(List<E> src) {
    if (src.size() < 2) {
        throw new IllegalArgumentException("src.size must be greater than 1");
    }
    return IntStream.range(0, src.size() - 1)
            .mapToObj(i -> new Pair<>(src.get(i), src.get(i + 1)))
            .collect(Collectors.toList());
}

Pair类:

public final class Pair<E> {

    private E left;
    private E right;

    public Pair() {
    }

    public Pair(E left, E right) {
        this.left = left;
        this.right = right;
    }

    public E getLeft() {
        return left;
    }

    public void setLeft(E left) {
        this.left = left;
    }

    public E getRight() {
        return right;
    }

    public void setRight(E right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "{" + left +
                ", " + right +
                '}';
    }

}

编辑

这是一个完整的例子:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;

public final class Pair<L, R> implements Comparable<Pair<L, R>> {

    private final Comparator<? super L> comparator;

    private L left;
    private R right;

    public Pair() {
        comparator = null;
    }

    public Pair(Comparator<? super L> comparator) {
        this.comparator = comparator;
    }

    public Pair(L left, R right) {
        this(left, right, null);
    }

    public Pair(L left, R right, Comparator<? super L> comparator) {
        this.left = left;
        this.right = right;
        this.comparator = comparator;
    }

    public L getLeft() {
        return left;
    }

    public void setLeft(L left) {
        this.left = left;
    }

    public R getRight() {
        return right;
    }

    public void setRight(R right) {
        this.right = right;
    }

    @Override
    public int hashCode() {
        return Objects.hash(left, right);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || !(obj instanceof Pair)) {
            return false;
        }
        Pair that = (Pair) obj;
        return Objects.equals(left, that.left) && Objects.equals(right, that.right);
    }

    @Override
    public String toString() {
        return "{" + left +
                ", " + right +
                '}';
    }

    @Override
    @SuppressWarnings("unchecked")
    public int compareTo(Pair<L, R> o) {
        return comparator == null ? ((Comparable<? super L>) left).compareTo(o.left) : comparator.compare(left, o.left);
    }

    public static abstract class Stream {

        private Stream() {
        }

        public final java.util.stream.Stream<Pair<Integer, Integer>> of(IntStream src) {
            return of(src.boxed());
        }

        public final java.util.stream.Stream<Pair<Long, Long>> of(LongStream src) {
            return of(src.boxed());
        }

        public final java.util.stream.Stream<Pair<Double, Double>> of(DoubleStream src) {
            return of(src.boxed());
        }

        public final <E> java.util.stream.Stream<Pair<E, E>> of(java.util.stream.Stream<E> src) {
            return of(src.collect(Collectors.toList()));
        }

        @SuppressWarnings("all")
        public abstract <E> java.util.stream.Stream<Pair<E, E>> of(E... src);

        public abstract <E> java.util.stream.Stream<Pair<E, E>> of(List<E> src);

        protected static void checkSize(int size) {
            if (size < 1) {
                throw new IllegalArgumentException("Empty source.");
            }
        }

        protected static <E> E getOrNull(E[] array, int index) {
            return index < array.length ? array[index] : null;
        }

        protected static <E> E getOrNull(List<E> list, int index) {
            return index < list.size() ? list.get(index) : null;
        }

        public static Stream chained() {
            return new ChainedPairStream();
        }

        public static Stream distinct() {
            return new DistinctPairStream();
        }

    }

    private static final class ChainedPairStream extends Stream {

        @SafeVarargs
        public final <E> java.util.stream.Stream<Pair<E, E>> of(E... src) {
            int length = src.length;
            checkSize(length);
            return IntStream.range(0, Math.max(1, length - 1)).mapToObj(i -> new Pair<>(src[i], getOrNull(src, i + 1)));
        }

        public final <E> java.util.stream.Stream<Pair<E, E>> of(List<E> src) {
            int size = src.size();
            checkSize(size);
            return IntStream.range(0, Math.max(1, size - 1)).mapToObj(i -> new Pair<>(src.get(i), getOrNull(src, i + 1)));
        }

    }

    private static final class DistinctPairStream extends Stream {

        @SafeVarargs
        public final <E> java.util.stream.Stream<Pair<E, E>> of(E... src) {
            int length = src.length;
            checkSize(length);
            return IntStream.iterate(0, i -> i + 2)
                    .limit((long) Math.ceil(length / 2.0))
                    .mapToObj(i -> new Pair<>(src[i], getOrNull(src, i + 1)));
        }

        public final <E> java.util.stream.Stream<Pair<E, E>> of(List<E> src) {
            int size = src.size();
            checkSize(size);
            return IntStream.iterate(0, i -> i + 2)
                    .limit((long) Math.ceil(size / 2.0))
                    .mapToObj(i -> new Pair<>(src.get(i), getOrNull(src, i + 1)));
        }

    }

    public static void main(String[] args) {
        Pair.Stream.distinct().of(1, 2, 3, 4, 5, 6).forEach(System.out::println);

        Pair.Stream.chained().of(Arrays.asList(1, 3, 5, 7, 9)).forEach(System.out::println);

        Pair.Stream.chained().of(Arrays.stream(new int[]{0, 2, 4, 6, 8})).forEach(System.out::println);
        Pair.Stream.distinct().of(Arrays.stream(new int[]{0, 2, 4, 6, 8})).forEach(System.out::println);

        Pair.Stream.distinct().of(IntStream.range(0, 6)).forEach(System.out::println);
    }

}

答案 3 :(得分:-1)

我认为完成工作的最短代码是:

IntStream.range(0, 6).reduce((a, b) -> {System.out.println(a + ", " + b); return b;});

由于上述(工作)代码引起了争议,因此这是一个简单易读的解决方案,可以接受使用API​​:

class PairPrinter implements IntConsumer {
    private boolean set;
    private int previous;

    public void accept(int i) {
        if (set) {
            System.out.println(previous + ", " + i);
        }
        set = true;
        previous = i;
    }
}

PairPrinter printer = new PairPrinter();
IntStream.range(0, 6).forEach(printer);

哪个(也)产生了期望的结果。