From fc8923b2e911d898183ddd259e7375cf22d9caa8 Mon Sep 17 00:00:00 2001 From: Zjp <1215582715@qq.com> Date: Fri, 5 Aug 2022 17:06:21 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BC=98=E5=8C=96FastStream:=201.=E6=96=B0?= =?UTF-8?q?=E5=A2=9EtakeWhile=E3=80=81dropWhile=E6=96=B9=E6=B3=95;=202.?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86=E5=86=99=E6=B3=95,=20?= =?UTF-8?q?=E5=B8=8C=E6=9C=9B=E8=BF=9B=E4=B8=80=E6=AD=A5=E6=8F=90=E9=AB=98?= =?UTF-8?q?=E6=95=88=E7=8E=87;=203.=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81,=20=E7=A7=BB=E5=8A=A8=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BB=A3=E7=A0=81=E5=88=B0=E5=85=B6=E4=BB=96=E7=B1=BB?= =?UTF-8?q?=E4=B8=AD;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/collection/ListUtil.java | 48 +++ .../main/java/cn/hutool/core/lang/Opt.java | 13 + .../hutool/core/lang/mutable/MutableInt.java | 41 +++ .../hutool/core/lang/mutable/MutableObj.java | 14 +- .../cn/hutool/core/stream/FastStream.java | 297 +++++++++--------- .../stream/support/DropWhileSpliterator.java | 72 +++++ .../stream/support/IterateSpliterator.java | 76 +++++ .../core/stream/support/StreamHelper.java | 99 ++++++ .../stream/support/TakeWhileSpliterator.java | 68 ++++ .../cn/hutool/core/stream/FastStreamTest.java | 95 +++++- 10 files changed, 660 insertions(+), 163 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java index 28f446daa..e21f3aa92 100755 --- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java @@ -13,6 +13,7 @@ import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.PageUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -692,4 +693,51 @@ public class ListUtil { } return list; } + + /** + * 类似js的splice函数 + * + * @param start 起始下标, 可以为负数, -1代表最后一个元素 + * @param deleteCount 删除个数,必须是正整数 + * @param items 放入值 + * @return 操作后的流 + * @since 6.0.0 + */ + @SafeVarargs + public static List splice(List list, int start, int deleteCount, T... items) { + if (CollUtil.isEmpty(list)) { + return zero(); + } + final int size = list.size(); + // 从后往前查找 + if (start < 0) { + start += size; + } else if (start >= size) { + // 直接在尾部追加,不删除 + start = size; + deleteCount = 0; + } + // 起始位置 加上 删除的数量 超过 数据长度,需要重新计算需要删除的数量 + if (start + deleteCount > size) { + deleteCount = size - start; + } + + // 新列表的长度 + final int newSize = size - deleteCount + items.length; + List resList = list; + // 新列表的长度 大于 旧列表,创建新列表 + if (newSize > size) { + resList = new ArrayList<>(newSize); + resList.addAll(list); + } + // 需要删除的部分 + if (deleteCount > 0) { + resList.subList(start, start + deleteCount).clear(); + } + // 新增的部分 + if (ArrayUtil.isNotEmpty(items)) { + resList.addAll(start, Arrays.asList(items)); + } + return resList; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java index e4730f1f3..66315b586 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java @@ -127,6 +127,19 @@ public class Opt { } } + /** + * 根据 {@link Optional} 构造 {@code Opt} + * + * @param optional optional + * @param 包裹的元素类型 + * @return 一个包裹里元素可能为空的 {@code Opt} + * @since 6.0.0 + */ + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public static Opt of(Optional optional) { + return ofNullable(optional).flattedMap(Function.identity()); + } + /** * 包裹里实际的元素 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java index 912a67afc..9cb300af5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java @@ -81,6 +81,47 @@ public class MutableInt extends Number implements Comparable, Mutabl return this; } + // ----------------------------------------------------------------------- + /** + * 先加1, 再获取值 + * + * @return +1后的值 + * @since 6.0.0 + */ + public int incrementAndGet() { + return ++value; + } + + /** + * 先获取原来的值, 再加1 + * + * @return 原始值 + * @since 6.0.0 + */ + public int getAndIncrement() { + return value++; + } + + /** + * 先减1, 再获取值 + * + * @return -1后的值 + * @since 6.0.0 + */ + public int decrementAndGet() { + return --value; + } + + /** + * 先获取原来的值, 再减1 + * + * @return 原始值 + * @since 6.0.0 + */ + public int getAndDecrement() { + return value--; + } + // ----------------------------------------------------------------------- /** * 增加值 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java index 64f71dc87..c8a9397b2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java @@ -1,6 +1,7 @@ package cn.hutool.core.lang.mutable; import java.io.Serializable; +import java.util.function.Consumer; /** * 可变{@code Object} @@ -8,7 +9,7 @@ import java.io.Serializable; * @param 可变的类型 * @since 3.0.1 */ -public class MutableObj implements Mutable, Serializable { +public class MutableObj implements Mutable, Serializable, Consumer { private static final long serialVersionUID = 1L; /** @@ -50,6 +51,17 @@ public class MutableObj implements Mutable, Serializable { this.value = value; } + /** + * 消费元素 + * + * @param t t + * @since 6.0.0 + */ + @Override + public void accept(T t) { + this.value = t; + } + // ----------------------------------------------------------------------- @Override public boolean equals(final Object obj) { 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 708583b1f..7619a5303 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 @@ -1,12 +1,12 @@ package cn.hutool.core.stream; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Console; 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; @@ -61,6 +61,7 @@ public class FastStream implements Stream, Iterable { this.stream = stream; } + // region Static method // --------------------------------------------------------------- Static method start /** @@ -157,51 +158,7 @@ public class FastStream implements Stream, Iterable { public static FastStream iterate(T seed, Predicate hasNext, UnaryOperator next) { Objects.requireNonNull(next); Objects.requireNonNull(hasNext); - Spliterator spliterator = new Spliterators.AbstractSpliterator(Long.MAX_VALUE, - Spliterator.ORDERED | Spliterator.IMMUTABLE) { - T prev; - boolean started; - 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); - } - } - }; - return new FastStream<>(StreamSupport.stream(spliterator, false)); + return new FastStream<>(StreamHelper.iterate(seed, hasNext, next)); } /** @@ -279,6 +236,7 @@ public class FastStream implements Stream, Iterable { } // --------------------------------------------------------------- Static method end + // endregion /** * 过滤元素,返回与指定断言匹配的元素组成的流 @@ -303,10 +261,9 @@ public class FastStream implements Stream, Iterable { */ public FastStream filter(Function mapper, R value) { Objects.requireNonNull(mapper); - return filter(e -> Objects.equals(Opt.ofNullable(e).map(mapper).get(), value)); + return filter(e -> Objects.equals(mapper.apply(e), value)); } - /** * 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1 * 这是一个无状态中间操作 @@ -320,7 +277,7 @@ public class FastStream implements Stream, Iterable { return filter(e -> predicate.test(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return filter(e -> predicate.test(e, index.increment().get())); + return filter(e -> predicate.test(e, index.incrementAndGet())); } } @@ -360,7 +317,7 @@ public class FastStream implements Stream, Iterable { return map(e -> mapper.apply(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return map(e -> mapper.apply(e, index.increment().get())); + return map(e -> mapper.apply(e, index.incrementAndGet())); } } @@ -395,7 +352,7 @@ public class FastStream implements Stream, Iterable { return flatMap(e -> mapper.apply(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return flatMap(e -> mapper.apply(e, index.increment().get())); + return flatMap(e -> mapper.apply(e, index.incrementAndGet())); } } @@ -449,7 +406,7 @@ public class FastStream implements Stream, Iterable { */ public FastStream flatMapIter(Function> mapper) { Objects.requireNonNull(mapper); - return flatMap(w -> Opt.of(w).map(mapper).map(FastStream::of).orElseGet(FastStream::empty)); + return flatMap(w -> of(mapper.apply(w))); } /** @@ -668,7 +625,7 @@ public class FastStream implements Stream, Iterable { stream.forEach(e -> action.accept(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream.forEach(e -> action.accept(e, index.increment().get())); + stream.forEach(e -> action.accept(e, index.incrementAndGet())); } } @@ -695,7 +652,7 @@ public class FastStream implements Stream, Iterable { stream.forEachOrdered(e -> action.accept(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream.forEachOrdered(e -> action.accept(e, index.increment().get())); + stream.forEachOrdered(e -> action.accept(e, index.incrementAndGet())); } } @@ -918,8 +875,8 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的第一个元素 */ - public T findFirst(Predicate predicate) { - return stream.filter(predicate).findFirst().orElse(null); + public Optional findFirst(Predicate predicate) { + return stream.filter(predicate).findFirst(); } /** @@ -928,7 +885,7 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1 */ - public Integer findFirstIdx(Predicate predicate) { + public int findFirstIdx(Predicate predicate) { Objects.requireNonNull(predicate); if (isParallel()) { return NOT_FOUND_INDEX; @@ -949,13 +906,9 @@ public class FastStream implements Stream, Iterable { * @return 最后一个元素 */ public Optional findLast() { - if (isParallel()) { - return Optional.of(toList()).filter(CollUtil::isNotEmpty).map(l -> l.get(l.size() - 1)); - } else { - MutableObj last = new MutableObj<>(null); - forEach(last::set); - return Optional.ofNullable(last.get()); - } + MutableObj last = new MutableObj<>(null); + spliterator().forEachRemaining(last); + return Optional.ofNullable(last.get()); } /** @@ -964,21 +917,15 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的最后一个元素 */ - public T findLast(Predicate predicate) { + public Optional findLast(Predicate predicate) { Objects.requireNonNull(predicate); - if (isParallel()) { - List list = toList(); - final int index = ListUtil.lastIndexOf(list, predicate); - return index == NOT_FOUND_INDEX ? null : list.get(index); - } else { - MutableObj last = new MutableObj<>(null); - forEach(e -> { - if (predicate.test(e)) { - last.set(e); - } - }); - return last.get(); - } + MutableObj last = new MutableObj<>(null); + spliterator().forEachRemaining(e -> { + if (predicate.test(e)) { + last.set(e); + } + }); + return Optional.ofNullable(last.get()); } /** @@ -987,7 +934,7 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的最后一个元素的下标,如果不存在则返回-1 */ - public Integer findLastIdx(Predicate predicate) { + public int findLastIdx(Predicate predicate) { Objects.requireNonNull(predicate); if (isParallel()) { return NOT_FOUND_INDEX; @@ -1008,9 +955,10 @@ public class FastStream implements Stream, Iterable { * @return 反转元素顺序 */ public FastStream reverse() { - List list = toList(); - Collections.reverse(list); - return of(list, isParallel()); + //noinspection unchecked + final T[] array = (T[]) toArray(); + ArrayUtil.reverse(array); + return of(array).parallel(isParallel()).onClose(stream::close); } /** @@ -1067,6 +1015,7 @@ public class FastStream implements Stream, Iterable { /** * 返回一个无序流(无手动排序) + *

标记一个流是不在意元素顺序的, 在并行流的某些情况下可以提高性能

* * @return 无序流 */ @@ -1136,11 +1085,11 @@ public class FastStream implements Stream, Iterable { * @param idx 下标 * @return 指定下标的元素 */ - public T at(Integer idx) { - if (Objects.isNull(idx)) { - return null; - } - return CollUtil.get(toList(), idx); + public Optional at(Integer idx) { + return Opt.ofNullable(idx).map(i -> { + //noinspection unchecked + return (T) ArrayUtil.get(toArray(), i); + }).toOptional(); } /** @@ -1227,28 +1176,29 @@ public class FastStream implements Stream, Iterable { } /** - * 与给定的可迭代对象转换成map,key为现有元素,value为给定可迭代对象迭代的元素
- * 至少包含全部的key,如果对应位置上的value不存在,则为null + * 与给定的可迭代对象转换成Map,key为现有元素,value为给定可迭代对象迭代的元素
+ * Map的大小与两个集合中较小的数量一致, 即, 只合并下标位置相同的部分 * * @param other 可迭代对象 * @param 可迭代对象迭代的元素类型 - * @return map,key为现有元素,value为给定可迭代对象迭代的元素;
- * 至少包含全部的key,如果对应位置上的value不存在,则为null;
- * 如果key重复, 则保留最后一个关联的value;
+ * @return map,key为现有元素,value为给定可迭代对象迭代的元素 */ public Map toZip(Iterable other) { - // value对象迭代器 - final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); - if (isParallel()) { - List keyList = toList(); - final Map map = MapUtil.newHashMap(keyList.size()); - for (T key : keyList) { - map.put(key, iterator.hasNext() ? iterator.next() : null); - } - return map; - } else { - return toMap(Function.identity(), e -> iterator.hasNext() ? iterator.next() : null); + final Spliterator keys = spliterator(); + final Spliterator values = Opt.ofNullable(other).map(Iterable::spliterator).orElseGet(Spliterators::emptySpliterator); + // 获取两个Spliterator的中较小的数量 + // 如果Spliterator经过流操作, getExactSizeIfKnown()可能会返回-1, 所以默认大小为 MapUtil.DEFAULT_INITIAL_CAPACITY + final int sizeIfKnown = (int) Math.max(Math.min(keys.getExactSizeIfKnown(), values.getExactSizeIfKnown()), MapUtil.DEFAULT_INITIAL_CAPACITY); + final Map map = MapUtil.newHashMap(sizeIfKnown); + // 保存第一个Spliterator的值 + MutableObj key = new MutableObj<>(); + // 保存第二个Spliterator的值 + MutableObj value = new MutableObj<>(); + // 当两个Spliterator中都还有剩余元素时 + while (keys.tryAdvance(key) && values.tryAdvance(value)) { + map.put(key.get(), value.get()); } + return map; } /** @@ -1391,8 +1341,7 @@ public class FastStream implements Stream, Iterable { /** * 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流
- * 新流的数量等于旧流元素的数量
- * 使用 zipper 转换时, 如果对应位置上已经没有other元素,则other元素为null
+ * 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分
* * @param other 给定的迭代器 * @param zipper 两个元素的合并器 @@ -1403,15 +1352,21 @@ public class FastStream implements Stream, Iterable { public FastStream zip(Iterable other, BiFunction zipper) { Objects.requireNonNull(zipper); - // 给定对象迭代器 - final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); - Stream resStream = this.stream; - if (isParallel()) { - resStream = toList().stream(); + final Spliterator keys = spliterator(); + final Spliterator values = Opt.ofNullable(other).map(Iterable::spliterator).orElseGet(Spliterators::emptySpliterator); + // 获取两个Spliterator的中较小的数量 + // 如果Spliterator经过流操作, getExactSizeIfKnown()可能会返回-1, 所以默认大小为 ArrayList.DEFAULT_CAPACITY + final int sizeIfKnown = (int) Math.max(Math.min(keys.getExactSizeIfKnown(), values.getExactSizeIfKnown()), 10); + final List list = new ArrayList<>(sizeIfKnown); + // 保存第一个Spliterator的值 + MutableObj key = new MutableObj<>(); + // 保存第二个Spliterator的值 + MutableObj value = new MutableObj<>(); + // 当两个Spliterator中都还有剩余元素时 + while (keys.tryAdvance(key) && values.tryAdvance(value)) { + list.add(zipper.apply(key.get(), value.get())); } - final FastStream newStream = of(resStream.map(e -> zipper.apply(e, iterator.hasNext() ? iterator.next() : null))); - newStream.parallel(isParallel()); - return newStream; + return of(list).parallel(isParallel()).onClose(stream::close); } /** @@ -1422,40 +1377,11 @@ public class FastStream implements Stream, Iterable { * @param items 放入值 * @return 操作后的流 */ - @SuppressWarnings("unchecked") - public FastStream splice(int start, int deleteCount, T... items) { - List list = toList(); - final int size = list.size(); - // 从后往前查找 - if (start < 0) { - start += size; - } else if (start >= size) { - // 直接在尾部追加,不删除 - start = size; - deleteCount = 0; - } - // 起始位置 加上 删除的数量 超过 数据长度,需要重新计算需要删除的数量 - if (start + deleteCount > size) { - deleteCount = size - start; - } - - // 新列表的长度 - final int newSize = size - deleteCount + items.length; - List resList = list; - // 新列表的长度 大于 旧列表,创建新列表 - if (newSize > size) { - resList = new ArrayList<>(newSize); - resList.addAll(list); - } - // 需要删除的部分 - if (deleteCount > 0) { - resList.subList(start, start + deleteCount).clear(); - } - // 新增的部分 - if (items.length > 0) { - resList.addAll(start, Arrays.asList(items)); - } - return FastStream.of(resList); + @SafeVarargs + public final FastStream splice(int start, int deleteCount, T... items) { + return of(ListUtil.splice(toList(), start, deleteCount, items)) + .parallel(isParallel()) + .onClose(stream::close); } /** @@ -1475,9 +1401,10 @@ public class FastStream implements Stream, Iterable { // 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]] return FastStream.>of(of(list, isParallel())); } - return FastStream.iterate(0, i -> i < size, i -> i + batchSize) + return iterate(0, i -> i < size, i -> i + batchSize) .map(skip -> of(list.subList(skip, Math.min(size, skip + batchSize)), isParallel())) - .parallel(isParallel()); + .parallel(isParallel()) + .onClose(stream::close); } /** @@ -1493,6 +1420,78 @@ public class FastStream implements Stream, Iterable { return split(batchSize).map(FastStream::toList); } + /** + * 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 + *

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

+ *
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
+	 * FastStream.iterate(1, i -> i + 1)
+	 *	.parallel()
+	 *	// 顺序执行
+	 * 	.takeWhile(e -> e < 50)
+	 * 	// 并发
+	 * 	.map(e -> e + 1)
+	 * 	// 并发
+	 * 	.map(String::valueOf)
+	 * 	.toList();
+	 * }
+ *

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

+ * + * @param predicate 断言 + * @return 与指定断言匹配的元素组成的流 + */ + public FastStream takeWhile(Predicate predicate) { + Objects.requireNonNull(predicate); + return of(StreamHelper.takeWhile(stream, predicate)); + } + + /** + * 保留 与指定断言 匹配的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 + *

takeWhile 的别名方法

+ * + * @param predicate 断言 + * @return 与指定断言匹配的元素组成的流 + * @see #takeWhile(Predicate) + */ + public FastStream limit(Predicate predicate) { + return takeWhile(predicate); + } + + /** + * 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 + *

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

+ *
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
+	 * FastStream.iterate(1, i <= 100, i -> i + 1)
+	 *	.parallel()
+	 *	// 顺序执行
+	 * 	.dropWhile(e -> e < 50)
+	 * 	// 并发
+	 * 	.map(e -> e + 1)
+	 * 	// 并发
+	 * 	.map(String::valueOf)
+	 * 	.toList();
+	 * }
+ *

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

+ * + * @param predicate 断言 + * @return 剩余元素组成的流 + */ + public FastStream dropWhile(Predicate predicate) { + Objects.requireNonNull(predicate); + return of(StreamHelper.dropWhile(stream, predicate)); + } + + /** + * 跳过 与断言匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 + *

dropWhile 的别名方法

+ * + * @param predicate 断言 + * @return 剩余元素组成的流 + * @see #dropWhile(Predicate) + */ + public FastStream skip(Predicate predicate) { + return dropWhile(predicate); + } + public interface FastStreamBuilder extends Consumer, cn.hutool.core.builder.Builder> { /** 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/support/DropWhileSpliterator.java new file mode 100644 index 000000000..0ab4433a9 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java @@ -0,0 +1,72 @@ +package cn.hutool.core.stream.support; + +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 + */ +class DropWhileSpliterator implements Spliterator { + + static DropWhileSpliterator create(Spliterator source, Predicate predicate) { + return new DropWhileSpliterator<>(source, predicate); + } + + private final Spliterator source; + private final Predicate predicate; + private boolean isFound = false; + + private DropWhileSpliterator(Spliterator source, Predicate predicate) { + this.source = source; + this.predicate = predicate; + } + + @Override + public boolean tryAdvance(Consumer 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 getComparator() { + return source.getComparator(); + } +} 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 new file mode 100644 index 000000000..71df09b7b --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java @@ -0,0 +1,76 @@ +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 new file mode 100644 index 000000000..478cbb091 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java @@ -0,0 +1,99 @@ +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/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java new file mode 100644 index 000000000..dc0ca8da6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java @@ -0,0 +1,68 @@ +package cn.hutool.core.stream.support; + +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 + */ +class TakeWhileSpliterator implements Spliterator { + + static TakeWhileSpliterator create(Spliterator source, Predicate predicate) { + return new TakeWhileSpliterator<>(source, predicate); + } + + private final Spliterator source; + private final Predicate predicate; + private boolean isContinue = true; + + TakeWhileSpliterator(Spliterator source, Predicate predicate) { + this.source = source; + this.predicate = predicate; + } + + @Override + public boolean tryAdvance(Consumer 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 getComparator() { + return source.getComparator(); + } +} + 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 9091ec6a0..937b7378d 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 @@ -76,7 +76,7 @@ public class FastStreamTest { Map toZip = FastStream.of(orders).toZip(list); Assert.assertEquals(map, toZip); - Map toZipParallel = FastStream.of(orders).parallel().toZip(list); + Map toZipParallel = FastStream.of(orders).parallel().nonNull().toZip(list); Assert.assertEquals(map, toZipParallel); } @@ -241,10 +241,12 @@ public class FastStreamTest { @Test public void testAt() { List list = Arrays.asList(1, 2, 3); - Assert.assertEquals(1, (Object) FastStream.of(list).at(0)); - Assert.assertEquals(1, (Object) FastStream.of(list).at(-3)); - Assert.assertEquals(3, (Object) FastStream.of(list).at(-1)); - Assert.assertNull(FastStream.of(list).at(-4)); + Assert.assertEquals(1, (Object) FastStream.of(list).at(0).orElse(null)); + Assert.assertEquals(2, (Object) FastStream.of(list).at(1).orElse(null)); + Assert.assertEquals(3, (Object) FastStream.of(list).at(2).orElse(null)); + Assert.assertEquals(1, (Object) FastStream.of(list).at(-3).orElse(null)); + Assert.assertEquals(3, (Object) FastStream.of(list).at(-1).orElse(null)); + Assert.assertNull(FastStream.of(list).at(-4).orElse(null)); } @Test @@ -262,34 +264,46 @@ public class FastStreamTest { @Test public void testFindFirst() { List list = Arrays.asList(1, 2, 3); - Integer find = FastStream.of(list).findFirst(Objects::nonNull); + Integer find = FastStream.of(list).findFirst(Objects::nonNull).orElse(null); Assert.assertEquals(1, (Object) find); } @Test public void testFindFirstIdx() { List list = Arrays.asList(null, 2, 3); - Integer idx = FastStream.of(list).findFirstIdx(Objects::nonNull); - Assert.assertEquals(1, (Object) idx); + Assert.assertEquals(1, FastStream.of(list).findFirstIdx(Objects::nonNull)); Assert.assertEquals(-1, (Object) FastStream.of(list).parallel().findFirstIdx(Objects::nonNull)); } @Test public void testFindLast() { - List list = ListUtil.of(1, null, 3); - Integer find = FastStream.of(list).parallel().findLast(Objects::nonNull); - Assert.assertEquals(3, (Object) find); + List list = ListUtil.of(1, 2, 4, 5, 6, 7, 8, 9, 10, 3); + Assert.assertEquals(3, (Object) FastStream.of(list).findLast().orElse(null)); Assert.assertEquals(3, (Object) FastStream.of(list).parallel().findLast().orElse(null)); + + List list2 = ListUtil.of(1, 2, 4, 5, 6, 7, 8, 9, 10, 3, null); + Assert.assertEquals(3, (Object) FastStream.of(list2).parallel().findLast(Objects::nonNull).orElse(null)); + + Assert.assertNull(FastStream.of().parallel().findLast(Objects::nonNull).orElse(null)); + Assert.assertNull(FastStream.of((Object) null).parallel().findLast(Objects::nonNull).orElse(null)); } @Test public void testFindLastIdx() { List list = Arrays.asList(1, null, 3); - Integer idx = FastStream.of(list).findLastIdx(Objects::nonNull); - Assert.assertEquals(2, (Object) idx); + Assert.assertEquals(2, (Object) FastStream.of(list).findLastIdx(Objects::nonNull)); Assert.assertEquals(-1, (Object) FastStream.of(list).parallel().findLastIdx(Objects::nonNull)); } + @Test + public void testReverse() { + final List list = ListUtil.of(Stream.iterate(1, i -> i + 1).limit(1000).collect(Collectors.toList())); + + Assert.assertEquals(ListUtil.reverseNew(list), FastStream.of(list).reverse().toList()); + Assert.assertEquals(ListUtil.empty(), FastStream.of().reverse().toList()); + Assert.assertEquals(ListUtil.of((Object) null), FastStream.of((Object) null).reverse().toList()); + } + @Test public void testZip() { List orders = Arrays.asList(1, 2, 3); @@ -319,4 +333,59 @@ public class FastStreamTest { lists = FastStream.of(list).splitList(list.size()).toList(); Assert.assertEquals(singletonList(list), lists); } + + @Test + public void testTakeWhile() { + // 1 到 10 + final List list = FastStream.iterate(1, i -> i <= 10, i -> i + 1).toList(); + + final List res1 = FastStream.of(list) + // 舍弃 5 + .takeWhile(e -> e < 5) + // 过滤奇数 + .filter(e -> (e & 1) == 0) + // 反序 + .sorted(Comparator.reverseOrder()) + .map(String::valueOf) + .toList(); + Assert.assertEquals(Arrays.asList("4", "2"), res1); + + final List res2 = FastStream.iterate(1, i -> i + 1) + .parallel() + .takeWhile(e -> e < 5) + .map(String::valueOf) + .map(Integer::valueOf) + .sorted(Comparator.naturalOrder()) + .toList(); + Assert.assertEquals(Arrays.asList(1, 2, 3, 4), res2); + } + + @Test + public void testDropWhile() { + // 1 到 10 + final List list = FastStream.iterate(1, i -> i <= 10, i -> i + 1).toList(); + + final List res1 = FastStream.of(list) + // 舍弃 5之前的数字 + .dropWhile(e -> e < 5) + // 过滤偶数 + .filter(e -> (e & 1) == 1) + // 反序 + .sorted(Comparator.reverseOrder()) + .map(String::valueOf) + .toList(); + Assert.assertEquals(Arrays.asList("9", "7", "5"), res1); + + final List res2 = FastStream.of(list) + .parallel() + .dropWhile(e -> e < 5) + // 过滤偶数 + .filter(e -> (e & 1) == 1) + .map(String::valueOf) + .map(Integer::valueOf) + .sorted(Comparator.naturalOrder()) + .toList(); + Assert.assertEquals(Arrays.asList(5, 7, 9), res2); + } + } From 3608f9dbe025103b795b5c6b2532688f154eef20 Mon Sep 17 00:00:00 2001 From: Zjp <1215582715@qq.com> Date: Fri, 5 Aug 2022 17:06:21 +0800 Subject: [PATCH 2/7] =?UTF-8?q?fixed=20fc8923b=20from=20https://gitee.com/?= =?UTF-8?q?VampireAchao/hutool/pulls/742=20=E4=BC=98=E5=8C=96FastStream:?= =?UTF-8?q?=201.=E6=96=B0=E5=A2=9EtakeWhile=E3=80=81dropWhile=E6=96=B9?= =?UTF-8?q?=E6=B3=95;=202.=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86=E5=86=99?= =?UTF-8?q?=E6=B3=95,=20=E5=B8=8C=E6=9C=9B=E8=BF=9B=E4=B8=80=E6=AD=A5?= =?UTF-8?q?=E6=8F=90=E9=AB=98=E6=95=88=E7=8E=87;=203.=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=E4=BB=A3=E7=A0=81,=20=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81=E5=88=B0=E5=85=B6?= =?UTF-8?q?=E4=BB=96=E7=B1=BB=E4=B8=AD;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/collection/ListUtil.java | 48 +++ .../main/java/cn/hutool/core/lang/Opt.java | 13 + .../hutool/core/lang/mutable/MutableInt.java | 41 +++ .../hutool/core/lang/mutable/MutableObj.java | 14 +- .../cn/hutool/core/stream/FastStream.java | 297 +++++++++--------- .../stream/support/DropWhileSpliterator.java | 72 +++++ .../stream/support/IterateSpliterator.java | 76 +++++ .../core/stream/support/StreamHelper.java | 99 ++++++ .../stream/support/TakeWhileSpliterator.java | 68 ++++ .../cn/hutool/core/stream/FastStreamTest.java | 95 +++++- 10 files changed, 660 insertions(+), 163 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java index 28f446daa..e21f3aa92 100755 --- a/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/collection/ListUtil.java @@ -13,6 +13,7 @@ import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.PageUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -692,4 +693,51 @@ public class ListUtil { } return list; } + + /** + * 类似js的splice函数 + * + * @param start 起始下标, 可以为负数, -1代表最后一个元素 + * @param deleteCount 删除个数,必须是正整数 + * @param items 放入值 + * @return 操作后的流 + * @since 6.0.0 + */ + @SafeVarargs + public static List splice(List list, int start, int deleteCount, T... items) { + if (CollUtil.isEmpty(list)) { + return zero(); + } + final int size = list.size(); + // 从后往前查找 + if (start < 0) { + start += size; + } else if (start >= size) { + // 直接在尾部追加,不删除 + start = size; + deleteCount = 0; + } + // 起始位置 加上 删除的数量 超过 数据长度,需要重新计算需要删除的数量 + if (start + deleteCount > size) { + deleteCount = size - start; + } + + // 新列表的长度 + final int newSize = size - deleteCount + items.length; + List resList = list; + // 新列表的长度 大于 旧列表,创建新列表 + if (newSize > size) { + resList = new ArrayList<>(newSize); + resList.addAll(list); + } + // 需要删除的部分 + if (deleteCount > 0) { + resList.subList(start, start + deleteCount).clear(); + } + // 新增的部分 + if (ArrayUtil.isNotEmpty(items)) { + resList.addAll(start, Arrays.asList(items)); + } + return resList; + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java index e4730f1f3..66315b586 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Opt.java @@ -127,6 +127,19 @@ public class Opt { } } + /** + * 根据 {@link Optional} 构造 {@code Opt} + * + * @param optional optional + * @param 包裹的元素类型 + * @return 一个包裹里元素可能为空的 {@code Opt} + * @since 6.0.0 + */ + @SuppressWarnings("OptionalUsedAsFieldOrParameterType") + public static Opt of(Optional optional) { + return ofNullable(optional).flattedMap(Function.identity()); + } + /** * 包裹里实际的元素 */ diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java index 912a67afc..9cb300af5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableInt.java @@ -81,6 +81,47 @@ public class MutableInt extends Number implements Comparable, Mutabl return this; } + // ----------------------------------------------------------------------- + /** + * 先加1, 再获取值 + * + * @return +1后的值 + * @since 6.0.0 + */ + public int incrementAndGet() { + return ++value; + } + + /** + * 先获取原来的值, 再加1 + * + * @return 原始值 + * @since 6.0.0 + */ + public int getAndIncrement() { + return value++; + } + + /** + * 先减1, 再获取值 + * + * @return -1后的值 + * @since 6.0.0 + */ + public int decrementAndGet() { + return --value; + } + + /** + * 先获取原来的值, 再减1 + * + * @return 原始值 + * @since 6.0.0 + */ + public int getAndDecrement() { + return value--; + } + // ----------------------------------------------------------------------- /** * 增加值 diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java index 64f71dc87..c8a9397b2 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java @@ -1,6 +1,7 @@ package cn.hutool.core.lang.mutable; import java.io.Serializable; +import java.util.function.Consumer; /** * 可变{@code Object} @@ -8,7 +9,7 @@ import java.io.Serializable; * @param 可变的类型 * @since 3.0.1 */ -public class MutableObj implements Mutable, Serializable { +public class MutableObj implements Mutable, Serializable, Consumer { private static final long serialVersionUID = 1L; /** @@ -50,6 +51,17 @@ public class MutableObj implements Mutable, Serializable { this.value = value; } + /** + * 消费元素 + * + * @param t t + * @since 6.0.0 + */ + @Override + public void accept(T t) { + this.value = t; + } + // ----------------------------------------------------------------------- @Override public boolean equals(final Object obj) { 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 5c74135e0..350d6068c 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 @@ -1,12 +1,12 @@ package cn.hutool.core.stream; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Console; 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; @@ -61,6 +61,7 @@ public class FastStream implements Stream, Iterable { this.stream = stream; } + // region Static method // --------------------------------------------------------------- Static method start /** @@ -157,51 +158,7 @@ public class FastStream implements Stream, Iterable { public static FastStream iterate(T seed, Predicate hasNext, UnaryOperator next) { Objects.requireNonNull(next); Objects.requireNonNull(hasNext); - Spliterator spliterator = new Spliterators.AbstractSpliterator(Long.MAX_VALUE, - Spliterator.ORDERED | Spliterator.IMMUTABLE) { - T prev; - boolean started; - 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); - } - } - }; - return new FastStream<>(StreamSupport.stream(spliterator, false)); + return new FastStream<>(StreamHelper.iterate(seed, hasNext, next)); } /** @@ -279,6 +236,7 @@ public class FastStream implements Stream, Iterable { } // --------------------------------------------------------------- Static method end + // endregion /** * 过滤元素,返回与指定断言匹配的元素组成的流 @@ -303,10 +261,9 @@ public class FastStream implements Stream, Iterable { */ public FastStream filter(Function mapper, R value) { Objects.requireNonNull(mapper); - return filter(e -> Objects.equals(Opt.ofNullable(e).map(mapper).get(), value)); + return filter(e -> Objects.equals(mapper.apply(e), value)); } - /** * 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1 * 这是一个无状态中间操作 @@ -320,7 +277,7 @@ public class FastStream implements Stream, Iterable { return filter(e -> predicate.test(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return filter(e -> predicate.test(e, index.increment().get())); + return filter(e -> predicate.test(e, index.incrementAndGet())); } } @@ -360,7 +317,7 @@ public class FastStream implements Stream, Iterable { return map(e -> mapper.apply(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return map(e -> mapper.apply(e, index.increment().get())); + return map(e -> mapper.apply(e, index.incrementAndGet())); } } @@ -395,7 +352,7 @@ public class FastStream implements Stream, Iterable { return flatMap(e -> mapper.apply(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return flatMap(e -> mapper.apply(e, index.increment().get())); + return flatMap(e -> mapper.apply(e, index.incrementAndGet())); } } @@ -449,7 +406,7 @@ public class FastStream implements Stream, Iterable { */ public FastStream flatMapIter(Function> mapper) { Objects.requireNonNull(mapper); - return flatMap(w -> Opt.of(w).map(mapper).map(FastStream::of).orElseGet(FastStream::empty)); + return flatMap(w -> of(mapper.apply(w))); } /** @@ -668,7 +625,7 @@ public class FastStream implements Stream, Iterable { stream.forEach(e -> action.accept(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream.forEach(e -> action.accept(e, index.increment().get())); + stream.forEach(e -> action.accept(e, index.incrementAndGet())); } } @@ -695,7 +652,7 @@ public class FastStream implements Stream, Iterable { stream.forEachOrdered(e -> action.accept(e, NOT_FOUND_INDEX)); } else { MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream.forEachOrdered(e -> action.accept(e, index.increment().get())); + stream.forEachOrdered(e -> action.accept(e, index.incrementAndGet())); } } @@ -916,8 +873,8 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的第一个元素 */ - public T findFirst(Predicate predicate) { - return stream.filter(predicate).findFirst().orElse(null); + public Optional findFirst(Predicate predicate) { + return stream.filter(predicate).findFirst(); } /** @@ -926,7 +883,7 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1 */ - public Integer findFirstIdx(Predicate predicate) { + public int findFirstIdx(Predicate predicate) { Objects.requireNonNull(predicate); if (isParallel()) { return NOT_FOUND_INDEX; @@ -947,13 +904,9 @@ public class FastStream implements Stream, Iterable { * @return 最后一个元素 */ public Optional findLast() { - if (isParallel()) { - return Optional.of(toList()).filter(CollUtil::isNotEmpty).map(l -> l.get(l.size() - 1)); - } else { - MutableObj last = new MutableObj<>(null); - forEach(last::set); - return Optional.ofNullable(last.get()); - } + MutableObj last = new MutableObj<>(null); + spliterator().forEachRemaining(last); + return Optional.ofNullable(last.get()); } /** @@ -962,21 +915,15 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的最后一个元素 */ - public T findLast(Predicate predicate) { + public Optional findLast(Predicate predicate) { Objects.requireNonNull(predicate); - if (isParallel()) { - List list = toList(); - final int index = ListUtil.lastIndexOf(list, predicate); - return index == NOT_FOUND_INDEX ? null : list.get(index); - } else { - MutableObj last = new MutableObj<>(null); - forEach(e -> { - if (predicate.test(e)) { - last.set(e); - } - }); - return last.get(); - } + MutableObj last = new MutableObj<>(null); + spliterator().forEachRemaining(e -> { + if (predicate.test(e)) { + last.set(e); + } + }); + return Optional.ofNullable(last.get()); } /** @@ -985,7 +932,7 @@ public class FastStream implements Stream, Iterable { * @param predicate 断言 * @return 与给定断言匹配的最后一个元素的下标,如果不存在则返回-1 */ - public Integer findLastIdx(Predicate predicate) { + public int findLastIdx(Predicate predicate) { Objects.requireNonNull(predicate); if (isParallel()) { return NOT_FOUND_INDEX; @@ -1006,9 +953,10 @@ public class FastStream implements Stream, Iterable { * @return 反转元素顺序 */ public FastStream reverse() { - List list = toList(); - Collections.reverse(list); - return of(list, isParallel()); + //noinspection unchecked + final T[] array = (T[]) toArray(); + ArrayUtil.reverse(array); + return of(array).parallel(isParallel()).onClose(stream::close); } /** @@ -1065,6 +1013,7 @@ public class FastStream implements Stream, Iterable { /** * 返回一个无序流(无手动排序) + *

标记一个流是不在意元素顺序的, 在并行流的某些情况下可以提高性能

* * @return 无序流 */ @@ -1134,11 +1083,11 @@ public class FastStream implements Stream, Iterable { * @param idx 下标 * @return 指定下标的元素 */ - public T at(Integer idx) { - if (Objects.isNull(idx)) { - return null; - } - return CollUtil.get(toList(), idx); + public Optional at(Integer idx) { + return Opt.ofNullable(idx).map(i -> { + //noinspection unchecked + return (T) ArrayUtil.get(toArray(), i); + }).toOptional(); } /** @@ -1225,28 +1174,29 @@ public class FastStream implements Stream, Iterable { } /** - * 与给定的可迭代对象转换成map,key为现有元素,value为给定可迭代对象迭代的元素
- * 至少包含全部的key,如果对应位置上的value不存在,则为null + * 与给定的可迭代对象转换成Map,key为现有元素,value为给定可迭代对象迭代的元素
+ * Map的大小与两个集合中较小的数量一致, 即, 只合并下标位置相同的部分 * * @param other 可迭代对象 * @param 可迭代对象迭代的元素类型 - * @return map,key为现有元素,value为给定可迭代对象迭代的元素;
- * 至少包含全部的key,如果对应位置上的value不存在,则为null;
- * 如果key重复, 则保留最后一个关联的value;
+ * @return map,key为现有元素,value为给定可迭代对象迭代的元素 */ public Map toZip(Iterable other) { - // value对象迭代器 - final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); - if (isParallel()) { - List keyList = toList(); - final Map map = MapUtil.newHashMap(keyList.size()); - for (T key : keyList) { - map.put(key, iterator.hasNext() ? iterator.next() : null); - } - return map; - } else { - return toMap(Function.identity(), e -> iterator.hasNext() ? iterator.next() : null); + final Spliterator keys = spliterator(); + final Spliterator values = Opt.ofNullable(other).map(Iterable::spliterator).orElseGet(Spliterators::emptySpliterator); + // 获取两个Spliterator的中较小的数量 + // 如果Spliterator经过流操作, getExactSizeIfKnown()可能会返回-1, 所以默认大小为 MapUtil.DEFAULT_INITIAL_CAPACITY + final int sizeIfKnown = (int) Math.max(Math.min(keys.getExactSizeIfKnown(), values.getExactSizeIfKnown()), MapUtil.DEFAULT_INITIAL_CAPACITY); + final Map map = MapUtil.newHashMap(sizeIfKnown); + // 保存第一个Spliterator的值 + MutableObj key = new MutableObj<>(); + // 保存第二个Spliterator的值 + MutableObj value = new MutableObj<>(); + // 当两个Spliterator中都还有剩余元素时 + while (keys.tryAdvance(key) && values.tryAdvance(value)) { + map.put(key.get(), value.get()); } + return map; } /** @@ -1389,8 +1339,7 @@ public class FastStream implements Stream, Iterable { /** * 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流
- * 新流的数量等于旧流元素的数量
- * 使用 zipper 转换时, 如果对应位置上已经没有other元素,则other元素为null
+ * 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分
* * @param other 给定的迭代器 * @param zipper 两个元素的合并器 @@ -1401,15 +1350,21 @@ public class FastStream implements Stream, Iterable { public FastStream zip(Iterable other, BiFunction zipper) { Objects.requireNonNull(zipper); - // 给定对象迭代器 - final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); - Stream resStream = this.stream; - if (isParallel()) { - resStream = toList().stream(); + final Spliterator keys = spliterator(); + final Spliterator values = Opt.ofNullable(other).map(Iterable::spliterator).orElseGet(Spliterators::emptySpliterator); + // 获取两个Spliterator的中较小的数量 + // 如果Spliterator经过流操作, getExactSizeIfKnown()可能会返回-1, 所以默认大小为 ArrayList.DEFAULT_CAPACITY + final int sizeIfKnown = (int) Math.max(Math.min(keys.getExactSizeIfKnown(), values.getExactSizeIfKnown()), 10); + final List list = new ArrayList<>(sizeIfKnown); + // 保存第一个Spliterator的值 + MutableObj key = new MutableObj<>(); + // 保存第二个Spliterator的值 + MutableObj value = new MutableObj<>(); + // 当两个Spliterator中都还有剩余元素时 + while (keys.tryAdvance(key) && values.tryAdvance(value)) { + list.add(zipper.apply(key.get(), value.get())); } - final FastStream newStream = of(resStream.map(e -> zipper.apply(e, iterator.hasNext() ? iterator.next() : null))); - newStream.parallel(isParallel()); - return newStream; + return of(list).parallel(isParallel()).onClose(stream::close); } /** @@ -1420,40 +1375,11 @@ public class FastStream implements Stream, Iterable { * @param items 放入值 * @return 操作后的流 */ - @SuppressWarnings("unchecked") - public FastStream splice(int start, int deleteCount, T... items) { - List list = toList(); - final int size = list.size(); - // 从后往前查找 - if (start < 0) { - start += size; - } else if (start >= size) { - // 直接在尾部追加,不删除 - start = size; - deleteCount = 0; - } - // 起始位置 加上 删除的数量 超过 数据长度,需要重新计算需要删除的数量 - if (start + deleteCount > size) { - deleteCount = size - start; - } - - // 新列表的长度 - final int newSize = size - deleteCount + items.length; - List resList = list; - // 新列表的长度 大于 旧列表,创建新列表 - if (newSize > size) { - resList = new ArrayList<>(newSize); - resList.addAll(list); - } - // 需要删除的部分 - if (deleteCount > 0) { - resList.subList(start, start + deleteCount).clear(); - } - // 新增的部分 - if (items.length > 0) { - resList.addAll(start, Arrays.asList(items)); - } - return FastStream.of(resList); + @SafeVarargs + public final FastStream splice(int start, int deleteCount, T... items) { + return of(ListUtil.splice(toList(), start, deleteCount, items)) + .parallel(isParallel()) + .onClose(stream::close); } /** @@ -1473,9 +1399,10 @@ public class FastStream implements Stream, Iterable { // 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]] return FastStream.>of(of(list, isParallel())); } - return FastStream.iterate(0, i -> i < size, i -> i + batchSize) + return iterate(0, i -> i < size, i -> i + batchSize) .map(skip -> of(list.subList(skip, Math.min(size, skip + batchSize)), isParallel())) - .parallel(isParallel()); + .parallel(isParallel()) + .onClose(stream::close); } /** @@ -1491,6 +1418,78 @@ public class FastStream implements Stream, Iterable { return split(batchSize).map(FastStream::toList); } + /** + * 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 + *

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

+ *
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
+	 * FastStream.iterate(1, i -> i + 1)
+	 *	.parallel()
+	 *	// 顺序执行
+	 * 	.takeWhile(e -> e < 50)
+	 * 	// 并发
+	 * 	.map(e -> e + 1)
+	 * 	// 并发
+	 * 	.map(String::valueOf)
+	 * 	.toList();
+	 * }
+ *

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

+ * + * @param predicate 断言 + * @return 与指定断言匹配的元素组成的流 + */ + public FastStream takeWhile(Predicate predicate) { + Objects.requireNonNull(predicate); + return of(StreamHelper.takeWhile(stream, predicate)); + } + + /** + * 保留 与指定断言 匹配的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 + *

takeWhile 的别名方法

+ * + * @param predicate 断言 + * @return 与指定断言匹配的元素组成的流 + * @see #takeWhile(Predicate) + */ + public FastStream limit(Predicate predicate) { + return takeWhile(predicate); + } + + /** + * 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 + *

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

+ *
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
+	 * FastStream.iterate(1, i <= 100, i -> i + 1)
+	 *	.parallel()
+	 *	// 顺序执行
+	 * 	.dropWhile(e -> e < 50)
+	 * 	// 并发
+	 * 	.map(e -> e + 1)
+	 * 	// 并发
+	 * 	.map(String::valueOf)
+	 * 	.toList();
+	 * }
+ *

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

+ * + * @param predicate 断言 + * @return 剩余元素组成的流 + */ + public FastStream dropWhile(Predicate predicate) { + Objects.requireNonNull(predicate); + return of(StreamHelper.dropWhile(stream, predicate)); + } + + /** + * 跳过 与断言匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 + *

dropWhile 的别名方法

+ * + * @param predicate 断言 + * @return 剩余元素组成的流 + * @see #dropWhile(Predicate) + */ + public FastStream skip(Predicate predicate) { + return dropWhile(predicate); + } + public interface FastStreamBuilder extends Consumer, cn.hutool.core.builder.Builder> { /** 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/support/DropWhileSpliterator.java new file mode 100644 index 000000000..0ab4433a9 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java @@ -0,0 +1,72 @@ +package cn.hutool.core.stream.support; + +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 + */ +class DropWhileSpliterator implements Spliterator { + + static DropWhileSpliterator create(Spliterator source, Predicate predicate) { + return new DropWhileSpliterator<>(source, predicate); + } + + private final Spliterator source; + private final Predicate predicate; + private boolean isFound = false; + + private DropWhileSpliterator(Spliterator source, Predicate predicate) { + this.source = source; + this.predicate = predicate; + } + + @Override + public boolean tryAdvance(Consumer 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 getComparator() { + return source.getComparator(); + } +} 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 new file mode 100644 index 000000000..71df09b7b --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/IterateSpliterator.java @@ -0,0 +1,76 @@ +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 new file mode 100644 index 000000000..478cbb091 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/StreamHelper.java @@ -0,0 +1,99 @@ +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/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java new file mode 100644 index 000000000..dc0ca8da6 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java @@ -0,0 +1,68 @@ +package cn.hutool.core.stream.support; + +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 + */ +class TakeWhileSpliterator implements Spliterator { + + static TakeWhileSpliterator create(Spliterator source, Predicate predicate) { + return new TakeWhileSpliterator<>(source, predicate); + } + + private final Spliterator source; + private final Predicate predicate; + private boolean isContinue = true; + + TakeWhileSpliterator(Spliterator source, Predicate predicate) { + this.source = source; + this.predicate = predicate; + } + + @Override + public boolean tryAdvance(Consumer 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 getComparator() { + return source.getComparator(); + } +} + 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 9091ec6a0..937b7378d 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 @@ -76,7 +76,7 @@ public class FastStreamTest { Map toZip = FastStream.of(orders).toZip(list); Assert.assertEquals(map, toZip); - Map toZipParallel = FastStream.of(orders).parallel().toZip(list); + Map toZipParallel = FastStream.of(orders).parallel().nonNull().toZip(list); Assert.assertEquals(map, toZipParallel); } @@ -241,10 +241,12 @@ public class FastStreamTest { @Test public void testAt() { List list = Arrays.asList(1, 2, 3); - Assert.assertEquals(1, (Object) FastStream.of(list).at(0)); - Assert.assertEquals(1, (Object) FastStream.of(list).at(-3)); - Assert.assertEquals(3, (Object) FastStream.of(list).at(-1)); - Assert.assertNull(FastStream.of(list).at(-4)); + Assert.assertEquals(1, (Object) FastStream.of(list).at(0).orElse(null)); + Assert.assertEquals(2, (Object) FastStream.of(list).at(1).orElse(null)); + Assert.assertEquals(3, (Object) FastStream.of(list).at(2).orElse(null)); + Assert.assertEquals(1, (Object) FastStream.of(list).at(-3).orElse(null)); + Assert.assertEquals(3, (Object) FastStream.of(list).at(-1).orElse(null)); + Assert.assertNull(FastStream.of(list).at(-4).orElse(null)); } @Test @@ -262,34 +264,46 @@ public class FastStreamTest { @Test public void testFindFirst() { List list = Arrays.asList(1, 2, 3); - Integer find = FastStream.of(list).findFirst(Objects::nonNull); + Integer find = FastStream.of(list).findFirst(Objects::nonNull).orElse(null); Assert.assertEquals(1, (Object) find); } @Test public void testFindFirstIdx() { List list = Arrays.asList(null, 2, 3); - Integer idx = FastStream.of(list).findFirstIdx(Objects::nonNull); - Assert.assertEquals(1, (Object) idx); + Assert.assertEquals(1, FastStream.of(list).findFirstIdx(Objects::nonNull)); Assert.assertEquals(-1, (Object) FastStream.of(list).parallel().findFirstIdx(Objects::nonNull)); } @Test public void testFindLast() { - List list = ListUtil.of(1, null, 3); - Integer find = FastStream.of(list).parallel().findLast(Objects::nonNull); - Assert.assertEquals(3, (Object) find); + List list = ListUtil.of(1, 2, 4, 5, 6, 7, 8, 9, 10, 3); + Assert.assertEquals(3, (Object) FastStream.of(list).findLast().orElse(null)); Assert.assertEquals(3, (Object) FastStream.of(list).parallel().findLast().orElse(null)); + + List list2 = ListUtil.of(1, 2, 4, 5, 6, 7, 8, 9, 10, 3, null); + Assert.assertEquals(3, (Object) FastStream.of(list2).parallel().findLast(Objects::nonNull).orElse(null)); + + Assert.assertNull(FastStream.of().parallel().findLast(Objects::nonNull).orElse(null)); + Assert.assertNull(FastStream.of((Object) null).parallel().findLast(Objects::nonNull).orElse(null)); } @Test public void testFindLastIdx() { List list = Arrays.asList(1, null, 3); - Integer idx = FastStream.of(list).findLastIdx(Objects::nonNull); - Assert.assertEquals(2, (Object) idx); + Assert.assertEquals(2, (Object) FastStream.of(list).findLastIdx(Objects::nonNull)); Assert.assertEquals(-1, (Object) FastStream.of(list).parallel().findLastIdx(Objects::nonNull)); } + @Test + public void testReverse() { + final List list = ListUtil.of(Stream.iterate(1, i -> i + 1).limit(1000).collect(Collectors.toList())); + + Assert.assertEquals(ListUtil.reverseNew(list), FastStream.of(list).reverse().toList()); + Assert.assertEquals(ListUtil.empty(), FastStream.of().reverse().toList()); + Assert.assertEquals(ListUtil.of((Object) null), FastStream.of((Object) null).reverse().toList()); + } + @Test public void testZip() { List orders = Arrays.asList(1, 2, 3); @@ -319,4 +333,59 @@ public class FastStreamTest { lists = FastStream.of(list).splitList(list.size()).toList(); Assert.assertEquals(singletonList(list), lists); } + + @Test + public void testTakeWhile() { + // 1 到 10 + final List list = FastStream.iterate(1, i -> i <= 10, i -> i + 1).toList(); + + final List res1 = FastStream.of(list) + // 舍弃 5 + .takeWhile(e -> e < 5) + // 过滤奇数 + .filter(e -> (e & 1) == 0) + // 反序 + .sorted(Comparator.reverseOrder()) + .map(String::valueOf) + .toList(); + Assert.assertEquals(Arrays.asList("4", "2"), res1); + + final List res2 = FastStream.iterate(1, i -> i + 1) + .parallel() + .takeWhile(e -> e < 5) + .map(String::valueOf) + .map(Integer::valueOf) + .sorted(Comparator.naturalOrder()) + .toList(); + Assert.assertEquals(Arrays.asList(1, 2, 3, 4), res2); + } + + @Test + public void testDropWhile() { + // 1 到 10 + final List list = FastStream.iterate(1, i -> i <= 10, i -> i + 1).toList(); + + final List res1 = FastStream.of(list) + // 舍弃 5之前的数字 + .dropWhile(e -> e < 5) + // 过滤偶数 + .filter(e -> (e & 1) == 1) + // 反序 + .sorted(Comparator.reverseOrder()) + .map(String::valueOf) + .toList(); + Assert.assertEquals(Arrays.asList("9", "7", "5"), res1); + + final List res2 = FastStream.of(list) + .parallel() + .dropWhile(e -> e < 5) + // 过滤偶数 + .filter(e -> (e & 1) == 1) + .map(String::valueOf) + .map(Integer::valueOf) + .sorted(Comparator.naturalOrder()) + .toList(); + Assert.assertEquals(Arrays.asList(5, 7, 9), res2); + } + } From db0e590fb199c355fd963bbb2e5bdbdd54ea21cd Mon Sep 17 00:00:00 2001 From: achao Date: Sat, 6 Aug 2022 21:28:05 +0800 Subject: [PATCH 3/7] =?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()); + } + } From f17b0a41667d8b1a5785245603dbc66c79d63639 Mon Sep 17 00:00:00 2001 From: emptypoint Date: Sat, 6 Aug 2022 23:36:25 +0800 Subject: [PATCH 4/7] =?UTF-8?q?1.=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=96=87=E4=BB=B6;=202.=E8=B0=83=E6=95=B4=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/stream/FastStream.java | 7 +- .../stream/support/DropWhileSpliterator.java | 72 -------------- .../stream/support/IterateSpliterator.java | 76 -------------- .../core/stream/support/StreamHelper.java | 99 ------------------- .../stream/support/TakeWhileSpliterator.java | 68 ------------- 5 files changed, 4 insertions(+), 318 deletions(-) delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java 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 delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.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 13fdd7182..ca83b2488 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; @@ -669,13 +668,15 @@ public class FastStream implements Stream, Iterable { /** * 返回一个包含此流元素的指定的数组 + *

+ * 例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException} + *

{@code String[] strings = Stream.builder().add(1).build().toArray(String[]::new); }
+ *

* * @param generator 这里的IntFunction的参数是元素的个数,返回值为数组类型 * @param
给定的数组类型 * @return 包含此流元素的指定的数组 * @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常 - * 例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException} - *
{@code String[] strings = Stream.builder().add(1).build().toArray(String[]::new); }
*/ @Override public
A[] toArray(IntFunction generator) { 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/support/DropWhileSpliterator.java deleted file mode 100644 index 0ab4433a9..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/support/DropWhileSpliterator.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.hutool.core.stream.support; - -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 - */ -class DropWhileSpliterator implements Spliterator { - - static DropWhileSpliterator create(Spliterator source, Predicate predicate) { - return new DropWhileSpliterator<>(source, predicate); - } - - private final Spliterator source; - private final Predicate predicate; - private boolean isFound = false; - - private DropWhileSpliterator(Spliterator source, Predicate predicate) { - this.source = source; - this.predicate = predicate; - } - - @Override - public boolean tryAdvance(Consumer 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 getComparator() { - return source.getComparator(); - } -} 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/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java b/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java deleted file mode 100644 index dc0ca8da6..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/support/TakeWhileSpliterator.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.hutool.core.stream.support; - -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 - */ -class TakeWhileSpliterator implements Spliterator { - - static TakeWhileSpliterator create(Spliterator source, Predicate predicate) { - return new TakeWhileSpliterator<>(source, predicate); - } - - private final Spliterator source; - private final Predicate predicate; - private boolean isContinue = true; - - TakeWhileSpliterator(Spliterator source, Predicate predicate) { - this.source = source; - this.predicate = predicate; - } - - @Override - public boolean tryAdvance(Consumer 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 getComparator() { - return source.getComparator(); - } -} - From c5fa258079394c9c0f5648a88712d6b6db33934c Mon Sep 17 00:00:00 2001 From: Zjp <1215582715@qq.com> Date: Tue, 9 Aug 2022 11:36:20 +0800 Subject: [PATCH 5/7] =?UTF-8?q?1.=E6=96=B0=E5=A2=9ENonNull(Function)?= =?UTF-8?q?=E3=80=81mapNonNull(Function);=202.flatMapIter=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E4=BF=AE=E6=94=B9=E4=B8=BAflatIter;=203.flatMap?= =?UTF-8?q?=E5=92=8CflatIter=E4=B8=BB=E5=8A=A8=E8=BF=87=E6=BB=A4null?= =?UTF-8?q?=E5=85=83=E7=B4=A0;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/stream/FastStream.java | 43 ++++++++++++++++--- .../cn/hutool/core/stream/FastStreamTest.java | 13 ++++-- 2 files changed, 48 insertions(+), 8 deletions(-) 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 ca83b2488..eb9a4d248 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 @@ -289,6 +289,21 @@ public class FastStream implements Stream, Iterable { return new FastStream<>(stream.filter(Objects::nonNull)); } + /** + * 过滤掉 元素 指定字段值为 {@code null} 的元素, 返回剩下元素组成的流
+ *
{@code
+	 * // 例如, 过滤 id为空 的元素
+	 * .of(userList).nonNull(UserEntity::getId)...
+	 * }
+ * 这是一个无状态中间操作
+ * + * @return 过滤后的流 + */ + public FastStream nonNull(Function mapper) { + Objects.requireNonNull(mapper); + return new FastStream<>(stream.filter(e -> Objects.nonNull(mapper.apply(e)))); + } + /** * 返回与指定函数将元素作为参数执行的结果组成的流 * 这是一个无状态中间操作 @@ -302,6 +317,23 @@ public class FastStream implements Stream, Iterable { return new FastStream<>(stream.map(mapper)); } + /** + * 返回 元素 转换后 并且不为 {@code null} 的 新元素组成的流
+ * 这是一个无状态中间操作
+ *
{@code
+	 * // 等价于先调用map再调用nonNull
+	 * .map(...).nonNull()...
+	 * }
+ * + * @param mapper 指定的函数 + * @param 函数执行后返回的类型 + * @return 新元素组成的流 + */ + public FastStream mapNonNull(Function mapper) { + final Stream mapStream = this.stream.map(mapper); + return new FastStream<>(mapStream.filter(Objects::nonNull)); + } + /** * 返回与指定函数将元素作为参数执行的结果组成的流,操作带下标,并行流时下标永远为-1 * 这是一个无状态中间操作 @@ -321,7 +353,7 @@ public class FastStream implements Stream, Iterable { } /** - * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流 + * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,并过滤{@code null}元素, 返回多个流所有非空元素组成的流
* 这是一个无状态中间操作 * 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: *
{@code
@@ -334,7 +366,7 @@ public class FastStream implements Stream, Iterable {
 	 */
 	@Override
 	public  FastStream flatMap(Function> mapper) {
-		return new FastStream<>(stream.flatMap(mapper));
+		return new FastStream<>(stream.flatMap(mapper).filter(Objects::nonNull));
 	}
 
 	/**
@@ -392,8 +424,9 @@ public class FastStream implements Stream, Iterable {
 	}
 
 	/**
-	 * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
-	 * 这是一个无状态中间操作
+	 * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素,
+	 * 并过滤迭代器中的 {@code null} 元素,最后返回所有迭代器的所有非空元素组成的流
+ * 这是一个无状态中间操作
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: *
{@code
 	 *     FastStream ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
@@ -403,7 +436,7 @@ public class FastStream implements Stream, Iterable {
 	 * @param     拆分后流的元素类型
 	 * @return 返回叠加拆分操作后的流
 	 */
-	public  FastStream flatMapIter(Function> mapper) {
+	public  FastStream flatIter(Function> mapper) {
 		Objects.requireNonNull(mapper);
 		return flatMap(w -> of(mapper.apply(w)));
 	}
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 e08dfd2b3..dd15418e3 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
@@ -186,12 +186,19 @@ public class FastStreamTest {
 	}
 
 	@Test
-	public void testFlatMapIter() {
+	public void testFlatIter() {
 		List list = Arrays.asList(1, 2, 3);
-		List flatMapIter = FastStream.of(list).flatMapIter(e -> null).toList();
+
+		// 一个元素 扩散为 多个元素(迭代器)
+		List flatMapIter = FastStream.of(list).flatIter(e -> Arrays.asList(e, e * 10)).toList();
+		Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter);
+
+		// 过滤迭代器为null的元素
+		flatMapIter = FastStream.of(list).flatIter(e -> null).toList();
 		Assert.assertEquals(Collections.emptyList(), flatMapIter);
 
-		flatMapIter = FastStream.of(list).flatMapIter(e -> Arrays.asList(e, e * 10)).toList();
+		// 自动过滤null元素
+		flatMapIter = FastStream.of(list).flatIter(e -> Arrays.asList(e, null)).toList();
 		Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter);
 		// 不报npe测试
 		Assert.assertTrue(FastStream.of(list).flatMapIter(e -> null).isEmpty());

From 414439b89b6ddece72a303d1845413e512b05464 Mon Sep 17 00:00:00 2001
From: achao 
Date: Tue, 9 Aug 2022 14:35:56 +0800
Subject: [PATCH 6/7] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=87=BD=E6=95=B0?=
 =?UTF-8?q?=E5=90=8D=E3=80=81=E6=96=B0=E5=A2=9E=E4=B8=A4=E4=B8=AA=E9=A2=9D?=
 =?UTF-8?q?=E5=A4=96=E5=87=BD=E6=95=B0=E4=BD=9C=E4=B8=BAmap=E5=92=8Cflat?=
 =?UTF-8?q?=E7=9A=84=E5=89=8D=E5=90=8E=E7=A9=BA=E5=A4=84=E7=90=86=E3=80=81?=
 =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=EF=BC=8C?=
 =?UTF-8?q?=E6=8F=90=E5=8D=87=E8=A6=86=E7=9B=96=E7=8E=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../cn/hutool/core/stream/FastStream.java     | 59 ++++---------------
 .../cn/hutool/core/stream/FastStreamTest.java | 38 +++++++++---
 2 files changed, 42 insertions(+), 55 deletions(-)

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 eb9a4d248..d0d302519 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
@@ -289,21 +289,6 @@ public class FastStream implements Stream, Iterable {
 		return new FastStream<>(stream.filter(Objects::nonNull));
 	}
 
-	/**
-	 * 过滤掉 元素 指定字段值为 {@code null} 的元素, 返回剩下元素组成的流
- *
{@code
-	 * // 例如, 过滤 id为空 的元素
-	 * .of(userList).nonNull(UserEntity::getId)...
-	 * }
- * 这是一个无状态中间操作
- * - * @return 过滤后的流 - */ - public FastStream nonNull(Function mapper) { - Objects.requireNonNull(mapper); - return new FastStream<>(stream.filter(e -> Objects.nonNull(mapper.apply(e)))); - } - /** * 返回与指定函数将元素作为参数执行的结果组成的流 * 这是一个无状态中间操作 @@ -330,8 +315,7 @@ public class FastStream implements Stream, Iterable { * @return 新元素组成的流 */ public FastStream mapNonNull(Function mapper) { - final Stream mapStream = this.stream.map(mapper); - return new FastStream<>(mapStream.filter(Objects::nonNull)); + return nonNull().map(mapper).nonNull(); } /** @@ -366,7 +350,7 @@ public class FastStream implements Stream, Iterable { */ @Override public FastStream flatMap(Function> mapper) { - return new FastStream<>(stream.flatMap(mapper).filter(Objects::nonNull)); + return new FastStream<>(stream.flatMap(mapper)); } /** @@ -429,18 +413,22 @@ public class FastStream implements Stream, Iterable { * 这是一个无状态中间操作
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: *
{@code
-	 *     FastStream ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
+	 *     FastStream ids = FastStream.of(users).flat(user -> FastStream.of(user.getId(), user.getParentId()));
 	 * }
* * @param mapper 操作,返回可迭代对象 * @param 拆分后流的元素类型 * @return 返回叠加拆分操作后的流 */ - public FastStream flatIter(Function> mapper) { + public FastStream flat(Function> mapper) { Objects.requireNonNull(mapper); return flatMap(w -> of(mapper.apply(w))); } + public FastStream flatNonNull(Function> mapper) { + return nonNull().flat(mapper).nonNull(); + } + /** * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流 * 这是一个无状态中间操作 @@ -1457,8 +1445,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)
@@ -1476,25 +1464,14 @@ public class FastStream implements Stream, Iterable {
 		return of(StreamUtil.takeWhile(stream, predicate));
 	}
 
-	/**
-	 * 保留 与指定断言 匹配的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素
-	 * 

takeWhile 的别名方法

- * - * @param predicate 断言 - * @return 与指定断言匹配的元素组成的流 - * @see #takeWhile(Predicate) - */ - public FastStream limit(Predicate predicate) { - return takeWhile(predicate); - } /** * 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流 *

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

*
本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
 	 * FastStream.iterate(1, i <= 100, i -> i + 1)
-	 *	.parallel()
-	 *	// 顺序执行
+	 * 	.parallel()
+	 * 	// 顺序执行
 	 * 	.dropWhile(e -> e < 50)
 	 * 	// 并发
 	 * 	.map(e -> e + 1)
@@ -1512,18 +1489,6 @@ public class FastStream implements Stream, Iterable {
 		return of(StreamUtil.dropWhile(stream, predicate));
 	}
 
-	/**
-	 * 跳过 与断言匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流
-	 * 

dropWhile 的别名方法

- * - * @param predicate 断言 - * @return 剩余元素组成的流 - * @see #dropWhile(Predicate) - */ - public FastStream skip(Predicate predicate) { - return dropWhile(predicate); - } - /** * 流是否为空 * 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 dd15418e3..e9d51b7c5 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 @@ -23,6 +23,12 @@ public class FastStreamTest { Assert.assertEquals(Arrays.asList(1, 2, 3), list); } + @Test + public void testGenerate() { + List list = FastStream.generate(() -> 0).limit(3).toList(); + Assert.assertEquals(Arrays.asList(0, 0, 0), list); + } + @Test public void testOf() { Assert.assertEquals(3, FastStream.of(Arrays.asList(1, 2, 3), true).count()); @@ -140,6 +146,13 @@ public class FastStreamTest { Assert.assertEquals(Arrays.asList(1, 2, 2, 3, 3, 3), mapMulti); } + @Test + public void testMapNonNull() { + List list = Arrays.asList(1, 2, 3, null); + List mapNonNull = FastStream.of(list).mapNonNull(String::valueOf).toList(); + Assert.assertEquals(Arrays.asList("1", "2", "3"), mapNonNull); + } + @Test public void testDistinct() { List list = ListUtil.of(3, 2, 2, 1, null, null); @@ -174,6 +187,11 @@ public class FastStreamTest { FastStream.FastStreamBuilder builder = FastStream.builder(); FastStream.of(list).forEachOrderedIdx((e, i) -> builder.accept(i + 1 + "." + e)); Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), builder.build().toList()); + + FastStream.FastStreamBuilder streamBuilder = FastStream.builder(); + FastStream.of(list).parallel().forEachOrderedIdx((e, i) -> streamBuilder.accept(i + 1 + "." + e)); + Assert.assertEquals(Arrays.asList("0.dromara", "0.hutool", "0.sweet"), streamBuilder.build().toList()); + } @Test @@ -186,22 +204,22 @@ public class FastStreamTest { } @Test - public void testFlatIter() { + public void testFlat() { List list = Arrays.asList(1, 2, 3); // 一个元素 扩散为 多个元素(迭代器) - List flatMapIter = FastStream.of(list).flatIter(e -> Arrays.asList(e, e * 10)).toList(); - Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter); + List flat = FastStream.of(list).flat(e -> Arrays.asList(e, e * 10)).toList(); + Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flat); // 过滤迭代器为null的元素 - flatMapIter = FastStream.of(list).flatIter(e -> null).toList(); - Assert.assertEquals(Collections.emptyList(), flatMapIter); + flat = FastStream.of(list).flat(e -> null).toList(); + Assert.assertEquals(Collections.emptyList(), flat); // 自动过滤null元素 - flatMapIter = FastStream.of(list).flatIter(e -> Arrays.asList(e, null)).toList(); - Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter); + flat = FastStream.of(list).flat(e -> Arrays.asList(e, e > 2 ? e : null)).toList(); + Assert.assertEquals(ListUtil.of(1, null, 2, null, 3, 3), flat); // 不报npe测试 - Assert.assertTrue(FastStream.of(list).flatMapIter(e -> null).isEmpty()); + Assert.assertTrue(FastStream.of(list).flat(e -> null).isEmpty()); } @Test @@ -238,6 +256,8 @@ public class FastStreamTest { List list = Arrays.asList(1, 2); List push = FastStream.of(list).push(3).toList(); Assert.assertEquals(Arrays.asList(1, 2, 3), push); + + Assert.assertEquals(Arrays.asList(1, 2, 3, 4), FastStream.of(list).push(3, 4).toList()); } @Test @@ -245,6 +265,8 @@ public class FastStreamTest { List list = Arrays.asList(2, 3); List unshift = FastStream.of(list).unshift(1).toList(); Assert.assertEquals(Arrays.asList(1, 2, 3), unshift); + + Assert.assertEquals(Arrays.asList(1, 2, 2, 3), FastStream.of(list).unshift(1, 2).toList()); } @Test From d2df665eaafbbada6b68a356892f1df4b6630960 Mon Sep 17 00:00:00 2001 From: Zjp <1215582715@qq.com> Date: Tue, 9 Aug 2022 14:53:52 +0800 Subject: [PATCH 7/7] =?UTF-8?q?1.=E4=BF=AE=E6=94=B9flatMap,=20flat?= =?UTF-8?q?=E5=92=8CflatNonNull=E6=96=B9=E6=B3=95=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/stream/FastStream.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) 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 d0d302519..d07307938 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 @@ -337,8 +337,8 @@ public class FastStream implements Stream, Iterable { } /** - * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,并过滤{@code null}元素, 返回多个流所有非空元素组成的流
- * 这是一个无状态中间操作 + * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
+ * 这是一个无状态中间操作
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: *
{@code
 	 *     FastStream ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
@@ -409,7 +409,7 @@ public class FastStream implements Stream, Iterable {
 
 	/**
 	 * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素,
-	 * 并过滤迭代器中的 {@code null} 元素,最后返回所有迭代器的所有非空元素组成的流
+ * 最后返回所有迭代器的所有元素组成的流
* 这是一个无状态中间操作
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: *
{@code
@@ -425,6 +425,17 @@ public class FastStream implements Stream, Iterable {
 		return flatMap(w -> of(mapper.apply(w)));
 	}
 
+	/**
+	 * 扩散流操作,可能影响流元素个数,对过滤后的非{@code null}元素执行mapper操作,转换为迭代器,
+	 * 并过滤迭代器中为{@code null}的元素, 返回所有迭代器的所有非空元素组成的流
+ * 这是一个无状态中间操作
+ * + * @param mapper 操作,返回流 + * @param 拆分后流的元素类型 + * @return 返回叠加拆分操作后的流 + * @see #flat(Function) + * @see #nonNull() + */ public FastStream flatNonNull(Function> mapper) { return nonNull().flat(mapper).nonNull(); }