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

标签 java java-8 java-stream fold

<分区>

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

Given the IntStream.range(0, 6). Produce the String Stream below:

"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, ...] 这样的 Stream 将是

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

编辑2

在向所有答案致敬之后,尽管我的问题与 Tunaki 指出的另一个问题重复。我想针对 Bohemian 提供的答案展开社区讨论。尽管有些人不喜欢他的回答,但它提出了一个严重的问题,即具有副作用的 reduce 操作。我对社区的要求是为该问题提供合理的反有效技术。因此,我想按如下方式重用 Bohemian 答案:

Given the input: 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));

最佳答案

在我看来,解决这个问题最简洁的方法是编写一个自定义拆分器并在其上创建一个 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

关于带状态的 Java Streams - 一个简单的练习,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35676949/

相关文章:

javascript - 将 HMAC-SHA384 函数从 Java 转换为 JavaScript

java - 尝试创建 Web 服务客户端时找不到类异常

Java 令人困惑的三元 Sonar 违规问题

java - 按字母顺序连接有关字段名称的对象值

java - Android 中 LocationManager 的 requestLocationUpdates 到底是如何工作的?

java - 从多个 JEditorPane 选择文本

java - Spliterator - 大小旗帜与小型旗帜

collections - java8如何收集数据并作为对象的属性添加到列表中

java - 在 mapToInt 之后调用 map 有什么好处吗?

java - 构造函数引用和调用构造函数的 lambda 表达式有什么区别?