From db0e590fb199c355fd963bbb2e5bdbdd54ea21cd Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 6 Aug 2022 21:28:05 +0800 Subject: [PATCH] =?UTF-8?q?:trollface:=20=E5=AE=A1=E6=9F=A5=E3=80=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/stream/FastStream.java | 37 +++++-- .../cn/hutool/core/stream/StreamUtil.java | 89 ++++++++++++++++- .../DropWhileSpliterator.java | 6 +- .../spliterators/IterateSpliterator.java | 76 ++++++++++++++ .../TakeWhileSpliterator.java | 6 +- .../stream/support/IterateSpliterator.java | 76 -------------- .../core/stream/support/StreamHelper.java | 99 ------------------- .../java/cn/hutool/core/lang/OptTest.java | 14 +-- .../cn/hutool/core/stream/FastStreamTest.java | 10 ++ 9 files changed, 214 insertions(+), 199 deletions(-) rename hutool-core/src/main/java/cn/hutool/core/stream/{support => spliterators}/DropWhileSpliterator.java (86%) create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/spliterators/IterateSpliterator.java rename hutool-core/src/main/java/cn/hutool/core/stream/{support => spliterators}/TakeWhileSpliterator.java (86%) delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/FastStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/FastStream.java index 350d6068c..1dc560a60 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/FastStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/FastStream.java @@ -6,7 +6,6 @@ import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.mutable.MutableInt; import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.stream.support.StreamHelper; import cn.hutool.core.text.StrUtil; import cn.hutool.core.util.ArrayUtil; @@ -158,7 +157,7 @@ public class FastStream implements Stream, Iterable { public static FastStream iterate(T seed, Predicate hasNext, UnaryOperator next) { Objects.requireNonNull(next); Objects.requireNonNull(hasNext); - return new FastStream<>(StreamHelper.iterate(seed, hasNext, next)); + return new FastStream<>(StreamUtil.iterate(seed, hasNext, next)); } /** @@ -674,8 +673,8 @@ public class FastStream implements Stream, Iterable { * @param 给定的数组类型 * @return 包含此流元素的指定的数组 * @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常 - * 例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException} - *
{@code String[] strings = Stream.builder().add(1).build().toArray(String[]::new); }
+ * 例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException} + *
{@code String[] strings = Stream.builder().add(1).build().toArray(String[]::new); }
*/ @Override public
A[] toArray(IntFunction generator) { @@ -1423,8 +1422,8 @@ public class FastStream implements Stream, Iterable { *

与 jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作

*
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
 	 * FastStream.iterate(1, i -> i + 1)
-	 *	.parallel()
-	 *	// 顺序执行
+	 * 	.parallel()
+	 * 	// 顺序执行
 	 * 	.takeWhile(e -> e < 50)
 	 * 	// 并发
 	 * 	.map(e -> e + 1)
@@ -1439,7 +1438,7 @@ public class FastStream implements Stream, Iterable {
 	 */
 	public FastStream takeWhile(Predicate predicate) {
 		Objects.requireNonNull(predicate);
-		return of(StreamHelper.takeWhile(stream, predicate));
+		return of(StreamUtil.takeWhile(stream, predicate));
 	}
 
 	/**
@@ -1459,8 +1458,8 @@ public class FastStream implements Stream, Iterable {
 	 * 

与 jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作

*
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
 	 * FastStream.iterate(1, i <= 100, i -> i + 1)
-	 *	.parallel()
-	 *	// 顺序执行
+	 * 	.parallel()
+	 * 	// 顺序执行
 	 * 	.dropWhile(e -> e < 50)
 	 * 	// 并发
 	 * 	.map(e -> e + 1)
@@ -1475,7 +1474,7 @@ public class FastStream implements Stream, Iterable {
 	 */
 	public FastStream dropWhile(Predicate predicate) {
 		Objects.requireNonNull(predicate);
-		return of(StreamHelper.dropWhile(stream, predicate));
+		return of(StreamUtil.dropWhile(stream, predicate));
 	}
 
 	/**
@@ -1490,6 +1489,24 @@ public class FastStream implements Stream, Iterable {
 		return dropWhile(predicate);
 	}
 
+	/**
+	 * 流是否为空
+	 *
+	 * @return 流是否为空
+	 */
+	public boolean isEmpty() {
+		return !findAny().isPresent();
+	}
+
+	/**
+	 * 流是否不为空
+	 *
+	 * @return 流是否不为空
+	 */
+	public boolean isNotEmpty() {
+		return !isEmpty();
+	}
+
 	public interface FastStreamBuilder extends Consumer, cn.hutool.core.builder.Builder> {
 
 		/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/StreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/stream/StreamUtil.java
index bbd4b011a..3cb1bd849 100644
--- a/hutool-core/src/main/java/cn/hutool/core/stream/StreamUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/stream/StreamUtil.java
@@ -3,6 +3,9 @@ package cn.hutool.core.stream;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.io.IORuntimeException;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.stream.spliterators.DropWhileSpliterator;
+import cn.hutool.core.stream.spliterators.IterateSpliterator;
+import cn.hutool.core.stream.spliterators.TakeWhileSpliterator;
 import cn.hutool.core.util.CharsetUtil;
 
 import java.io.File;
@@ -10,16 +13,20 @@ import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
+import static java.util.Objects.requireNonNull;
+
 /**
  * {@link Stream} 工具类
  *
- * @author looly
+ * @author looly emptypoint VampireAchao
  * @since 5.6.7
  */
 public class StreamUtil {
@@ -141,4 +148,84 @@ public class StreamUtil {
 								  final Function toStringFunc) {
 		return stream.collect(CollectorUtil.joining(delimiter, toStringFunc));
 	}
+
+
+	/**
+	 * 返回无限有序流
+	 * 该流由 初始值 然后判断条件 以及执行 迭代函数 进行迭代获取到元素
+	 *
+	 * @param      元素类型
+	 * @param seed    初始值
+	 * @param hasNext 条件值
+	 * @param next    用上一个元素作为参数执行并返回一个新的元素
+	 * @return 无限有序流
+	 */
+	public static  Stream iterate(T seed, Predicate hasNext, UnaryOperator next) {
+		requireNonNull(next);
+		requireNonNull(hasNext);
+		return StreamSupport.stream(IterateSpliterator.create(seed, hasNext, next), false);
+	}
+
+	/**
+	 * 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素
+	 * 

与 jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作

+ *

本环节中是顺序执行的, 但是后续操作可以支持并行流

+ *

但是不建议在并行流中使用, 除非你确定 takeWhile 之后的操作能在并行流中受益很多

+ * + * @param source 源流 + * @param 元素类型 + * @param predicate 断言 + * @return 与指定断言匹配的元素组成的流 + */ + public static Stream takeWhile(Stream source, Predicate predicate) { + requireNonNull(source); + requireNonNull(predicate); + return createStatefulNewStream(source, TakeWhileSpliterator.create(source.spliterator(), predicate)); + } + + /** + * 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 + *

与 jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作

+ *

本环节中是顺序执行的, 但是后续操作可以支持并行流

+ *

但是不建议在并行流中使用, 除非你确定 dropWhile 之后的操作能在并行流中受益很多

+ * + * @param source 源流 + * @param 元素类型 + * @param predicate 断言 + * @return 剩余元素组成的流 + */ + public static Stream dropWhile(Stream source, Predicate predicate) { + requireNonNull(source); + requireNonNull(predicate); + return createStatefulNewStream(source, DropWhileSpliterator.create(source.spliterator(), predicate)); + } + + // region 私有方法 + /* ================================================== 私有方法 =================================================== */ + + /** + * 根据 源流 和 新的Spliterator 生成新的流 + *

这是一个 顺序的、有状态的流

+ *

在新流的第一个节点是顺序执行的, 但是后续操作可以支持并行流

+ * + * @param source 源流 + * @param newSpliterator 新流的Spliterator + * @param 旧流的元素类型 + * @param 新流的元素类型 + * @return 新流 + */ + private static Stream createStatefulNewStream(Stream source, Spliterator newSpliterator) { + // 创建新流 + Stream newStream = StreamSupport.stream(newSpliterator, source.isParallel()); + // 如果旧流是并行流, 新流主动调用一个有状态的操作, 虽然没有意义, 但是可以让后续的无状态节点正常并发 + if (source.isParallel()) { + newStream = newStream.limit(Long.MAX_VALUE); + } + // 由于新流不与旧流的节点关联, 所以需要主动设置旧流的close方法, 哪怕几乎不可能有人在旧流上设置onClose操作 + return newStream.onClose(source::close); + } + + /* ============================================================================================================== */ + // endregion + } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/DropWhileSpliterator.java similarity index 86% rename from hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java rename to hutool-core/src/main/java/cn/hutool/core/stream/spliterators/DropWhileSpliterator.java index 0ab4433a9..4bb620670 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/DropWhileSpliterator.java @@ -1,4 +1,4 @@ -package cn.hutool.core.stream.support; +package cn.hutool.core.stream.spliterators; import java.util.Comparator; import java.util.Spliterator; @@ -12,9 +12,9 @@ import java.util.function.Predicate; * @author emptypoint * @since 6.0.0 */ -class DropWhileSpliterator implements Spliterator { +public class DropWhileSpliterator implements Spliterator { - static DropWhileSpliterator create(Spliterator source, Predicate predicate) { + public static DropWhileSpliterator create(Spliterator source, Predicate predicate) { return new DropWhileSpliterator<>(source, predicate); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/IterateSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/IterateSpliterator.java new file mode 100644 index 000000000..edf2170f5 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/IterateSpliterator.java @@ -0,0 +1,76 @@ +package cn.hutool.core.stream.spliterators; + +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +/** + * 无限有序流 的Spliterator + * + * @author VampireAchao + * @since 6.0.0 + */ +public class IterateSpliterator extends Spliterators.AbstractSpliterator { + private final T seed; + private final Predicate hasNext; + private final UnaryOperator next; + private T prev; + private boolean started; + private boolean finished; + + /** + * Creates a spliterator reporting the given estimated size and + * additionalCharacteristics. + */ + IterateSpliterator(T seed, Predicate hasNext, UnaryOperator next) { + super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE); + this.seed = seed; + this.hasNext = hasNext; + this.next = next; + } + + public static IterateSpliterator create(T seed, Predicate hasNext, UnaryOperator next) { + return new IterateSpliterator<>(seed, hasNext, next); + } + + @Override + public boolean tryAdvance(Consumer action) { + Objects.requireNonNull(action); + if (finished) { + return false; + } + T t; + if (started) { + t = next.apply(prev); + } else { + t = seed; + started = true; + } + if (!hasNext.test(t)) { + prev = null; + finished = true; + return false; + } + prev = t; + action.accept(prev); + return true; + } + + @Override + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + if (finished) { + return; + } + finished = true; + T t = started ? next.apply(prev) : seed; + prev = null; + while (hasNext.test(t)) { + action.accept(t); + t = next.apply(t); + } + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/TakeWhileSpliterator.java similarity index 86% rename from hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java rename to hutool-core/src/main/java/cn/hutool/core/stream/spliterators/TakeWhileSpliterator.java index dc0ca8da6..318e33351 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/TakeWhileSpliterator.java @@ -1,4 +1,4 @@ -package cn.hutool.core.stream.support; +package cn.hutool.core.stream.spliterators; import java.util.Comparator; import java.util.Spliterator; @@ -12,9 +12,9 @@ import java.util.function.Predicate; * @author emptypoint * @since 6.0.0 */ -class TakeWhileSpliterator implements Spliterator { +public class TakeWhileSpliterator implements Spliterator { - static TakeWhileSpliterator create(Spliterator source, Predicate predicate) { + public static TakeWhileSpliterator create(Spliterator source, Predicate predicate) { return new TakeWhileSpliterator<>(source, predicate); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java deleted file mode 100644 index 71df09b7b..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java +++ /dev/null @@ -1,76 +0,0 @@ -package cn.hutool.core.stream.support; - -import java.util.Objects; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; - -/** - * 无限有序流 的Spliterator - * - * @author VampireAchao - * @since 6.0.0 - */ -class IterateSpliterator extends Spliterators.AbstractSpliterator { - public static IterateSpliterator create(T seed, Predicate hasNext, UnaryOperator next) { - return new IterateSpliterator<>(seed, hasNext, next); - } - - /** - * Creates a spliterator reporting the given estimated size and - * additionalCharacteristics. - */ - IterateSpliterator(T seed, Predicate hasNext, UnaryOperator next) { - super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE); - this.seed = seed; - this.hasNext = hasNext; - this.next = next; - } - - private final T seed; - private final Predicate hasNext; - private final UnaryOperator next; - private T prev; - private boolean started; - private boolean finished; - - @Override - public boolean tryAdvance(Consumer action) { - Objects.requireNonNull(action); - if (finished) { - return false; - } - T t; - if (started) { - t = next.apply(prev); - } else { - t = seed; - started = true; - } - if (!hasNext.test(t)) { - prev = null; - finished = true; - return false; - } - prev = t; - action.accept(prev); - return true; - } - - @Override - public void forEachRemaining(Consumer action) { - Objects.requireNonNull(action); - if (finished) { - return; - } - finished = true; - T t = started ? next.apply(prev) : seed; - prev = null; - while (hasNext.test(t)) { - action.accept(t); - t = next.apply(t); - } - } -} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java b/hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java deleted file mode 100644 index 478cbb091..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java +++ /dev/null @@ -1,99 +0,0 @@ -package cn.hutool.core.stream.support; - -import java.util.Spliterator; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import static java.util.Objects.requireNonNull; - -/** - * FastStream 辅助工具类 - * - * @author emptypoint - * @since 6.0.0 - */ -public final class StreamHelper { - private StreamHelper() { - } - - /** - * 返回无限有序流 - * 该流由 初始值 然后判断条件 以及执行 迭代函数 进行迭代获取到元素 - * - * @param 元素类型 - * @param seed 初始值 - * @param hasNext 条件值 - * @param next 用上一个元素作为参数执行并返回一个新的元素 - * @return 无限有序流 - */ - public static Stream iterate(T seed, Predicate hasNext, UnaryOperator next) { - requireNonNull(next); - requireNonNull(hasNext); - return StreamSupport.stream(IterateSpliterator.create(seed, hasNext, next), false); - } - - /** - * 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 - *

与 jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作

- *

本环节中是顺序执行的, 但是后续操作可以支持并行流

- *

但是不建议在并行流中使用, 除非你确定 takeWhile 之后的操作能在并行流中受益很多

- * - * @param source 源流 - * @param 元素类型 - * @param predicate 断言 - * @return 与指定断言匹配的元素组成的流 - */ - public static Stream takeWhile(Stream source, Predicate predicate) { - requireNonNull(source); - requireNonNull(predicate); - return createStatefulNewStream(source, TakeWhileSpliterator.create(source.spliterator(), predicate)); - } - - /** - * 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 - *

与 jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作

- *

本环节中是顺序执行的, 但是后续操作可以支持并行流

- *

但是不建议在并行流中使用, 除非你确定 dropWhile 之后的操作能在并行流中受益很多

- * - * @param source 源流 - * @param 元素类型 - * @param predicate 断言 - * @return 剩余元素组成的流 - */ - public static Stream dropWhile(Stream source, Predicate predicate) { - requireNonNull(source); - requireNonNull(predicate); - return createStatefulNewStream(source, DropWhileSpliterator.create(source.spliterator(), predicate)); - } - - // region 私有方法 - /* ================================================== 私有方法 =================================================== */ - - /** - * 根据 源流 和 新的Spliterator 生成新的流 - *

这是一个 顺序的、有状态的流

- *

在新流的第一个节点是顺序执行的, 但是后续操作可以支持并行流

- * - * @param source 源流 - * @param newSpliterator 新流的Spliterator - * @param 旧流的元素类型 - * @param 新流的元素类型 - * @return 新流 - */ - private static Stream createStatefulNewStream(Stream source, Spliterator newSpliterator) { - // 创建新流 - Stream newStream = StreamSupport.stream(newSpliterator, source.isParallel()); - // 如果旧流是并行流, 新流主动调用一个有状态的操作, 虽然没有意义, 但是可以让后续的无状态节点正常并发 - if (source.isParallel()) { - newStream = newStream.limit(Long.MAX_VALUE); - } - // 由于新流不与旧流的节点关联, 所以需要主动设置旧流的close方法, 哪怕几乎不可能有人在旧流上设置onClose操作 - return newStream.onClose(source::close); - } - - /* ============================================================================================================== */ - // endregion - -} diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java index ff3d4ace6..f1542d46a 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/OptTest.java @@ -8,13 +8,7 @@ import lombok.NoArgsConstructor; import org.junit.Assert; import org.junit.Test; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; +import java.util.*; import java.util.stream.Stream; /** @@ -24,6 +18,12 @@ import java.util.stream.Stream; */ public class OptTest { + @Test + public void ofTest() { + Assert.assertTrue(Opt.of(Optional.empty()).isEmpty()); + Assert.assertTrue(Opt.of(Optional.of(1)).isPresent()); + } + @Test public void ofBlankAbleTest() { // ofBlankAble相对于ofNullable考虑了字符串为空串的情况 diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/FastStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/FastStreamTest.java index 937b7378d..e08dfd2b3 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/FastStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/FastStreamTest.java @@ -193,6 +193,8 @@ public class FastStreamTest { flatMapIter = FastStream.of(list).flatMapIter(e -> Arrays.asList(e, e * 10)).toList(); Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter); + // 不报npe测试 + Assert.assertTrue(FastStream.of(list).flatMapIter(e -> null).isEmpty()); } @Test @@ -310,6 +312,9 @@ public class FastStreamTest { List list = Arrays.asList("dromara", "hutool", "sweet"); List zip = FastStream.of(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList(); Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); + + zip = FastStream.iterate(1, i -> i + 1).zip(list, (e1, e2) -> e1 + "." + e2).toList(); + Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); } @Test @@ -388,4 +393,9 @@ public class FastStreamTest { Assert.assertEquals(Arrays.asList(5, 7, 9), res2); } + @Test + public void testIsNotEmpty() { + Assert.assertTrue(FastStream.of(1).isNotEmpty()); + } + }