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 super T> 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 super T> 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/spliterators/DropWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/DropWhileSpliterator.java
new file mode 100644
index 000000000..4bb620670
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/DropWhileSpliterator.java
@@ -0,0 +1,72 @@
+package cn.hutool.core.stream.spliterators;
+
+import java.util.Comparator;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * dropWhile 的 Spliterator
+ * 借鉴自StreamEx
+ *
+ * @author emptypoint
+ * @since 6.0.0
+ */
+public class DropWhileSpliterator implements Spliterator {
+
+ public static DropWhileSpliterator create(Spliterator source, Predicate super T> predicate) {
+ return new DropWhileSpliterator<>(source, predicate);
+ }
+
+ private final Spliterator source;
+ private final Predicate super T> predicate;
+ private boolean isFound = false;
+
+ private DropWhileSpliterator(Spliterator source, Predicate super T> predicate) {
+ this.source = source;
+ this.predicate = predicate;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer super T> action) {
+ boolean hasNext = true;
+ // 如果 还没找到 并且 流中还有元素 继续找
+ while (!isFound && hasNext) {
+ hasNext = source.tryAdvance(e -> {
+ if (!predicate.test(e)) {
+ // 第一次不匹配
+ isFound = true;
+ action.accept(e);
+ }
+ });
+ }
+
+ // 对找到的元素进行后续处理
+ if (isFound) {
+ source.forEachRemaining(action);
+ }
+
+ // 该环节已经处理完成
+ return false;
+ }
+
+ @Override
+ public Spliterator trySplit() {
+ return null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public int characteristics() {
+ return source.characteristics() & ~Spliterator.SIZED;
+ }
+
+ @Override
+ public Comparator super T> getComparator() {
+ return source.getComparator();
+ }
+}
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 super T> 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 super T> 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 super T> hasNext, UnaryOperator next) {
+ return new IterateSpliterator<>(seed, hasNext, next);
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer super T> 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 super T> 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/spliterators/TakeWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/TakeWhileSpliterator.java
new file mode 100644
index 000000000..318e33351
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/stream/spliterators/TakeWhileSpliterator.java
@@ -0,0 +1,68 @@
+package cn.hutool.core.stream.spliterators;
+
+import java.util.Comparator;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * takeWhile 的 Spliterator
+ * 借鉴自StreamEx
+ *
+ * @author emptypoint
+ * @since 6.0.0
+ */
+public class TakeWhileSpliterator implements Spliterator {
+
+ public static TakeWhileSpliterator create(Spliterator source, Predicate super T> predicate) {
+ return new TakeWhileSpliterator<>(source, predicate);
+ }
+
+ private final Spliterator source;
+ private final Predicate super T> predicate;
+ private boolean isContinue = true;
+
+ TakeWhileSpliterator(Spliterator source, Predicate super T> predicate) {
+ this.source = source;
+ this.predicate = predicate;
+ }
+
+ @Override
+ public boolean tryAdvance(Consumer super T> action) {
+ boolean hasNext = true;
+ // 如果 还可以继续 并且 流中还有元素 则继续遍历
+ while (isContinue && hasNext) {
+ hasNext = source.tryAdvance(e -> {
+ if (predicate.test(e)) {
+ action.accept(e);
+ } else {
+ // 终止遍历剩下的元素
+ isContinue = false;
+ }
+ });
+ }
+ // 该环节已经处理完成
+ return false;
+ }
+
+ @Override
+ public Spliterator trySplit() {
+ return null;
+ }
+
+ @Override
+ public long estimateSize() {
+ return isContinue ? source.estimateSize() : 0;
+ }
+
+ @Override
+ public int characteristics() {
+ return source.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
+ }
+
+ @Override
+ public Comparator super T> getComparator() {
+ return source.getComparator();
+ }
+}
+
diff --git a/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java
index c376f2b60..12364e7f8 100755
--- a/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/util/RandomUtil.java
@@ -575,11 +575,11 @@ public class RandomUtil {
if (StrUtil.isEmpty(baseString)) {
return StrUtil.EMPTY;
}
- final StringBuilder sb = new StringBuilder(length);
-
- if (length < 1) {
+ if(length < 1){
length = 1;
}
+
+ final StringBuilder sb = new StringBuilder(length);
final int baseLength = baseString.length();
for (int i = 0; i < length; i++) {
final int number = randomInt(baseLength);
diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java
index b5f067915..8729ec3ec 100644
--- a/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/date/DateTimeTest.java
@@ -140,4 +140,5 @@ public class DateTimeTest {
final String a = "2021-09-27 00:00:99";
new DateTime(a, DatePattern.NORM_DATETIME_FORMAT, false);
}
+
}
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());
+ }
+
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java
index 25c4c67a0..1fc845ee9 100644
--- a/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/RandomUtilTest.java
@@ -75,6 +75,12 @@ public class RandomUtilTest {
}
}
+ @Test
+ public void randomStringOfLengthTest(){
+ final String s = RandomUtil.randomString("123", -1);
+ Assert.assertNotNull(s);
+ }
+
@Test
public void generateRandomNumberTest(){
final int[] ints = RandomUtil.randomPickInts(5, NumberUtil.range(5, 20));
diff --git a/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java b/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java
index 0e99f1dda..2005ca077 100644
--- a/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java
+++ b/hutool-db/src/test/java/cn/hutool/db/CRUDTest.java
@@ -195,4 +195,10 @@ public class CRUDTest {
MapUtil.of("ids", new int[]{1, 2, 3}));
Assert.assertEquals(2, results.size());
}
+
+ @Test
+ @Ignore
+ public void findWithDotTest(){
+ db.find(Entity.of("user").set("WTUR.Other.Rg.S.WTName", "value"));
+ }
}
diff --git a/hutool-json/src/test/java/cn/hutool/json/Issue2507Test.java b/hutool-json/src/test/java/cn/hutool/json/Issue2507Test.java
new file mode 100755
index 000000000..5101aec1b
--- /dev/null
+++ b/hutool-json/src/test/java/cn/hutool/json/Issue2507Test.java
@@ -0,0 +1,17 @@
+package cn.hutool.json;
+
+import cn.hutool.core.lang.Console;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class Issue2507Test {
+
+ @Test
+ @Ignore
+ public void xmlToJsonTest(){
+ String xml = " 低盐饮食[嘱托]]>
]]> 流质饮食
]]> ";
+ JSONObject jsonObject = JSONUtil.xmlToJson(xml);
+
+ Console.log(jsonObject.toStringPretty());
+ }
+}