From 3c5313f0a71db58c66b67f1ff2a1c30d12f19a90 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Fri, 2 Sep 2022 16:14:47 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E5=B0=86=E9=94=AE=E5=80=BC=E5=AF=B9?= =?UTF-8?q?=E6=B5=81=E7=9A=84=E9=94=AE=E5=80=BC=E5=AF=B9=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E4=BB=8E=E5=86=85=E9=83=A8=E7=B1=BB=E8=B0=83=E6=95=B4=E4=B8=BA?= =?UTF-8?q?AbstractMap.SimpleImmutableEntry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/hutool/core/stream/EntryStream.java | 190 ++++++++---------- .../hutool/core/stream/EntryStreamTest.java | 4 +- 2 files changed, 89 insertions(+), 105 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java index d97e74a3f..81b7a6ecd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java @@ -1,6 +1,7 @@ package cn.hutool.core.stream; import cn.hutool.core.collection.ConcurrentHashSet; +import cn.hutool.core.collection.iter.IterUtil; import cn.hutool.core.map.multi.RowKeyTable; import cn.hutool.core.map.multi.Table; import cn.hutool.core.util.ObjUtil; @@ -13,9 +14,9 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; /** - *

针对键值对对象{@link Map.Entry}特化的增强流, - * 本身可视为一个元素类型为{@link Map.Entry}的{@link Stream}。
- * 用于支持流式处理{@link Map}集合中的、或具有潜在可能转为{@link Map}集合的数据。 + *

参考StreamEx的EntryStream与vavr的Map,是针对键值对对象{@link Map.Entry}特化的增强流实现。
+ * 本身可视为一个元素类型为{@link Map.Entry}的{@link Stream}, + * 用于支持流式处理{@link Map}集合中的、或其他键值对类型的数据。 * * @param 键类型 * @param 值类型 @@ -23,6 +24,11 @@ import java.util.stream.StreamSupport; */ public class EntryStream extends StreamWrapper, EntryStream> { + /** + * 默认的空键值对 + */ + private static final Map.Entry EMPTY_ENTRY = new AbstractMap.SimpleImmutableEntry<>(null, null); + /** * 根据键与值的集合创建键值对流,若两集合在相同下标的位置找不到对应的键或值,则使用{@code null}填充。
* 比如: {@code [1, 2, 3]}与{@code [1, 2]}合并,则得到{@code [{1=1}, {2=2}, {3=null}]}。 @@ -51,7 +57,7 @@ public class EntryStream extends StreamWrapper, EntryStrea final Iterator keyItr = keys.iterator(); final Iterator valueItr = values.iterator(); while (keyItr.hasNext() || valueItr.hasNext()) { - entries.add(new Entry<>( + entries.add(ofEntry( keyItr.hasNext() ? keyItr.next() : null, valueItr.hasNext() ? valueItr.next() : null )); @@ -106,7 +112,7 @@ public class EntryStream extends StreamWrapper, EntryStrea return empty(); } final Stream> stream = StreamSupport.stream(source.spliterator(), false) - .map(t -> new Entry<>(keyMapper.apply(t), valueMapper.apply(t))); + .map(t -> ofEntry(keyMapper.apply(t), valueMapper.apply(t))); return new EntryStream<>(stream); } @@ -121,7 +127,7 @@ public class EntryStream extends StreamWrapper, EntryStrea */ public static EntryStream of(Stream> stream) { return ObjUtil.isNull(stream) ? - empty() : new EntryStream<>(stream.map(Entry::new)); + empty() : new EntryStream<>(stream.map(EntryStream::ofEntry)); } /** @@ -142,19 +148,6 @@ public class EntryStream extends StreamWrapper, EntryStrea super(stream); } - // ================================ override ================================ - - /** - * 根据一个原始的流,返回一个新包装类实例 - * - * @param stream 流 - * @return 实现类 - */ - @Override - protected EntryStream convertToStreamImpl(Stream> stream) { - return new EntryStream<>(stream); - } - // ================================ 中间操作 ================================ /** @@ -164,7 +157,7 @@ public class EntryStream extends StreamWrapper, EntryStrea */ public EntryStream distinctByKey() { Set accessed = new ConcurrentHashSet<>(16); - return new EntryStream<>(stream.filter(e -> { + return convertToStreamImpl(stream.filter(e -> { K key = e.getKey(); if (accessed.contains(key)) { return false; @@ -181,7 +174,7 @@ public class EntryStream extends StreamWrapper, EntryStrea */ public EntryStream distinctByValue() { Set accessed = new ConcurrentHashSet<>(16); - return new EntryStream<>(stream.filter(e -> { + return convertToStreamImpl(stream.filter(e -> { V val = e.getValue(); if (accessed.contains(val)) { return false; @@ -229,6 +222,7 @@ public class EntryStream extends StreamWrapper, EntryStrea * * @return {@link EntryStream}实例 */ + @Override public EntryStream nonNull() { return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey()) && ObjUtil.isNotNull(e.getValue())); } @@ -304,8 +298,49 @@ public class EntryStream extends StreamWrapper, EntryStrea * @param value 值 * @return {@link EntryStream}实例 */ - public EntryStream push(K key, V value) { - return new EntryStream<>(Stream.concat(stream, Stream.of(new Entry<>(key, value)))); + public EntryStream append(K key, V value) { + return convertToStreamImpl(Stream.concat(stream, Stream.of(ofEntry(key, value)))); + } + + /** + * 项当前流队首追加元素 + * + * @param key 键 + * @param value 值 + * @return {@link EntryStream}实例 + */ + public EntryStream prepend(K key, V value) { + return convertToStreamImpl(Stream.concat(Stream.of(ofEntry(key, value)), stream)); + } + + /** + * 将输入元素转为流,返回一个前半段为当前流,后半段为新流的新{@link EasyStream}实例 + * + * @param entries 键值对 + * @return {@link EntryStream}实例 + */ + public EntryStream append(Iterable> entries) { + if (IterUtil.isEmpty(entries)) { + return this; + } + final Stream> contacted = StreamSupport.stream(entries.spliterator(), isParallel()) + .map(EntryStream::ofEntry); + return convertToStreamImpl(Stream.concat(stream, contacted)); + } + + /** + * 将输入元素转为流,返回一个前半段为新流,后半段为当前流的新{@link EasyStream}实例 + * + * @param entries 键值对 + * @return {@link EntryStream}实例 + */ + public EntryStream prepend(Iterable> entries) { + if (IterUtil.isEmpty(entries)) { + return this; + } + final Stream> contacted = StreamSupport.stream(entries.spliterator(), isParallel()) + .map(EntryStream::ofEntry); + return convertToStreamImpl(Stream.concat(contacted, stream)); } /** @@ -336,7 +371,7 @@ public class EntryStream extends StreamWrapper, EntryStrea public EntryStream mapKeys(Function mapper) { Objects.requireNonNull(mapper); return new EntryStream<>( - stream.map(e -> new Entry<>(mapper.apply(e.getKey()), e.getValue())) + stream.map(e -> ofEntry(mapper.apply(e.getKey()), e.getValue())) ); } @@ -350,7 +385,7 @@ public class EntryStream extends StreamWrapper, EntryStrea public EntryStream mapValues(Function mapper) { Objects.requireNonNull(mapper); return new EntryStream<>( - stream.map(e -> new Entry<>(e.getKey(), mapper.apply(e.getValue()))) + stream.map(e -> ofEntry(e.getKey(), mapper.apply(e.getValue()))) ); } @@ -417,7 +452,7 @@ public class EntryStream extends StreamWrapper, EntryStrea return new EntryStream<>( stream.flatMap(e -> keyMapper .apply(e.getKey()) - .map(newKey -> new Entry<>(newKey, e.getValue())) + .map(newKey -> ofEntry(newKey, e.getValue())) ) ); } @@ -441,7 +476,7 @@ public class EntryStream extends StreamWrapper, EntryStrea return new EntryStream<>( stream.flatMap(e -> valueMapper .apply(e.getValue()) - .map(newVal -> new Entry<>(e.getKey(), newVal)) + .map(newVal -> ofEntry(e.getKey(), newVal)) ) ); } @@ -625,7 +660,7 @@ public class EntryStream extends StreamWrapper, EntryStrea */ public EntryStream inverse() { return new EntryStream<>( - stream.map(e -> new Entry<>(e.getValue(), e.getKey())) + stream.map(e -> ofEntry(e.getValue(), e.getKey())) ); } @@ -683,85 +718,34 @@ public class EntryStream extends StreamWrapper, EntryStrea return super.noneMatch(e -> predicate.test(e.getKey(), e.getValue())); } + // ========================= private ========================= + /** - * {@link Map.Entry}的基本实现 + * 将键值对转为{@link AbstractMap.SimpleImmutableEntry} */ - static class Entry implements Map.Entry { + @SuppressWarnings("unchecked") + private static Map.Entry ofEntry(Map.Entry entry) { + return ObjUtil.defaultIfNull( + entry, e -> ofEntry(e.getKey(), e.getValue()), (Map.Entry)EMPTY_ENTRY + ); + } - /** - * 键 - */ - private final K key; - - /** - * 值 - */ - private V val; - - /** - * 创建一个简单键值对对象 - * - * @param key 键 - * @param val 值 - */ - public Entry(K key, V val) { - this.key = key; - this.val = val; - } - - /** - * 创建一个简单键值对对象 - * - * @param entry 键值对 - */ - public Entry(Map.Entry entry) { - if (ObjUtil.isNull(entry)) { - this.key = null; - this.val = null; - } else { - this.key = entry.getKey(); - this.val = entry.getValue(); - } - } - - /** - * 获取键 - * - * @return 键 - */ - @Override - public K getKey() { - return key; - } - - /** - * 获取值 - * - * @return 值 - */ - @Override - public V getValue() { - return val; - } - - /** - * 设置值 - * - * @param value 值 - * @return 旧值 - */ - @Override - public V setValue(V value) { - V old = val; - val = value; - return old; - } - - @Override - public String toString() { - return "{" + key + "=" + val + '}'; - } + /** + * 将键值对转为{@link AbstractMap.SimpleImmutableEntry} + */ + private static Map.Entry ofEntry(K key, V value) { + return new AbstractMap.SimpleImmutableEntry<>(key, value); + } + /** + * 根据一个原始的流,返回一个新包装类实例 + * + * @param stream 流 + * @return 实现类 + */ + @Override + public EntryStream convertToStreamImpl(Stream> stream) { + return new EntryStream<>(stream); } /** diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java index 3bbfe072c..c841d031f 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java @@ -116,8 +116,8 @@ public class EntryStreamTest { Assert.assertEquals( 5, EntryStream.of(Arrays.asList(1, 2, 3), Function.identity(), Function.identity()) - .push(4, 4) - .push(5, 5) + .append(4, 4) + .append(5, 5) .count() ); From 2d1255cbff80b8a8f91af36c65b06b032892a670 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Fri, 2 Sep 2022 18:06:50 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=B0=86=E5=A2=9E=E5=BC=BA=E6=B5=81?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=83=A8=E5=88=86=E9=80=9A=E7=94=A8=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=88=86=E7=A6=BB=E4=B8=BA=E6=8E=A5=E5=8F=A3=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E9=BB=98=E8=AE=A4=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stream/AbstractEnhancedStreamWrapper.java | 40 ++ .../cn/hutool/core/stream/EasyStream.java | 528 +----------------- .../cn/hutool/core/stream/EntryStream.java | 16 +- .../core/stream/SimpleStreamWrapper.java | 55 ++ .../cn/hutool/core/stream/StreamWrapper.java | 234 ++++---- .../core/stream/TerminableStreamWrapper.java | 475 ++++++++++++++++ .../stream/TransformableStreamWrapper.java | 216 +++++++ .../hutool/core/stream/EntryStreamTest.java | 3 +- 8 files changed, 919 insertions(+), 648 deletions(-) create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java new file mode 100644 index 000000000..02a9a6789 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java @@ -0,0 +1,40 @@ +package cn.hutool.core.stream; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * {@link StreamWrapper}的基本实现,用于包装一个已有的流实例, + * 使其支持相对原生{@link Stream}更多的中间操作与终端操作。 + * + * @author huangchengxing + * @see EasyStream + * @see EntryStream + */ +public abstract class AbstractEnhancedStreamWrapper> + implements TerminableStreamWrapper, TransformableStreamWrapper { + + /** + * 原始的流实例 + */ + protected final Stream stream; + + /** + * 获取被包装的元素流实例 + */ + @Override + public Stream stream() { + return stream; + } + + /** + * 创建一个流包装器 + * + * @param stream 包装的流对象 + * @throws NullPointerException 当{@code stream}为{@code null}时抛出 + */ + protected AbstractEnhancedStreamWrapper(Stream stream) { + this.stream = Objects.requireNonNull(stream, "stream must not null"); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java index 5de688e5c..6fed9712c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java @@ -1,23 +1,16 @@ package cn.hutool.core.stream; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.lang.Assert; -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.text.StrUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.*; -import java.util.stream.Collector; -import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -56,11 +49,7 @@ import java.util.stream.StreamSupport; * @see java.util.stream.Stream * @since 6.0.0 */ -public class EasyStream extends StreamWrapper> implements Stream, Iterable { - /** - * 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标 - */ - private static final int NOT_FOUND_INDEX = -1; +public class EasyStream extends AbstractEnhancedStreamWrapper> { /** * 构造 @@ -266,32 +255,6 @@ public class EasyStream extends StreamWrapper> implements St return filter(e -> Objects.equals(mapper.apply(e), value)); } - /** - * 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1 - * 这是一个无状态中间操作 - * - * @param predicate 断言 - * @return 返回叠加过滤操作后的流 - */ - public EasyStream filterIdx(final BiPredicate predicate) { - Objects.requireNonNull(predicate); - if (isParallel()) { - return filter(e -> predicate.test(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return filter(e -> predicate.test(e, index.incrementAndGet())); - } - } - - /** - * 过滤掉空元素 - * - * @return 过滤后的流 - */ - public EasyStream nonNull() { - return new EasyStream<>(stream.filter(Objects::nonNull)); - } - /** * 返回与指定函数将元素作为参数执行的结果组成的流 * 这是一个无状态中间操作 @@ -459,175 +422,6 @@ public class EasyStream extends StreamWrapper> implements St } } - /** - * 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1 - * 这是一个无状态中间操作 - * @param action 指定的函数 - * @return 返回叠加操作后的FastStream - * @apiNote 该方法存在的意义主要是用来调试 - * 当你需要查看经过操作管道某处的元素和下标,可以执行以下操作: - *

{@code
-	 *     .of("one", "two", "three", "four")
-	 * 				.filter(e -> e.length() > 3)
-	 * 				.peekIdx((e,i) -> System.out.println("Filtered value: " + e + " Filtered idx:" + i))
-	 * 				.map(String::toUpperCase)
-	 * 				.peekIdx((e,i) -> System.out.println("Mapped value: " + e + " Mapped idx:" + i))
-	 * 				.collect(Collectors.toList());
-	 * }
- */ - public EasyStream peekIdx(BiConsumer action) { - Objects.requireNonNull(action); - if (isParallel()) { - return peek(e -> action.accept(e, NOT_FOUND_INDEX)); - } else { - AtomicInteger index = new AtomicInteger(NOT_FOUND_INDEX); - return peek(e -> action.accept(e, index.incrementAndGet())); - } - } - - /** - * 返回叠加调用{@link Console#log(Object)}打印出结果的流 - * - * @return 返回叠加操作后的FastStream - */ - public EasyStream log() { - return peek(Console::log); - } - - /** - * 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1 - * 这是一个终端操作 - * - * @param action 操作 - */ - public void forEachIdx(final BiConsumer action) { - Objects.requireNonNull(action); - if (isParallel()) { - stream.forEach(e -> action.accept(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream.forEach(e -> action.accept(e, index.incrementAndGet())); - } - } - - /** - * 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1 - * 这是一个终端操作 - * - * @param action 操作 - */ - public void forEachOrderedIdx(final BiConsumer action) { - Objects.requireNonNull(action); - if (isParallel()) { - stream.forEachOrdered(e -> action.accept(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream.forEachOrdered(e -> action.accept(e, index.incrementAndGet())); - } - } - - /** - * 获取与给定断言匹配的第一个元素 - * - * @param predicate 断言 - * @return 与给定断言匹配的第一个元素 - */ - public Optional findFirst(final Predicate predicate) { - return stream.filter(predicate).findFirst(); - } - - /** - * 获取与给定断言匹配的第一个元素的下标,并行流下标永远为-1 - * - * @param predicate 断言 - * @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1 - */ - public int findFirstIdx(final Predicate predicate) { - Objects.requireNonNull(predicate); - if (isParallel()) { - return NOT_FOUND_INDEX; - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - //noinspection ResultOfMethodCallIgnored - stream.filter(e -> { - index.increment(); - return predicate.test(e); - }).findFirst(); - return index.get(); - } - } - - /** - * 获取最后一个元素 - * - * @return 最后一个元素 - */ - public Optional findLast() { - final MutableObj last = new MutableObj<>(null); - spliterator().forEachRemaining(last::set); - return Optional.ofNullable(last.get()); - } - - /** - * 获取与给定断言匹配的最后一个元素 - * - * @param predicate 断言 - * @return 与给定断言匹配的最后一个元素 - */ - public Optional findLast(final Predicate predicate) { - Objects.requireNonNull(predicate); - final MutableObj last = new MutableObj<>(null); - spliterator().forEachRemaining(e -> { - if (predicate.test(e)) { - last.set(e); - } - }); - return Optional.ofNullable(last.get()); - } - - /** - * 获取与给定断言匹配的最后一个元素的下标,并行流下标永远为-1 - * - * @param predicate 断言 - * @return 与给定断言匹配的最后一个元素的下标,如果不存在则返回-1 - */ - public int findLastIdx(final Predicate predicate) { - Objects.requireNonNull(predicate); - if (isParallel()) { - return NOT_FOUND_INDEX; - } else { - final MutableInt idxRef = new MutableInt(NOT_FOUND_INDEX); - forEachIdx((e, i) -> { - if (predicate.test(e)) { - idxRef.set(i); - } - }); - return idxRef.get(); - } - } - - /** - * 反转顺序 - * - * @return 反转元素顺序 - */ - @SuppressWarnings("unchecked") - public EasyStream reverse() { - final T[] array = (T[]) toArray(); - ArrayUtil.reverse(array); - return of(array).parallel(isParallel()).onClose(stream::close); - } - - /** - * 更改流的并行状态 - * - * @param parallel 是否并行 - * @return 流 - */ - public EasyStream parallel(final boolean parallel) { - return parallel ? parallel() : sequential(); - } - /** * 与给定元素组成的流合并,成为新的流 * @@ -638,16 +432,6 @@ public class EasyStream extends StreamWrapper> implements St return EasyStream.concat(this.stream, of(obj)); } - /** - * 与给定元素组成的流合并,成为新的流 - * - * @param obj 元素 - * @return 流 - */ - @SuppressWarnings("unchecked") - public EasyStream push(final T... obj) { - return EasyStream.concat(this.stream, of(obj)); - } /** * 给定元素组成的流与当前流合并,成为新的流 @@ -659,27 +443,6 @@ public class EasyStream extends StreamWrapper> implements St return EasyStream.concat(of(obj), this.stream); } - /** - * 给定元素组成的流与当前流合并,成为新的流 - * - * @param obj 元素 - * @return 流 - */ - @SafeVarargs - public final EasyStream unshift(final T... obj) { - return EasyStream.concat(of(obj), this.stream); - } - - /** - * 获取流中指定下标的元素,如果是负数,则从最后一个开始数起 - * - * @param idx 下标 - * @return 指定下标的元素 - */ - @SuppressWarnings("unchecked") - public Optional at(final Integer idx) { - return Opt.ofNullable(idx).map(i -> (T) ArrayUtil.get(toArray(), i)).toOptional(); - } /** * 根据一个原始的流,返回一个新包装类实例 @@ -688,158 +451,10 @@ public class EasyStream extends StreamWrapper> implements St * @return 实现类 */ @Override - protected EasyStream convertToStreamImpl(Stream stream) { + public EasyStream wrapping(Stream stream) { return new EasyStream<>(stream); } - /** - * 转换成集合 - * - * @param collectionFactory 集合工厂(可以是集合构造器) - * @param 集合类型 - * @return 集合 - */ - public > C toColl(final Supplier collectionFactory) { - return collect(Collectors.toCollection(collectionFactory)); - } - - /** - * 转换为ArrayList - * - * @return list - */ - public List toList() { - return collect(Collectors.toList()); - } - - /** - * 转换为HashSet - * - * @return hashSet - */ - public Set toSet() { - return collect(Collectors.toSet()); - } - - /** - * 与给定的可迭代对象转换成Map,key为现有元素,value为给定可迭代对象迭代的元素
- * Map的大小与两个集合中较小的数量一致, 即, 只合并下标位置相同的部分 - * - * @param other 可迭代对象 - * @param 可迭代对象迭代的元素类型 - * @return map,key为现有元素,value为给定可迭代对象迭代的元素 - */ - public Map toZip(final Iterable other) { - 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的值 - final MutableObj key = new MutableObj<>(); - // 保存第二个Spliterator的值 - final MutableObj value = new MutableObj<>(); - // 当两个Spliterator中都还有剩余元素时 - while (keys.tryAdvance(key::set) && values.tryAdvance(value::set)) { - map.put(key.get(), value.get()); - } - return map; - } - - /** - * 返回拼接后的字符串 - * - * @return 拼接后的字符串 - */ - public String join() { - return join(StrUtil.EMPTY); - } - - /** - * 返回拼接后的字符串 - * - * @param delimiter 分隔符 - * @return 拼接后的字符串 - */ - public String join(final CharSequence delimiter) { - return join(delimiter, StrUtil.EMPTY, StrUtil.EMPTY); - } - - /** - * 返回拼接后的字符串 - * - * @param delimiter 分隔符 - * @param prefix 前缀 - * @param suffix 后缀 - * @return 拼接后的字符串 - */ - public String join(final CharSequence delimiter, - final CharSequence prefix, - final CharSequence suffix) { - return map(String::valueOf).collect(Collectors.joining(delimiter, prefix, suffix)); - } - - /** - * 转换为map,key为给定操作执行后的返回值,value为当前元素 - * - * @param keyMapper 指定的key操作 - * @param key类型 - * @return map - */ - public Map toMap(final Function keyMapper) { - return toMap(keyMapper, Function.identity()); - } - - /** - * 转换为map,key,value为给定操作执行后的返回值 - * - * @param keyMapper 指定的key操作 - * @param valueMapper 指定value操作 - * @param key类型 - * @param value类型 - * @return map - */ - public Map toMap(final Function keyMapper, - final Function valueMapper) { - return toMap(keyMapper, valueMapper, (l, r) -> r); - } - - /** - * 转换为map,key,value为给定操作执行后的返回值 - * - * @param keyMapper 指定的key操作 - * @param valueMapper 指定value操作 - * @param mergeFunction 合并操作 - * @param key类型 - * @param value类型 - * @return map - */ - public Map toMap(final Function keyMapper, - final Function valueMapper, - final BinaryOperator mergeFunction) { - return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); - } - - /** - * 转换为map,key,value为给定操作执行后的返回值 - * - * @param keyMapper 指定的key操作 - * @param valueMapper 指定value操作 - * @param mergeFunction 合并操作 - * @param mapSupplier map工厂 - * @param key类型 - * @param value类型 - * @param map类型 - * @return map - */ - public > M toMap(final Function keyMapper, - final Function valueMapper, - final BinaryOperator mergeFunction, - final Supplier mapSupplier) { - return collect(CollectorUtil.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)); - } - /** * 将集合转换为树,默认用 {@code parentId == null} 作为顶部,内置一个小递归 * 因为需要在当前传入数据里查找,所以这是一个结束操作 @@ -929,51 +544,6 @@ public class EasyStream extends StreamWrapper> implements St return flat(recursive).peek(e -> childrenSetter.accept(e, null)); } - - /** - * 通过给定分组依据进行分组 - * - * @param classifier 分组依据 - * @param 实体中的分组依据对应类型,也是Map中key的类型 - * @return {@link Collector} - */ - public Map> group(final Function classifier) { - return group(classifier, Collectors.toList()); - } - - /** - * 通过给定分组依据进行分组 - * - * @param classifier 分组依据 - * @param downstream 下游操作 - * @param 实体中的分组依据对应类型,也是Map中key的类型 - * @param 下游操作对应返回类型,也是Map中value的类型 - * @param
下游操作在进行中间操作时对应类型 - * @return {@link Collector} - */ - public Map group(final Function classifier, - final Collector downstream) { - return group(classifier, HashMap::new, downstream); - } - - /** - * 通过给定分组依据进行分组 - * - * @param classifier 分组依据 - * @param mapFactory 提供的map - * @param downstream 下游操作 - * @param 实体中的分组依据对应类型,也是Map中key的类型 - * @param 下游操作对应返回类型,也是Map中value的类型 - * @param 下游操作在进行中间操作时对应类型 - * @param 最后返回结果Map类型 - * @return {@link Collector} - */ - public > M group(final Function classifier, - final Supplier mapFactory, - final Collector downstream) { - return collect(CollectorUtil.groupingBy(classifier, mapFactory, downstream)); - } - /** * 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流
* 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分
@@ -1004,21 +574,6 @@ public class EasyStream extends StreamWrapper> implements St return of(list).parallel(isParallel()).onClose(stream::close); } - /** - * 类似js的
splice函数 - * - * @param start 起始下标 - * @param deleteCount 删除个数,正整数 - * @param items 放入值 - * @return 操作后的流 - */ - @SafeVarargs - public final EasyStream splice(final int start, final int deleteCount, final T... items) { - return of(ListUtil.splice(toList(), start, deleteCount, items)) - .parallel(isParallel()) - .onClose(stream::close); - } - /** * 按指定长度切分为双层流 *

@@ -1055,85 +610,6 @@ public class EasyStream extends StreamWrapper> implements St return split(batchSize).map(EasyStream::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 EasyStream takeWhile(final Predicate predicate) { - Objects.requireNonNull(predicate); - return of(StreamUtil.takeWhile(stream, 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 EasyStream dropWhile(final Predicate predicate) { - Objects.requireNonNull(predicate); - return of(StreamUtil.dropWhile(stream, predicate)); - } - - /** - * 流是否为空 - * - * @return 流是否为空 - */ - public boolean isEmpty() { - return !findAny().isPresent(); - } - - /** - * 流是否不为空 - * - * @return 流是否不为空 - */ - public boolean isNotEmpty() { - return !isEmpty(); - } - - /** - * 将当前流转为另一对象。用于提供针对流本身而非流中元素的操作 - * - * @param 转换类型 - * @param transform 转换 - * @return 转换后的流 - */ - public Optional transform(final Function, R> transform) { - Assert.notNull(transform, "transform must not null"); - return Optional.ofNullable(transform.apply(this)); - } - public interface FastStreamBuilder extends Consumer, cn.hutool.core.builder.Builder> { /** diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java index 81b7a6ecd..c9eadd81c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java @@ -22,7 +22,7 @@ import java.util.stream.StreamSupport; * @param 值类型 * @author huangchengxing */ -public class EntryStream extends StreamWrapper, EntryStream> { +public class EntryStream extends AbstractEnhancedStreamWrapper, EntryStream> { /** * 默认的空键值对 @@ -157,7 +157,7 @@ public class EntryStream extends StreamWrapper, EntryStrea */ public EntryStream distinctByKey() { Set accessed = new ConcurrentHashSet<>(16); - return convertToStreamImpl(stream.filter(e -> { + return wrapping(stream.filter(e -> { K key = e.getKey(); if (accessed.contains(key)) { return false; @@ -174,7 +174,7 @@ public class EntryStream extends StreamWrapper, EntryStrea */ public EntryStream distinctByValue() { Set accessed = new ConcurrentHashSet<>(16); - return convertToStreamImpl(stream.filter(e -> { + return wrapping(stream.filter(e -> { V val = e.getValue(); if (accessed.contains(val)) { return false; @@ -299,7 +299,7 @@ public class EntryStream extends StreamWrapper, EntryStrea * @return {@link EntryStream}实例 */ public EntryStream append(K key, V value) { - return convertToStreamImpl(Stream.concat(stream, Stream.of(ofEntry(key, value)))); + return wrapping(Stream.concat(stream, Stream.of(ofEntry(key, value)))); } /** @@ -310,7 +310,7 @@ public class EntryStream extends StreamWrapper, EntryStrea * @return {@link EntryStream}实例 */ public EntryStream prepend(K key, V value) { - return convertToStreamImpl(Stream.concat(Stream.of(ofEntry(key, value)), stream)); + return wrapping(Stream.concat(Stream.of(ofEntry(key, value)), stream)); } /** @@ -325,7 +325,7 @@ public class EntryStream extends StreamWrapper, EntryStrea } final Stream> contacted = StreamSupport.stream(entries.spliterator(), isParallel()) .map(EntryStream::ofEntry); - return convertToStreamImpl(Stream.concat(stream, contacted)); + return wrapping(Stream.concat(stream, contacted)); } /** @@ -340,7 +340,7 @@ public class EntryStream extends StreamWrapper, EntryStrea } final Stream> contacted = StreamSupport.stream(entries.spliterator(), isParallel()) .map(EntryStream::ofEntry); - return convertToStreamImpl(Stream.concat(contacted, stream)); + return wrapping(Stream.concat(contacted, stream)); } /** @@ -744,7 +744,7 @@ public class EntryStream extends StreamWrapper, EntryStrea * @return 实现类 */ @Override - public EntryStream convertToStreamImpl(Stream> stream) { + public EntryStream wrapping(Stream> stream) { return new EntryStream<>(stream); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java new file mode 100644 index 000000000..636e54f1f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java @@ -0,0 +1,55 @@ +package cn.hutool.core.stream; + +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * {@link AbstractEnhancedStreamWrapper}的基本实现 + * + * @author huangchengxing + */ +public class SimpleStreamWrapper extends AbstractEnhancedStreamWrapper> { + + /** + * 创建一个流包装器 + * + * @param stream 包装的流对象 + * @throws NullPointerException 当{@code stream}为{@code null}时抛出 + */ + SimpleStreamWrapper(Stream stream) { + super(stream); + } + + /** + * 将一个流包装为{@link SimpleStreamWrapper} + * + * @param source 被包装的流 + * @return 包装后的流 + */ + @Override + public SimpleStreamWrapper wrapping(Stream source) { + return new SimpleStreamWrapper<>(source); + } + + /** + * 将当前流中元素映射为另一类型 + * + * @param mapper 映射方法 + * @return 映射后的流 + */ + @Override + public SimpleStreamWrapper map(Function mapper) { + return new SimpleStreamWrapper<>(stream().map(mapper)); + } + + /** + * 将当前流中元素展开为流,然后返回由这些新流中元素组成的流 + * + * @param mapper 映射方法 + * @return 映射后的流 + */ + @Override + public SimpleStreamWrapper flatMap(Function> mapper) { + return new SimpleStreamWrapper<>(stream().flatMap(mapper)); + } +} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java index 60da8e88d..8c6519f66 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java @@ -1,32 +1,57 @@ package cn.hutool.core.stream; -import java.util.*; +import cn.hutool.core.util.ObjUtil; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.Optional; +import java.util.Spliterator; import java.util.function.*; import java.util.stream.*; /** - * {@link Stream}的包装类,用于基于一个已有的流实例进行扩展 + *

表示一个用于增强原始{@link Stream}对象的包装器,当调用{@link Stream}中的方法时, + * 将会代理到被包装的原始流对象,并返回指定的包装器实例。 * + * @param 流中的元素类型 + * @param 链式调用获得的实现类类型 * @author huangchengxing - * @see EasyStream + * @see TerminableStreamWrapper + * @see TransformableStreamWrapper + * @see AbstractEnhancedStreamWrapper */ -abstract class StreamWrapper> implements Stream, Iterable { +public interface StreamWrapper> extends Stream, Iterable { /** - * 原始的流实例 + * 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标 */ - protected final Stream stream; + int NOT_FOUND_INDEX = -1; /** - * 创建一个流包装器 + * 将一个流包装为简单增强流,若{@code stream}为{@code null}则默认返回一个空串行流 * - * @param stream 包装的流对象 + * @param stream 被包装的流 + * @return {@link SimpleStreamWrapper}实例 */ - protected StreamWrapper(Stream stream) { - Objects.requireNonNull(stream, "stream must not null"); - this.stream = stream; + static SimpleStreamWrapper create(Stream stream) { + return new SimpleStreamWrapper<>(ObjUtil.defaultIfNull(stream, Stream.empty())); } + /** + * 获取被包装的原始流 + * + * @return 被包装的原始流 + */ + Stream stream(); + + /** + * 将一个原始流包装为指定类型的增强流 + * + * @param source 被包装的流 + * @return I + */ + I wrapping(Stream source); + /** * 过滤元素,返回与指定断言匹配的元素组成的流 * 这是一个无状态中间操作 @@ -35,8 +60,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 返回叠加过滤操作后的流 */ @Override - public I filter(Predicate predicate) { - return convertToStreamImpl(stream.filter(predicate)); + default I filter(Predicate predicate) { + return wrapping(stream().filter(predicate)); } /** @@ -47,8 +72,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 叠加操作后元素类型全为int的流 */ @Override - public IntStream mapToInt(ToIntFunction mapper) { - return stream.mapToInt(mapper); + default IntStream mapToInt(ToIntFunction mapper) { + return stream().mapToInt(mapper); } /** @@ -59,8 +84,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 叠加操作后元素类型全为long的流 */ @Override - public LongStream mapToLong(ToLongFunction mapper) { - return stream.mapToLong(mapper); + default LongStream mapToLong(ToLongFunction mapper) { + return stream().mapToLong(mapper); } /** @@ -71,8 +96,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 叠加操作后元素类型全为double的流 */ @Override - public DoubleStream mapToDouble(ToDoubleFunction mapper) { - return stream.mapToDouble(mapper); + default DoubleStream mapToDouble(ToDoubleFunction mapper) { + return stream().mapToDouble(mapper); } /** @@ -83,8 +108,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 返回叠加拆分操作后的IntStream */ @Override - public IntStream flatMapToInt(Function mapper) { - return stream.flatMapToInt(mapper); + default IntStream flatMapToInt(Function mapper) { + return stream().flatMapToInt(mapper); } /** @@ -95,8 +120,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 返回叠加拆分操作后的LongStream */ @Override - public LongStream flatMapToLong(Function mapper) { - return stream.flatMapToLong(mapper); + default LongStream flatMapToLong(Function mapper) { + return stream().flatMapToLong(mapper); } /** @@ -107,8 +132,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 返回叠加拆分操作后的DoubleStream */ @Override - public DoubleStream flatMapToDouble(Function mapper) { - return stream.flatMapToDouble(mapper); + default DoubleStream flatMapToDouble(Function mapper) { + return stream().flatMapToDouble(mapper); } /** @@ -118,8 +143,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 一个具有去重特征的流 */ @Override - public I distinct() { - return convertToStreamImpl(stream.distinct()); + default I distinct() { + return wrapping(stream().distinct()); } /** @@ -131,8 +156,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 一个元素按自然顺序排序的流 */ @Override - public I sorted() { - return convertToStreamImpl(stream.sorted()); + default I sorted() { + return wrapping(stream().sorted()); } /** @@ -145,8 +170,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 一个元素按指定的Comparator排序的流 */ @Override - public I sorted(Comparator comparator) { - return convertToStreamImpl(stream.sorted(comparator)); + default I sorted(Comparator comparator) { + return wrapping(stream().sorted(comparator)); } /** @@ -167,8 +192,8 @@ abstract class StreamWrapper> implements Stream, Itera * } */ @Override - public I peek(Consumer action) { - return convertToStreamImpl(stream.peek(action)); + default I peek(Consumer action) { + return wrapping(stream().peek(action)); } /** @@ -179,8 +204,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 截取后的流 */ @Override - public I limit(long maxSize) { - return convertToStreamImpl(stream.limit(maxSize)); + default I limit(long maxSize) { + return wrapping(stream().limit(maxSize)); } /** @@ -191,8 +216,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 丢弃前面n个元素后的剩余元素组成的流 */ @Override - public I skip(long n) { - return convertToStreamImpl(stream.skip(n)); + default I skip(long n) { + return wrapping(stream().skip(n)); } /** @@ -202,8 +227,8 @@ abstract class StreamWrapper> implements Stream, Itera * @param action 操作 */ @Override - public void forEach(Consumer action) { - stream.forEach(action); + default void forEach(Consumer action) { + stream().forEach(action); } /** @@ -213,8 +238,8 @@ abstract class StreamWrapper> implements Stream, Itera * @param action 操作 */ @Override - public void forEachOrdered(Consumer action) { - stream.forEachOrdered(action); + default void forEachOrdered(Consumer action) { + stream().forEachOrdered(action); } /** @@ -224,8 +249,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 包含此流元素的数组 */ @Override - public Object[] toArray() { - return stream.toArray(); + default Object[] toArray() { + return stream().toArray(); } /** @@ -238,8 +263,8 @@ abstract class StreamWrapper> implements Stream, Itera * @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常 */ @Override - public A[] toArray(IntFunction generator) { - return stream.toArray(generator); + default A[] toArray(IntFunction generator) { + return stream().toArray(generator); } /** @@ -263,8 +288,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 聚合计算后的值 */ @Override - public T reduce(T identity, BinaryOperator accumulator) { - return stream.reduce(identity, accumulator); + default T reduce(T identity, BinaryOperator accumulator) { + return stream().reduce(identity, accumulator); } /** @@ -298,8 +323,8 @@ abstract class StreamWrapper> implements Stream, Itera * @see #max(Comparator) */ @Override - public Optional reduce(BinaryOperator accumulator) { - return stream.reduce(accumulator); + default Optional reduce(BinaryOperator accumulator) { + return stream().reduce(accumulator); } /** @@ -315,8 +340,8 @@ abstract class StreamWrapper> implements Stream, Itera * @see #reduce(Object, BinaryOperator) */ @Override - public U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) { - return stream.reduce(identity, accumulator, combiner); + default U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) { + return stream().reduce(identity, accumulator, combiner); } /** @@ -333,8 +358,8 @@ abstract class StreamWrapper> implements Stream, Itera * } */ @Override - public R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) { - return stream.collect(supplier, accumulator, combiner); + default R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) { + return stream().collect(supplier, accumulator, combiner); } /** @@ -347,8 +372,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 收集后的容器 */ @Override - public R collect(Collector collector) { - return stream.collect(collector); + default R collect(Collector collector) { + return stream().collect(collector); } /** @@ -358,8 +383,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 最小值 */ @Override - public Optional min(Comparator comparator) { - return stream.min(comparator); + default Optional min(Comparator comparator) { + return stream().min(comparator); } /** @@ -369,8 +394,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 最大值 */ @Override - public Optional max(Comparator comparator) { - return stream.max(comparator); + default Optional max(Comparator comparator) { + return stream().max(comparator); } /** @@ -379,8 +404,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 流元素个数 */ @Override - public long count() { - return stream.count(); + default long count() { + return stream().count(); } /** @@ -390,8 +415,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 是否有任何一个元素满足给定断言 */ @Override - public boolean anyMatch(Predicate predicate) { - return stream.anyMatch(predicate); + default boolean anyMatch(Predicate predicate) { + return stream().anyMatch(predicate); } /** @@ -401,8 +426,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 是否所有元素满足给定断言 */ @Override - public boolean allMatch(Predicate predicate) { - return stream.allMatch(predicate); + default boolean allMatch(Predicate predicate) { + return stream().allMatch(predicate); } /** @@ -412,8 +437,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 是否没有元素满足给定断言 */ @Override - public boolean noneMatch(Predicate predicate) { - return stream.noneMatch(predicate); + default boolean noneMatch(Predicate predicate) { + return stream().noneMatch(predicate); } /** @@ -422,8 +447,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 第一个元素 */ @Override - public Optional findFirst() { - return stream.findFirst(); + default Optional findFirst() { + return stream().findFirst(); } /** @@ -432,8 +457,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 随便取一个 */ @Override - public Optional findAny() { - return stream.findAny(); + default Optional findAny() { + return stream().findAny(); } /** @@ -442,8 +467,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 流的迭代器 */ @Override - public Iterator iterator() { - return stream.iterator(); + default Iterator iterator() { + return stream().iterator(); } /** @@ -452,8 +477,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 流的拆分器 */ @Override - public Spliterator spliterator() { - return stream.spliterator(); + default Spliterator spliterator() { + return stream().spliterator(); } /** @@ -462,8 +487,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 流的并行状态 */ @Override - public boolean isParallel() { - return stream.isParallel(); + default boolean isParallel() { + return stream().isParallel(); } /** @@ -472,8 +497,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 串行流 */ @Override - public I sequential() { - return convertToStreamImpl(stream.sequential()); + default I sequential() { + return wrapping(stream().sequential()); } /** @@ -482,8 +507,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 并行流 */ @Override - public I parallel() { - return convertToStreamImpl(stream.parallel()); + default I parallel() { + return wrapping(stream().parallel()); } /** @@ -493,8 +518,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 无序流 */ @Override - public I unordered() { - return convertToStreamImpl(stream.unordered()); + default I unordered() { + return wrapping(stream().unordered()); } /** @@ -504,8 +529,8 @@ abstract class StreamWrapper> implements Stream, Itera * @return 流 */ @Override - public I onClose(Runnable closeHandler) { - return convertToStreamImpl(stream.onClose(closeHandler)); + default I onClose(Runnable closeHandler) { + return wrapping(stream().onClose(closeHandler)); } /** @@ -514,50 +539,33 @@ abstract class StreamWrapper> implements Stream, Itera * @see AutoCloseable#close() */ @Override - public void close() { - stream.close(); + default void close() { + stream().close(); } /** - * hashcode + * 获取当前实例的哈希值 * - * @return hashcode + * @return 哈希值 */ @Override - public int hashCode() { - return stream.hashCode(); - } + int hashCode(); /** - * equals + * 比较实例是否相等 * * @param obj 对象 - * @return 结果 + * @return 是否相等 */ @Override - public boolean equals(final Object obj) { - if (obj instanceof Stream) { - return stream.equals(obj); - } - return false; - } + boolean equals(final Object obj); /** - * toString + * 将当前实例转为字符串 * - * @return string + * @return 字符串 */ @Override - public String toString() { - return stream.toString(); - } - - /** - * 根据一个原始的流,返回一个新包装类实例 - * - * @param stream 流 - * @return 实现类 - */ - protected abstract I convertToStreamImpl(Stream stream); + String toString(); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java new file mode 100644 index 000000000..eee30f86f --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java @@ -0,0 +1,475 @@ +package cn.hutool.core.stream; + +import cn.hutool.core.lang.Opt; +import cn.hutool.core.lang.mutable.MutableInt; +import cn.hutool.core.lang.mutable.MutableObj; +import cn.hutool.core.util.ArrayUtil; + +import java.util.*; +import java.util.function.*; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +/** + * {@link StreamWrapper}的扩展,为实现类提供更多终端操作方法 + * + * @param 流中的元素类型 + * @param 链式调用获得的实现类类型 + * @author huangchengxing + */ +public interface TerminableStreamWrapper> extends StreamWrapper { + + // region ============ join ============ + + /** + * 返回拼接后的字符串 + * + * @return 拼接后的字符串 + */ + default String join() { + return this.join(""); + } + + /** + * 返回拼接后的字符串 + * + * @param delimiter 分隔符 + * @return 拼接后的字符串 + */ + default String join(CharSequence delimiter) { + return this.join(delimiter, "", ""); + } + + /** + * 返回拼接后的字符串 + * + * @param delimiter 分隔符 + * @param prefix 前缀 + * @param suffix 后缀 + * @return 拼接后的字符串 + */ + default String join(CharSequence delimiter, + CharSequence prefix, + CharSequence suffix) { + return stream().map(String::valueOf).collect(Collectors.joining(delimiter, prefix, suffix)); + } + + // endregion + + // region ============ to collection ============ + + /** + * 转换成集合 + * + * @param collectionFactory 集合工厂(可以是集合构造器) + * @param 集合类型 + * @return 集合 + */ + default > C toColl(Supplier collectionFactory) { + return stream().collect(Collectors.toCollection(collectionFactory)); + } + + /** + * 转换为{@link ArrayList} + * + * @return 集合 + */ + default List toList() { + return this.toColl(ArrayList::new); + } + + /** + * 换为不可变集合 + * + * @return 集合 + */ + default List toUnmodifiableList() { + return Collections.unmodifiableList(this.toList()); + } + + /** + * 转换为HashSet + * + * @return 集合 + */ + default Set toSet() { + return this.toColl(HashSet::new); + } + + /** + * 换为不可变集合 + * + * @return 集合 + */ + default Set toUnmodifiableSet() { + return Collections.unmodifiableSet(this.toSet()); + } + + // endregion + + // region ============ to map ============ + + /** + * 转换为map,key为给定操作执行后的返回值,value为当前元素 + * + * @param keyMapper 指定的key操作 + * @param key类型 + * @return map + */ + default Map toMap(Function keyMapper) { + return this.toMap(keyMapper, Function.identity()); + } + + /** + * 转换为map,key,value为给定操作执行后的返回值 + * + * @param keyMapper 指定的key操作 + * @param valueMapper 指定value操作 + * @param key类型 + * @param value类型 + * @return map + */ + default Map toMap( + Function keyMapper, Function valueMapper) { + return this.toMap(keyMapper, valueMapper, (l, r) -> r); + } + + /** + * 转换为不可变map,key,value为给定操作执行后的返回值 + * + * @param keyMapper 指定的key操作 + * @param valueMapper 指定value操作 + * @param key类型 + * @param value类型 + * @return map + */ + default Map toUnmodifiableMap( + Function keyMapper, Function valueMapper) { + return Collections.unmodifiableMap(this.toMap(keyMapper, valueMapper)); + } + + /** + * 转换为map,key,value为给定操作执行后的返回值 + * + * @param keyMapper 指定的key操作 + * @param valueMapper 指定value操作 + * @param mergeFunction 合并操作 + * @param key类型 + * @param value类型 + * @return map + */ + default Map toMap( + Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction) { + return this.toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); + } + + /** + * 转换为不可变map,key,value为给定操作执行后的返回值 + * + * @param keyMapper 指定的key操作 + * @param valueMapper 指定value操作 + * @param mergeFunction 合并操作 + * @param key类型 + * @param value类型 + * @return map + */ + default Map toUnmodifiableMap( + Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction) { + return Collections.unmodifiableMap( + this.toMap(keyMapper, valueMapper, mergeFunction, HashMap::new) + ); + } + + /** + * 转换为map,key,value为给定操作执行后的返回值 + * + * @param keyMapper 指定的key操作 + * @param valueMapper 指定value操作 + * @param mergeFunction 合并操作 + * @param mapSupplier map工厂 + * @param key类型 + * @param value类型 + * @param map类型 + * @return map + */ + default > M toMap( + Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction, + Supplier mapSupplier) { + return stream().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)); + } + + // endregion + + // region ============ to zip ============ + + /** + * 与给定的可迭代对象转换成map,key为现有元素,value为给定可迭代对象迭代的元素
+ * 至少包含全部的key,如果对应位置上的value不存在,则为null + * + * @param other 可迭代对象 + * @param 可迭代对象迭代的元素类型 + * @return map,key为现有元素,value为给定可迭代对象迭代的元素;
+ * 至少包含全部的key,如果对应位置上的value不存在,则为null;
+ * 如果key重复, 则保留最后一个关联的value;
+ */ + default Map toZip(Iterable other) { + // value对象迭代器 + final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); + if (this.isParallel()) { + List keyList = toList(); + final Map map = new HashMap<>(keyList.size()); + for (T key : keyList) { + map.put(key, iterator.hasNext() ? iterator.next() : null); + } + return map; + } else { + return this.toMap(Function.identity(), e -> iterator.hasNext() ? iterator.next() : null); + } + } + + // endregion + + // region ============ group ============ + + /** + * 通过给定分组依据进行分组 + * + * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @return map + */ + default Map> group(Function classifier) { + return this.group(classifier, Collectors.toList()); + } + + /** + * 通过给定分组依据进行分组 + * + * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 + * @param downstream 下游操作 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @param 下游操作对应返回类型,也是Map中value的类型 + * @param
下游操作在进行中间操作时对应类型 + * @return map + */ + default Map group( + Function classifier, Collector downstream) { + return this.group(classifier, HashMap::new, downstream); + } + + /** + * 通过给定分组依据进行分组 + * + * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 + * @param mapFactory 提供的map + * @param downstream 下游操作 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @param 下游操作对应返回类型,也是Map中value的类型 + * @param 下游操作在进行中间操作时对应类型 + * @param 最后返回结果Map类型 + * @return map + * @see CollectorUtil#groupingBy(Function, Supplier, Collector) + */ + default > M group( + Function classifier, + Supplier mapFactory, + Collector downstream) { + return stream().collect(CollectorUtil.groupingBy(classifier, mapFactory, downstream)); + } + + /** + * 根据给定判断条件分组 + * + * @param predicate 判断条件 + * @return map + */ + default Map> partition(Predicate predicate) { + return this.partition(predicate, ArrayList::new); + } + + /** + * 根据给定判断条件分组 + * + * @param predicate 判断条件 + * @param collFactory 提供的集合 + * @return map + */ + default > Map partition(Predicate predicate, Supplier collFactory) { + return this.partition(predicate, Collectors.toCollection(collFactory)); + } + + /** + * 根据给定判断条件分组 + * + * @param predicate 判断条件 + * @param downstream 下游操作 + * @param 返回值类型 + * @return map + */ + default Map partition(Predicate predicate, Collector downstream) { + return stream().collect(Collectors.partitioningBy(predicate, downstream)); + } + + // endregion + + // region ============ foreach ============ + + /** + * 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1 + * 这是一个终端操作 + * + * @param action 操作 + */ + default void forEachIdx(final BiConsumer action) { + Objects.requireNonNull(action); + if (isParallel()) { + stream().forEach(e -> action.accept(e, NOT_FOUND_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_INDEX); + stream().forEach(e -> action.accept(e, index.incrementAndGet())); + } + } + + /** + * 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1 + * 这是一个终端操作 + * + * @param action 操作 + */ + default void forEachOrderedIdx(final BiConsumer action) { + Objects.requireNonNull(action); + if (isParallel()) { + stream().forEachOrdered(e -> action.accept(e, NOT_FOUND_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_INDEX); + stream().forEachOrdered(e -> action.accept(e, index.incrementAndGet())); + } + } + + // endregion + + // region ============ find & get ============ + + + + /** + * 获取与给定断言匹配的第一个元素 + * + * @param predicate 断言 + * @return 与给定断言匹配的第一个元素 + */ + default Optional findFirst(final Predicate predicate) { + return stream().filter(predicate).findFirst(); + } + + /** + * 获取与给定断言匹配的第一个元素的下标,并行流下标永远为-1 + * + * @param predicate 断言 + * @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1 + */ + default int findFirstIdx(final Predicate predicate) { + Objects.requireNonNull(predicate); + if (isParallel()) { + return NOT_FOUND_INDEX; + } else { + final MutableInt index = new MutableInt(NOT_FOUND_INDEX); + stream().filter(e -> { + index.increment(); + return predicate.test(e); + }).findFirst(); + return index.get(); + } + } + + /** + * 获取最后一个元素 + * + * @return 最后一个元素 + */ + default Optional findLast() { + final MutableObj last = new MutableObj<>(null); + spliterator().forEachRemaining(last::set); + return Optional.ofNullable(last.get()); + } + + /** + * 获取与给定断言匹配的最后一个元素 + * + * @param predicate 断言 + * @return 与给定断言匹配的最后一个元素 + */ + default Optional findLast(final Predicate predicate) { + Objects.requireNonNull(predicate); + final MutableObj last = new MutableObj<>(null); + spliterator().forEachRemaining(e -> { + if (predicate.test(e)) { + last.set(e); + } + }); + return Optional.ofNullable(last.get()); + } + + /** + * 获取与给定断言匹配的最后一个元素的下标,并行流下标永远为-1 + * + * @param predicate 断言 + * @return 与给定断言匹配的最后一个元素的下标,如果不存在则返回-1 + */ + default int findLastIdx(final Predicate predicate) { + Objects.requireNonNull(predicate); + if (isParallel()) { + return NOT_FOUND_INDEX; + } else { + final MutableInt idxRef = new MutableInt(NOT_FOUND_INDEX); + forEachIdx((e, i) -> { + if (predicate.test(e)) { + idxRef.set(i); + } + }); + return idxRef.get(); + } + } + + /** + * 获取流中指定下标的元素,如果是负数,则从最后一个开始数起 + * + * @param idx 下标 + * @return 指定下标的元素 + */ + @SuppressWarnings("unchecked") + default Optional at(final Integer idx) { + return Opt.ofNullable(idx).map(i -> (T) ArrayUtil.get(toArray(), i)).toOptional(); + } + + // endregion + + // region ============ is ============ + + /** + * 流是否为空 + * + * @return 流是否为空 + */ + default boolean isEmpty() { + return !findAny().isPresent(); + } + + /** + * 流是否不为空 + * + * @return 流是否不为空 + */ + default boolean isNotEmpty() { + return !isEmpty(); + } + + // endregion + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java new file mode 100644 index 000000000..10ad264bf --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java @@ -0,0 +1,216 @@ +package cn.hutool.core.stream; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Console; +import cn.hutool.core.lang.mutable.MutableInt; +import cn.hutool.core.util.ArrayUtil; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * {@link StreamWrapper}的扩展,为实现类提供更多中间操作方法 + * + * @param 流中的元素类型 + * @param 链式调用获得的实现类类型 + * @author huangchengxing + */ +public interface TransformableStreamWrapper> extends StreamWrapper { + + /** + * 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1 + * 这是一个无状态中间操作 + * + * @param predicate 断言 + * @return 返回叠加过滤操作后的流 + */ + default I filterIdx(final BiPredicate predicate) { + Objects.requireNonNull(predicate); + if (isParallel()) { + return filter(e -> predicate.test(e, NOT_FOUND_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_INDEX); + return filter(e -> predicate.test(e, index.incrementAndGet())); + } + } + + /** + * 过滤掉空元素 + * + * @return 过滤后的流 + */ + default I nonNull() { + return filter(Objects::nonNull); + } + + /** + * 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1 + * 这是一个无状态中间操作 + * @param action 指定的函数 + * @return 返回叠加操作后的FastStream + * @apiNote 该方法存在的意义主要是用来调试 + * 当你需要查看经过操作管道某处的元素和下标,可以执行以下操作: + *

{@code
+	 *     Stream.of("one", "two", "three", "four")
+	 * 				.filter(e -> e.length() > 3)
+	 * 				.peekIdx((e,i) -> System.out.println("Filtered value: " + e + " Filtered idx:" + i))
+	 * 				.map(String::toUpperCase)
+	 * 				.peekIdx((e,i) -> System.out.println("Mapped value: " + e + " Mapped idx:" + i))
+	 * 				.collect(Collectors.toList());
+	 * }
+ */ + default I peekIdx(BiConsumer action) { + Objects.requireNonNull(action); + if (isParallel()) { + return peek(e -> action.accept(e, NOT_FOUND_INDEX)); + } else { + AtomicInteger index = new AtomicInteger(NOT_FOUND_INDEX); + return peek(e -> action.accept(e, index.incrementAndGet())); + } + } + + /** + * 返回叠加调用{@link Console#log(Object)}打印出结果的流 + * + * @return 返回叠加操作后的FastStream + */ + default I log() { + return peek(Console::log); + } + + /** + * 反转顺序 + * + * @return 反转元素顺序 + */ + @SuppressWarnings("unchecked") + default I reverse() { + final T[] array = (T[]) toArray(); + ArrayUtil.reverse(array); + return wrapping(Stream.of(array)).parallel(isParallel()); + } + + /** + * 更改流的并行状态 + * + * @param parallel 是否并行 + * @return 流 + */ + default I parallel(final boolean parallel) { + return parallel ? parallel() : sequential(); + } + + /** + * 与给定元素组成的流合并,成为新的流 + * + * @param obj 元素 + * @return 流 + */ + @SuppressWarnings("unchecked") + default I push(final T... obj) { + Stream result = stream(); + if (ArrayUtil.isNotEmpty(obj)) { + result = Stream.concat(stream(), Stream.of(obj)); + } + return wrapping(result); + } + + /** + * 给定元素组成的流与当前流合并,成为新的流 + * + * @param obj 元素 + * @return 流 + */ + default I unshift(final T... obj) { + Stream result = stream(); + if (ArrayUtil.isNotEmpty(obj)) { + result = Stream.concat(Stream.of(obj), stream()); + } + return wrapping(result); + } + + /** + * 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。 + * 类似js的splice函数 + * + * @param start 起始下标 + * @param deleteCount 删除个数,正整数 + * @param items 放入值 + * @return 操作后的流 + */ + default I splice(final int start, final int deleteCount, final T... items) { + final List elements = stream().collect(Collectors.toList()); + return wrapping(ListUtil.splice(elements, start, deleteCount, items).stream()) + .parallel(isParallel()); + } + + /** + * 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 + *

与 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 与指定断言匹配的元素组成的流 + */ + default I takeWhile(final Predicate predicate) { + Objects.requireNonNull(predicate); + return wrapping(StreamUtil.takeWhile(stream(), 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 剩余元素组成的流 + */ + default I dropWhile(final Predicate predicate) { + Objects.requireNonNull(predicate); + return wrapping(StreamUtil.dropWhile(stream(), predicate)); + } + + /** + * 将当前流转为另一对象。用于提供针对流本身而非流中元素的操作 + * + * @param 转换类型 + * @param transform 转换 + * @return 转换后的流 + */ + default Optional transform(final Function transform) { + Assert.notNull(transform, "transform must not null"); + return Optional.ofNullable(transform.apply(wrapping(this))); + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java index c841d031f..7e91c0144 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import java.util.*; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -327,7 +328,7 @@ public class EntryStreamTest { Map result = EntryStream.of(map).toMap(); Assert.assertEquals(map, result); - result = EntryStream.of(map).toMap(LinkedHashMap::new); + result = EntryStream.of(map).toMap((Supplier>)LinkedHashMap::new); Assert.assertEquals(new LinkedHashMap<>(map), result); result = EntryStream.of(map).toMap(LinkedHashMap::new, (t1, t2) -> t1); From 741f0aa53a08f81f8d3732c011209baa059d8a4f Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 6 Sep 2022 11:21:38 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E6=B5=81=EF=BC=9A=201.=E7=A7=BB=E9=99=A4SimpleStreamWrapper;?= =?UTF-8?q?=202.=E5=B0=86EasyStream=E4=B8=AD=E9=83=A8=E5=88=86=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=A7=BB=E5=8A=A8=E8=87=B3=E6=8E=A5=E5=8F=A3=E4=B8=AD?= =?UTF-8?q?;=203.=E8=B0=83=E6=95=B4=E7=B1=BB=E5=90=8D=E3=80=81=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=90=8D=E3=80=81=E5=8F=98=E9=87=8F=E5=90=8D=E4=B8=8E?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=B3=A8=E9=87=8A;=204.=E4=B8=BA=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E6=96=B9=E6=B3=95=E6=B7=BB=E5=8A=A0=E7=A9=BA=E5=80=BC?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C;=205.=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stream/AbstractEnhancedStreamWrapper.java | 40 - .../stream/AbstractEnhancedWrappedStream.java | 80 ++ .../cn/hutool/core/stream/CollectorUtil.java | 24 +- .../cn/hutool/core/stream/EasyStream.java | 319 +------- .../cn/hutool/core/stream/EntryStream.java | 22 +- .../core/stream/SimpleStreamWrapper.java | 55 -- ...pper.java => TerminableWrappedStream.java} | 390 +++++----- .../stream/TransformableStreamWrapper.java | 216 ------ .../stream/TransformableWrappedStream.java | 570 ++++++++++++++ ...{StreamWrapper.java => WrappedStream.java} | 91 ++- .../AbstractEnhancedWrappedStreamTest.java | 705 ++++++++++++++++++ .../cn/hutool/core/stream/EasyStreamTest.java | 58 +- .../hutool/core/stream/EntryStreamTest.java | 87 ++- 13 files changed, 1787 insertions(+), 870 deletions(-) delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java rename hutool-core/src/main/java/cn/hutool/core/stream/{TerminableStreamWrapper.java => TerminableWrappedStream.java} (59%) delete mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java create mode 100644 hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java rename hutool-core/src/main/java/cn/hutool/core/stream/{StreamWrapper.java => WrappedStream.java} (87%) create mode 100644 hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java deleted file mode 100644 index 02a9a6789..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedStreamWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.hutool.core.stream; - -import java.util.Objects; -import java.util.stream.Stream; - -/** - * {@link StreamWrapper}的基本实现,用于包装一个已有的流实例, - * 使其支持相对原生{@link Stream}更多的中间操作与终端操作。 - * - * @author huangchengxing - * @see EasyStream - * @see EntryStream - */ -public abstract class AbstractEnhancedStreamWrapper> - implements TerminableStreamWrapper, TransformableStreamWrapper { - - /** - * 原始的流实例 - */ - protected final Stream stream; - - /** - * 获取被包装的元素流实例 - */ - @Override - public Stream stream() { - return stream; - } - - /** - * 创建一个流包装器 - * - * @param stream 包装的流对象 - * @throws NullPointerException 当{@code stream}为{@code null}时抛出 - */ - protected AbstractEnhancedStreamWrapper(Stream stream) { - this.stream = Objects.requireNonNull(stream, "stream must not null"); - } - -} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java new file mode 100644 index 000000000..5fc66d653 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java @@ -0,0 +1,80 @@ +package cn.hutool.core.stream; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * {@link WrappedStream}接口的公共实现,用于包装并增强一个已有的流实例 + * + * @param 流中的元素类型 + * @param {@link AbstractEnhancedWrappedStream}的实现类类型 + * @author huangchengxing + * @see EasyStream + * @see EntryStream + * @since 6.0.0 + */ +public abstract class AbstractEnhancedWrappedStream> + implements TerminableWrappedStream, TransformableWrappedStream { + + /** + * 原始的流实例 + */ + protected final Stream stream; + + /** + * 获取被包装的元素流实例 + */ + @Override + public Stream stream() { + return stream; + } + + /** + * 创建一个流包装器 + * + * @param stream 包装的流对象 + * @throws NullPointerException 当{@code stream}为{@code null}时抛出 + */ + protected AbstractEnhancedWrappedStream(Stream stream) { + this.stream = Objects.requireNonNull(stream, "stream must not null"); + } + + /** + * 获取当前被包装的实例的哈希值 + * + * @return 哈希值 + */ + @Override + public int hashCode() { + return stream.hashCode(); + } + + /** + * 比较被包装的实例是否相等 + * + * @param obj 对象 + * @return 是否相等 + */ + @Override + public boolean equals(Object obj) { + return obj instanceof Stream && stream.equals(obj); + } + + /** + * 将被包装的实例转为字符串 + * + * @return 字符串 + */ + @Override + public String toString() { + return stream.toString(); + } + + /** + * 触发流的执行,这是一个终端操作 + */ + public void exec() { + stream.forEach(t -> {}); + } + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java index 5e1fcefb3..9b9cf5c18 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java @@ -15,7 +15,9 @@ import java.util.stream.Collectors; /** * 可变的汇聚操作{@link Collector} 相关工具封装 * - * @author looly, VampireAchao + * @author looly + * @author VampireAchao + * @author huangchengxing * @since 5.6.7 */ public class CollectorUtil { @@ -243,6 +245,20 @@ public class CollectorUtil { ); } + /** + * 将流转为{@link EntryStream} + * + * @param keyMapper 键的映射方法 + * @param 输入元素类型 + * @param 元素的键类型 + * @return 收集器 + * @since 6.0.0 + */ + public static Collector, EntryStream> toEntryStream( + Function keyMapper) { + return toEntryStream(keyMapper, Function.identity()); + } + /** * 将流转为{@link EntryStream} * @@ -252,6 +268,7 @@ public class CollectorUtil { * @param 元素的键类型 * @param 元素的值类型 * @return 收集器 + * @since 6.0.0 */ public static Collector, EntryStream> toEntryStream( Function keyMapper, Function valueMapper) { @@ -265,6 +282,7 @@ public class CollectorUtil { * * @param 输入元素类型 * @return 收集器 + * @since 6.0.0 */ public static Collector> toEasyStream() { return transform(ArrayList::new, EasyStream::of); @@ -275,7 +293,7 @@ public class CollectorUtil { * 返回的收集器的效果等同于: *
{@code
 	 * 	Collection coll = Stream.of(a, b, c, d)
-	 * 		.collect(Collectors.toCollection(collFactory));
+	 * 		.collect(Collectors.toColl(collFactory));
 	 * 	R result = mapper.apply(coll);
 	 * }
* @@ -285,6 +303,7 @@ public class CollectorUtil { * @param 输入元素类型 * @param 中间收集输入元素的集合类型 * @return 收集器 + * @since 6.0.0 */ public static > Collector transform( Supplier collFactory, Function mapper) { @@ -308,6 +327,7 @@ public class CollectorUtil { * @param 返回值类型 * @param 输入元素类型 * @return 收集器 + * @since 6.0.0 */ public static Collector, R> transform(Function, R> mapper) { return transform(ArrayList::new, mapper); diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java index 6fed9712c..1434cf099 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java @@ -1,21 +1,20 @@ package cn.hutool.core.stream; 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.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Spliterator; import java.util.function.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; /** - *

{@link Stream}的扩展实现,基于原生Stream进行了封装和增强。
+ *

单元素的扩展流实现。基于原生Stream进行了封装和增强。
* 作者经对比了vavr、eclipse-collection、stream-ex以及其他语言的api,结合日常使用习惯,进行封装和拓展 * Stream为集合提供了一些易用api,它让开发人员能使用声明式编程的方式去编写代码。 * @@ -46,10 +45,11 @@ import java.util.stream.StreamSupport; * * @author VampireAchao * @author emptypoint + * @author huangchengxing * @see java.util.stream.Stream * @since 6.0.0 */ -public class EasyStream extends AbstractEnhancedStreamWrapper> { +public class EasyStream extends AbstractEnhancedWrappedStream> { /** * 构造 @@ -69,10 +69,10 @@ public class EasyStream extends AbstractEnhancedStreamWrapper 元素的类型 * @return a stream builder */ - public static FastStreamBuilder builder() { - return new FastStreamBuilder() { + public static Builder builder() { + return new Builder() { private static final long serialVersionUID = 1L; - private final Builder streamBuilder = Stream.builder(); + private final Stream.Builder streamBuilder = Stream.builder(); @Override public void accept(final T t) { @@ -241,20 +241,6 @@ public class EasyStream extends AbstractEnhancedStreamWrapper 返回类型 - * @param mapper 操作 - * @param value 用来匹配的值 - * @return 与 指定操作结果 匹配 指定值 的元素组成的流 - */ - public EasyStream filter(final Function mapper, final R value) { - Objects.requireNonNull(mapper); - return filter(e -> Objects.equals(mapper.apply(e), value)); - } - /** * 返回与指定函数将元素作为参数执行的结果组成的流 * 这是一个无状态中间操作 @@ -265,185 +251,10 @@ public class EasyStream extends AbstractEnhancedStreamWrapper EasyStream map(final Function mapper) { + Objects.requireNonNull(mapper); return new EasyStream<>(stream.map(mapper)); } - /** - * 返回 元素 转换后 并且不为 {@code null} 的 新元素组成的流
- * 这是一个无状态中间操作
- *

{@code
-	 * // 等价于先调用map再调用nonNull
-	 * .map(...).nonNull()...
-	 * }
- * - * @param mapper 指定的函数 - * @param 函数执行后返回的类型 - * @return 新元素组成的流 - */ - public EasyStream mapNonNull(final Function mapper) { - return nonNull().map(mapper).nonNull(); - } - - /** - * 返回与指定函数将元素作为参数执行的结果组成的流,操作带下标,并行流时下标永远为-1 - * 这是一个无状态中间操作 - * - * @param mapper 指定的函数 - * @param 函数执行后返回的类型 - * @return 返回叠加操作后的流 - */ - public EasyStream mapIdx(final BiFunction mapper) { - Objects.requireNonNull(mapper); - if (isParallel()) { - return map(e -> mapper.apply(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return map(e -> mapper.apply(e, index.incrementAndGet())); - } - } - - /** - * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
- * 这是一个无状态中间操作
- * 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: - *
{@code
-	 *     FastStream ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
-	 * }
- * - * @param mapper 操作,返回流 - * @param 拆分后流的元素类型 - * @return 返回叠加拆分操作后的流 - */ - @Override - public EasyStream flatMap(final Function> mapper) { - return new EasyStream<>(stream.flatMap(mapper)); - } - - /** - * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带下标,并行流时下标永远为-1 - * 这是一个无状态中间操作 - * - * @param mapper 操作,返回流 - * @param 拆分后流的元素类型 - * @return 返回叠加拆分操作后的流 - */ - public EasyStream flatMapIdx(final BiFunction> mapper) { - Objects.requireNonNull(mapper); - if (isParallel()) { - return flatMap(e -> mapper.apply(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return flatMap(e -> mapper.apply(e, index.incrementAndGet())); - } - } - - /** - * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素, - * 最后返回所有迭代器的所有元素组成的流
- * 这是一个无状态中间操作
- * 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: - *
{@code
-	 *     FastStream ids = FastStream.of(users).flat(user -> FastStream.of(user.getId(), user.getParentId()));
-	 * }
- * - * @param mapper 操作,返回可迭代对象 - * @param 拆分后流的元素类型 - * @return 返回叠加拆分操作后的流 - */ - public EasyStream flat(final Function> mapper) { - Objects.requireNonNull(mapper); - return flatMap(w -> of(mapper.apply(w))); - } - - /** - * 扩散流操作,可能影响流元素个数,对过滤后的非{@code null}元素执行mapper操作,转换为迭代器, - * 并过滤迭代器中为{@code null}的元素, 返回所有迭代器的所有非空元素组成的流
- * 这是一个无状态中间操作
- * - * @param mapper 操作,返回流 - * @param 拆分后流的元素类型 - * @return 返回叠加拆分操作后的流 - * @see #flat(Function) - * @see #nonNull() - */ - public EasyStream flatNonNull(final Function> mapper) { - return nonNull().flat(mapper).nonNull(); - } - - /** - * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带一个方法,调用该方法可增加元素 - * 这是一个无状态中间操作 - * - * @param mapper 操作,返回流 - * @param 拆分后流的元素类型 - * @return 返回叠加拆分操作后的流 - */ - public EasyStream mapMulti(final BiConsumer> mapper) { - Objects.requireNonNull(mapper); - return flatMap(e -> { - final FastStreamBuilder buffer = EasyStream.builder(); - mapper.accept(e, buffer); - return buffer.build(); - }); - } - - /** - * 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个 - * 这是一个有状态中间操作 - * - * @param 参数类型 - * @param keyExtractor 去重依据 - * @return 一个具有去重特征的流 - */ - public EasyStream distinct(final Function keyExtractor) { - Objects.requireNonNull(keyExtractor); - if (isParallel()) { - final ConcurrentHashMap exists = MapUtil.newConcurrentHashMap(); - // 标记是否出现过null值,用于保留第一个出现的null - // 由于ConcurrentHashMap的key不能为null,所以用此变量来标记 - final AtomicBoolean hasNull = new AtomicBoolean(false); - return of(stream.filter(e -> { - final F key = keyExtractor.apply(e); - if (key == null) { - // 已经出现过null值,跳过该值 - if (hasNull.get()) { - return false; - } - hasNull.set(Boolean.TRUE); - return true; - } else { - // 第一次出现的key返回true - return null == exists.putIfAbsent(key, Boolean.TRUE); - } - })).parallel(); - } else { - final Set exists = new HashSet<>(); - return of(stream.filter(e -> exists.add(keyExtractor.apply(e)))); - } - } - - /** - * 与给定元素组成的流合并,成为新的流 - * - * @param obj 元素 - * @return 流 - */ - public EasyStream push(final T obj) { - return EasyStream.concat(this.stream, of(obj)); - } - - - /** - * 给定元素组成的流与当前流合并,成为新的流 - * - * @param obj 元素 - * @return 流 - */ - public EasyStream unshift(final T obj) { - return EasyStream.concat(of(obj), this.stream); - } - - /** * 根据一个原始的流,返回一个新包装类实例 * @@ -456,16 +267,19 @@ public class EasyStream extends AbstractEnhancedStreamWrapper将集合转换为树,默认用 {@code parentId == null} 作为顶部,内置一个小递归 + * 因为需要在当前传入数据里查找,所以这是一个结束操作
* * @param idGetter id的getter对应的lambda,可以写作 {@code Student::getId} * @param pIdGetter parentId的getter对应的lambda,可以写作 {@code Student::getParentId} * @param childrenSetter children的setter对应的lambda,可以写作{ @code Student::setChildren} * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树 + * @return list 组装好的树
* eg: - * {@code List studentTree = EasyStream.of(students).toTree(Student::getId, Student::getParentId, Student::setChildren) } + *
{@code
+	 * List studentTree = EasyStream.of(students).
+	 * 	toTree(Student::getId, Student::getParentId, Student::setChildren);
+	 * }
*/ public > List toTree(Function idGetter, Function pIdGetter, @@ -483,15 +297,19 @@ public class EasyStream extends AbstractEnhancedStreamWrapper Objects.equals(s.getParentId(),0L) } * @param 此处是id、parentId的泛型限制 - * @return list 组装好的树 + * @return list 组装好的树
* eg: - * {@code List studentTree = EasyStream.of(students).toTree(Student::getId, Student::getParentId, Student::setChildren, Student::getMatchParent) } + *
{@code
+	 * List studentTree = EasyStream.of(students).
+	 * 	.toTree(Student::getId, Student::getParentId, Student::setChildren, Student::getMatchParent);
+	 * }
*/ public > List toTree(Function idGetter, Function pIdGetter, BiConsumer> childrenSetter, Predicate parentPredicate) { + Objects.requireNonNull(parentPredicate); List list = toList(); List parents = EasyStream.of(list).filter(e -> // 此处是为了适配 parentPredicate.test空指针 情况 @@ -516,6 +334,9 @@ public class EasyStream extends AbstractEnhancedStreamWrapper> childrenSetter, Map> pIdValuesMap, List parents) { + Objects.requireNonNull(idGetter); + Objects.requireNonNull(childrenSetter); + Objects.requireNonNull(pIdValuesMap); MutableObj>> recursiveRef = new MutableObj<>(); Consumer> recursive = values -> EasyStream.of(values, isParallel()).forEach(value -> { List children = pIdValuesMap.get(idGetter.apply(value)); @@ -528,89 +349,11 @@ public class EasyStream extends AbstractEnhancedStreamWrapper flatTree(Function> childrenGetter, BiConsumer> childrenSetter) { - MutableObj>> recursiveRef = new MutableObj<>(); - Function> recursive = e -> EasyStream.of(childrenGetter.apply(e)).flat(recursiveRef.get()).unshift(e); - recursiveRef.set(recursive); - return flat(recursive).peek(e -> childrenSetter.accept(e, null)); - } - - /** - * 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流
- * 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分
- * - * @param other 给定的迭代器 - * @param zipper 两个元素的合并器 - * @param 给定的迭代对象类型 - * @param 合并后的结果对象类型 - * @return 合并后的结果对象的流 - */ - public EasyStream zip(final Iterable other, - final BiFunction zipper) { - Objects.requireNonNull(zipper); - 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的值 - final MutableObj key = new MutableObj<>(); - // 保存第二个Spliterator的值 - final MutableObj value = new MutableObj<>(); - // 当两个Spliterator中都还有剩余元素时 - while (keys.tryAdvance(key::set) && values.tryAdvance(value::set)) { - list.add(zipper.apply(key.get(), value.get())); - } - return of(list).parallel(isParallel()).onClose(stream::close); - } - - /** - * 按指定长度切分为双层流 - *

- * 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]] - *

- * - * @param batchSize 指定长度, 正整数 - * @return 切好的流 - */ - public EasyStream> split(final int batchSize) { - final List list = toList(); - final int size = list.size(); - // 指定长度 大于等于 列表长度 - if (size <= batchSize) { - // 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]] - return EasyStream.>of(of(list, isParallel())); - } - return iterate(0, i -> i < size, i -> i + batchSize) - .map(skip -> of(list.subList(skip, Math.min(size, skip + batchSize)), isParallel())) - .parallel(isParallel()) - .onClose(stream::close); - } - - /** - * 按指定长度切分为元素为list的流 - *

- * 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]] - *

- * - * @param batchSize 指定长度, 正整数 - * @return 切好的流 - */ - public EasyStream> splitList(final int batchSize) { - return split(batchSize).map(EasyStream::toList); - } - - public interface FastStreamBuilder extends Consumer, cn.hutool.core.builder.Builder> { + public interface Builder extends Consumer, cn.hutool.core.builder.Builder> { /** * Adds an element to the stream being built. @@ -625,7 +368,7 @@ public class EasyStream extends AbstractEnhancedStreamWrapper */ - default FastStreamBuilder add(final T t) { + default Builder add(final T t) { accept(t); return this; } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java index c9eadd81c..cb030f9df 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java @@ -14,15 +14,16 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; /** - *

参考StreamEx的EntryStream与vavr的Map,是针对键值对对象{@link Map.Entry}特化的增强流实现。
+ *

参考StreamEx的EntryStream与vavr的Map,是针对键值对对象{@link Map.Entry}特化的单元素增强流实现。
* 本身可视为一个元素类型为{@link Map.Entry}的{@link Stream}, * 用于支持流式处理{@link Map}集合中的、或其他键值对类型的数据。 * * @param 键类型 * @param 值类型 * @author huangchengxing + * @since 6.0.0 */ -public class EntryStream extends AbstractEnhancedStreamWrapper, EntryStream> { +public class EntryStream extends AbstractEnhancedWrappedStream, EntryStream> { /** * 默认的空键值对 @@ -222,8 +223,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper nonNull() { + public EntryStream nonNullKeyValue() { return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey()) && ObjUtil.isNotNull(e.getValue())); } @@ -232,7 +232,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper keyNonNull() { + public EntryStream nonNullKey() { return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey())); } @@ -241,7 +241,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper valueNonNull() { + public EntryStream nonNullValue() { return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getValue())); } @@ -298,7 +298,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper append(K key, V value) { + public EntryStream push(K key, V value) { return wrapping(Stream.concat(stream, Stream.of(ofEntry(key, value)))); } @@ -309,7 +309,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper prepend(K key, V value) { + public EntryStream unshift(K key, V value) { return wrapping(Stream.concat(Stream.of(ofEntry(key, value)), stream)); } @@ -319,6 +319,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper append(Iterable> entries) { if (IterUtil.isEmpty(entries)) { return this; @@ -334,6 +335,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper prepend(Iterable> entries) { if (IterUtil.isEmpty(entries)) { return this; @@ -724,7 +726,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper Map.Entry ofEntry(Map.Entry entry) { + static Map.Entry ofEntry(Map.Entry entry) { return ObjUtil.defaultIfNull( entry, e -> ofEntry(e.getKey(), e.getValue()), (Map.Entry)EMPTY_ENTRY ); @@ -733,7 +735,7 @@ public class EntryStream extends AbstractEnhancedStreamWrapper Map.Entry ofEntry(K key, V value) { + static Map.Entry ofEntry(K key, V value) { return new AbstractMap.SimpleImmutableEntry<>(key, value); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java deleted file mode 100644 index 636e54f1f..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/SimpleStreamWrapper.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.hutool.core.stream; - -import java.util.function.Function; -import java.util.stream.Stream; - -/** - * {@link AbstractEnhancedStreamWrapper}的基本实现 - * - * @author huangchengxing - */ -public class SimpleStreamWrapper extends AbstractEnhancedStreamWrapper> { - - /** - * 创建一个流包装器 - * - * @param stream 包装的流对象 - * @throws NullPointerException 当{@code stream}为{@code null}时抛出 - */ - SimpleStreamWrapper(Stream stream) { - super(stream); - } - - /** - * 将一个流包装为{@link SimpleStreamWrapper} - * - * @param source 被包装的流 - * @return 包装后的流 - */ - @Override - public SimpleStreamWrapper wrapping(Stream source) { - return new SimpleStreamWrapper<>(source); - } - - /** - * 将当前流中元素映射为另一类型 - * - * @param mapper 映射方法 - * @return 映射后的流 - */ - @Override - public SimpleStreamWrapper map(Function mapper) { - return new SimpleStreamWrapper<>(stream().map(mapper)); - } - - /** - * 将当前流中元素展开为流,然后返回由这些新流中元素组成的流 - * - * @param mapper 映射方法 - * @return 映射后的流 - */ - @Override - public SimpleStreamWrapper flatMap(Function> mapper) { - return new SimpleStreamWrapper<>(stream().flatMap(mapper)); - } -} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java similarity index 59% rename from hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java rename to hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java index eee30f86f..0e71feb09 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableStreamWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java @@ -9,70 +9,26 @@ import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Collectors; +import java.util.stream.Stream; /** - * {@link StreamWrapper}的扩展,为实现类提供更多终端操作方法 + * {@link WrappedStream}的扩展,用于为实现类提供更多终端操作方法的增强接口, + * 该接口提供的方法,返回值类型都不为{@link Stream}。 * * @param 流中的元素类型 - * @param 链式调用获得的实现类类型 + * @param {@link TerminableWrappedStream}的实现类类型 * @author huangchengxing + * @since 6.0.0 */ -public interface TerminableStreamWrapper> extends StreamWrapper { - - // region ============ join ============ - - /** - * 返回拼接后的字符串 - * - * @return 拼接后的字符串 - */ - default String join() { - return this.join(""); - } - - /** - * 返回拼接后的字符串 - * - * @param delimiter 分隔符 - * @return 拼接后的字符串 - */ - default String join(CharSequence delimiter) { - return this.join(delimiter, "", ""); - } - - /** - * 返回拼接后的字符串 - * - * @param delimiter 分隔符 - * @param prefix 前缀 - * @param suffix 后缀 - * @return 拼接后的字符串 - */ - default String join(CharSequence delimiter, - CharSequence prefix, - CharSequence suffix) { - return stream().map(String::valueOf).collect(Collectors.joining(delimiter, prefix, suffix)); - } - - // endregion +public interface TerminableWrappedStream> extends WrappedStream { // region ============ to collection ============ - /** - * 转换成集合 - * - * @param collectionFactory 集合工厂(可以是集合构造器) - * @param 集合类型 - * @return 集合 - */ - default > C toColl(Supplier collectionFactory) { - return stream().collect(Collectors.toCollection(collectionFactory)); - } - /** * 转换为{@link ArrayList} * * @return 集合 + * @see #toColl(Supplier) */ default List toList() { return this.toColl(ArrayList::new); @@ -82,7 +38,8 @@ public interface TerminableStreamWrapper toUnmodifiableList() { return Collections.unmodifiableList(this.toList()); } @@ -91,7 +48,8 @@ public interface TerminableStreamWrapper toSet() { return this.toColl(HashSet::new); } @@ -100,11 +58,24 @@ public interface TerminableStreamWrapper toUnmodifiableSet() { return Collections.unmodifiableSet(this.toSet()); } + /** + * 转换成集合 + * + * @param collectionFactory 集合工厂(可以是集合构造器) + * @param 集合类型 + * @return 集合 + */ + default > C toColl(Supplier collectionFactory) { + Objects.requireNonNull(collectionFactory); + return stream().collect(Collectors.toCollection(collectionFactory)); + } + // endregion // region ============ to map ============ @@ -115,6 +86,7 @@ public interface TerminableStreamWrapper key类型 * @return map + * @see #toMap(Function, Function, BinaryOperator, Supplier) */ default Map toMap(Function keyMapper) { return this.toMap(keyMapper, Function.identity()); @@ -128,6 +100,7 @@ public interface TerminableStreamWrapper key类型 * @param value类型 * @return map + * @see #toMap(Function, Function, BinaryOperator, Supplier) */ default Map toMap( Function keyMapper, Function valueMapper) { @@ -142,6 +115,7 @@ public interface TerminableStreamWrapper key类型 * @param value类型 * @return map + * @see #toMap(Function, Function, BinaryOperator, Supplier) */ default Map toUnmodifiableMap( Function keyMapper, Function valueMapper) { @@ -157,6 +131,7 @@ public interface TerminableStreamWrapper key类型 * @param value类型 * @return map + * @see #toMap(Function, Function, BinaryOperator, Supplier) */ default Map toMap( Function keyMapper, @@ -174,6 +149,7 @@ public interface TerminableStreamWrapper key类型 * @param value类型 * @return map + * @see #toMap(Function, Function, BinaryOperator, Supplier) */ default Map toUnmodifiableMap( Function keyMapper, @@ -201,6 +177,10 @@ public interface TerminableStreamWrapper valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier) { + Objects.requireNonNull(keyMapper); + Objects.requireNonNull(valueMapper); + Objects.requireNonNull(mergeFunction); + Objects.requireNonNull(mapSupplier); return stream().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)); } @@ -219,6 +199,7 @@ public interface TerminableStreamWrapper */ default Map toZip(Iterable other) { + Objects.requireNonNull(other); // value对象迭代器 final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); if (this.isParallel()) { @@ -235,129 +216,20 @@ public interface TerminableStreamWrapper 实体中的分组依据对应类型,也是Map中key的类型 - * @return map - */ - default Map> group(Function classifier) { - return this.group(classifier, Collectors.toList()); - } - - /** - * 通过给定分组依据进行分组 - * - * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 - * @param downstream 下游操作 - * @param 实体中的分组依据对应类型,也是Map中key的类型 - * @param 下游操作对应返回类型,也是Map中value的类型 - * @param 下游操作在进行中间操作时对应类型 - * @return map - */ - default Map group( - Function classifier, Collector downstream) { - return this.group(classifier, HashMap::new, downstream); - } - - /** - * 通过给定分组依据进行分组 - * - * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 - * @param mapFactory 提供的map - * @param downstream 下游操作 - * @param 实体中的分组依据对应类型,也是Map中key的类型 - * @param 下游操作对应返回类型,也是Map中value的类型 - * @param 下游操作在进行中间操作时对应类型 - * @param 最后返回结果Map类型 - * @return map - * @see CollectorUtil#groupingBy(Function, Supplier, Collector) - */ - default > M group( - Function classifier, - Supplier mapFactory, - Collector downstream) { - return stream().collect(CollectorUtil.groupingBy(classifier, mapFactory, downstream)); - } - - /** - * 根据给定判断条件分组 - * - * @param predicate 判断条件 - * @return map - */ - default Map> partition(Predicate predicate) { - return this.partition(predicate, ArrayList::new); - } - - /** - * 根据给定判断条件分组 - * - * @param predicate 判断条件 - * @param collFactory 提供的集合 - * @return map - */ - default > Map partition(Predicate predicate, Supplier collFactory) { - return this.partition(predicate, Collectors.toCollection(collFactory)); - } - - /** - * 根据给定判断条件分组 - * - * @param predicate 判断条件 - * @param downstream 下游操作 - * @param 返回值类型 - * @return map - */ - default Map partition(Predicate predicate, Collector downstream) { - return stream().collect(Collectors.partitioningBy(predicate, downstream)); - } - - // endregion - - // region ============ foreach ============ + // region ============ to optional ============ /** - * 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1 - * 这是一个终端操作 + * 将当前流转为另一对象。用于提供针对流本身而非流中元素的操作 * - * @param action 操作 + * @param 转换类型 + * @param transform 转换 + * @return 转换后的流 */ - default void forEachIdx(final BiConsumer action) { - Objects.requireNonNull(action); - if (isParallel()) { - stream().forEach(e -> action.accept(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream().forEach(e -> action.accept(e, index.incrementAndGet())); - } + default Optional transform(final Function transform) { + Objects.requireNonNull(transform); + return Optional.ofNullable(transform.apply(wrapping(this))); } - /** - * 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1 - * 这是一个终端操作 - * - * @param action 操作 - */ - default void forEachOrderedIdx(final BiConsumer action) { - Objects.requireNonNull(action); - if (isParallel()) { - stream().forEachOrdered(e -> action.accept(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - stream().forEachOrdered(e -> action.accept(e, index.incrementAndGet())); - } - } - - // endregion - - // region ============ find & get ============ - - - /** * 获取与给定断言匹配的第一个元素 * @@ -365,6 +237,7 @@ public interface TerminableStreamWrapper findFirst(final Predicate predicate) { + Objects.requireNonNull(predicate); return stream().filter(predicate).findFirst(); } @@ -377,9 +250,9 @@ public interface TerminableStreamWrapper predicate) { Objects.requireNonNull(predicate); if (isParallel()) { - return NOT_FOUND_INDEX; + return NOT_FOUND_ELEMENT_INDEX; } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); + final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); stream().filter(e -> { index.increment(); return predicate.test(e); @@ -425,9 +298,9 @@ public interface TerminableStreamWrapper predicate) { Objects.requireNonNull(predicate); if (isParallel()) { - return NOT_FOUND_INDEX; + return NOT_FOUND_ELEMENT_INDEX; } else { - final MutableInt idxRef = new MutableInt(NOT_FOUND_INDEX); + final MutableInt idxRef = new MutableInt(NOT_FOUND_ELEMENT_INDEX); forEachIdx((e, i) -> { if (predicate.test(e)) { idxRef.set(i); @@ -450,7 +323,7 @@ public interface TerminableStreamWrapper 实体中的分组依据对应类型,也是Map中key的类型 + * @return map + * @see #group(Function, Supplier, Collector) + */ + default Map> group(Function classifier) { + return this.group(classifier, Collectors.toList()); + } + + /** + * 通过给定分组依据进行分组 + * + * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 + * @param downstream 下游操作 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @param 下游操作对应返回类型,也是Map中value的类型 + * @param 下游操作在进行中间操作时对应类型 + * @return map + * @see #group(Function, Supplier, Collector) + */ + default Map group( + Function classifier, Collector downstream) { + return this.group(classifier, HashMap::new, downstream); + } + + /** + * 通过给定分组依据进行分组 + * + * @param classifier 分组依据,得到的键为{@code null}时不会抛出异常 + * @param mapFactory 提供的map + * @param downstream 下游操作 + * @param 实体中的分组依据对应类型,也是Map中key的类型 + * @param 下游操作对应返回类型,也是Map中value的类型 + * @param 下游操作在进行中间操作时对应类型 + * @param 最后返回结果Map类型 + * @return map + * @see CollectorUtil#groupingBy(Function, Supplier, Collector) + */ + default > M group( + Function classifier, + Supplier mapFactory, + Collector downstream) { + Objects.requireNonNull(classifier); + Objects.requireNonNull(mapFactory); + Objects.requireNonNull(downstream); + return stream().collect(CollectorUtil.groupingBy(classifier, mapFactory, downstream)); + } + + /** + * 根据给定判断条件分组 + * + * @param predicate 判断条件 + * @return map + * @see #partitioning(Predicate, Collector) + */ + default Map> partitioning(Predicate predicate) { + return this.partitioning(predicate, ArrayList::new); + } + + /** + * 根据给定判断条件分组 + * + * @param predicate 判断条件 + * @param collFactory 提供的集合 + * @return map + * @see #partitioning(Predicate, Collector) + */ + default > Map partitioning(Predicate predicate, Supplier collFactory) { + return this.partitioning(predicate, Collectors.toCollection(collFactory)); + } + + /** + * 根据给定判断条件分组 + * + * @param predicate 判断条件 + * @param downstream 下游操作 + * @param 返回值类型 + * @return map + */ + default Map partitioning(Predicate predicate, Collector downstream) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(downstream); + return stream().collect(Collectors.partitioningBy(predicate, downstream)); + } + + // endregion + + // region ============ foreach ============ + + /** + * 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1 + * 这是一个终端操作 + * + * @param action 操作 + */ + default void forEachIdx(final BiConsumer action) { + Objects.requireNonNull(action); + if (isParallel()) { + stream().forEach(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); + stream().forEach(e -> action.accept(e, index.incrementAndGet())); + } + } + + /** + * 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1 + * 这是一个终端操作 + * + * @param action 操作 + */ + default void forEachOrderedIdx(final BiConsumer action) { + Objects.requireNonNull(action); + if (isParallel()) { + stream().forEachOrdered(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); + stream().forEachOrdered(e -> action.accept(e, index.incrementAndGet())); + } + } + + // endregion + } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java deleted file mode 100644 index 10ad264bf..000000000 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableStreamWrapper.java +++ /dev/null @@ -1,216 +0,0 @@ -package cn.hutool.core.stream; - -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Console; -import cn.hutool.core.lang.mutable.MutableInt; -import cn.hutool.core.util.ArrayUtil; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * {@link StreamWrapper}的扩展,为实现类提供更多中间操作方法 - * - * @param 流中的元素类型 - * @param 链式调用获得的实现类类型 - * @author huangchengxing - */ -public interface TransformableStreamWrapper> extends StreamWrapper { - - /** - * 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1 - * 这是一个无状态中间操作 - * - * @param predicate 断言 - * @return 返回叠加过滤操作后的流 - */ - default I filterIdx(final BiPredicate predicate) { - Objects.requireNonNull(predicate); - if (isParallel()) { - return filter(e -> predicate.test(e, NOT_FOUND_INDEX)); - } else { - final MutableInt index = new MutableInt(NOT_FOUND_INDEX); - return filter(e -> predicate.test(e, index.incrementAndGet())); - } - } - - /** - * 过滤掉空元素 - * - * @return 过滤后的流 - */ - default I nonNull() { - return filter(Objects::nonNull); - } - - /** - * 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1 - * 这是一个无状态中间操作 - * @param action 指定的函数 - * @return 返回叠加操作后的FastStream - * @apiNote 该方法存在的意义主要是用来调试 - * 当你需要查看经过操作管道某处的元素和下标,可以执行以下操作: - *

{@code
-	 *     Stream.of("one", "two", "three", "four")
-	 * 				.filter(e -> e.length() > 3)
-	 * 				.peekIdx((e,i) -> System.out.println("Filtered value: " + e + " Filtered idx:" + i))
-	 * 				.map(String::toUpperCase)
-	 * 				.peekIdx((e,i) -> System.out.println("Mapped value: " + e + " Mapped idx:" + i))
-	 * 				.collect(Collectors.toList());
-	 * }
- */ - default I peekIdx(BiConsumer action) { - Objects.requireNonNull(action); - if (isParallel()) { - return peek(e -> action.accept(e, NOT_FOUND_INDEX)); - } else { - AtomicInteger index = new AtomicInteger(NOT_FOUND_INDEX); - return peek(e -> action.accept(e, index.incrementAndGet())); - } - } - - /** - * 返回叠加调用{@link Console#log(Object)}打印出结果的流 - * - * @return 返回叠加操作后的FastStream - */ - default I log() { - return peek(Console::log); - } - - /** - * 反转顺序 - * - * @return 反转元素顺序 - */ - @SuppressWarnings("unchecked") - default I reverse() { - final T[] array = (T[]) toArray(); - ArrayUtil.reverse(array); - return wrapping(Stream.of(array)).parallel(isParallel()); - } - - /** - * 更改流的并行状态 - * - * @param parallel 是否并行 - * @return 流 - */ - default I parallel(final boolean parallel) { - return parallel ? parallel() : sequential(); - } - - /** - * 与给定元素组成的流合并,成为新的流 - * - * @param obj 元素 - * @return 流 - */ - @SuppressWarnings("unchecked") - default I push(final T... obj) { - Stream result = stream(); - if (ArrayUtil.isNotEmpty(obj)) { - result = Stream.concat(stream(), Stream.of(obj)); - } - return wrapping(result); - } - - /** - * 给定元素组成的流与当前流合并,成为新的流 - * - * @param obj 元素 - * @return 流 - */ - default I unshift(final T... obj) { - Stream result = stream(); - if (ArrayUtil.isNotEmpty(obj)) { - result = Stream.concat(Stream.of(obj), stream()); - } - return wrapping(result); - } - - /** - * 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。 - * 类似js的splice函数 - * - * @param start 起始下标 - * @param deleteCount 删除个数,正整数 - * @param items 放入值 - * @return 操作后的流 - */ - default I splice(final int start, final int deleteCount, final T... items) { - final List elements = stream().collect(Collectors.toList()); - return wrapping(ListUtil.splice(elements, start, deleteCount, items).stream()) - .parallel(isParallel()); - } - - /** - * 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素 - *

与 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 与指定断言匹配的元素组成的流 - */ - default I takeWhile(final Predicate predicate) { - Objects.requireNonNull(predicate); - return wrapping(StreamUtil.takeWhile(stream(), 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 剩余元素组成的流 - */ - default I dropWhile(final Predicate predicate) { - Objects.requireNonNull(predicate); - return wrapping(StreamUtil.dropWhile(stream(), predicate)); - } - - /** - * 将当前流转为另一对象。用于提供针对流本身而非流中元素的操作 - * - * @param 转换类型 - * @param transform 转换 - * @return 转换后的流 - */ - default Optional transform(final Function transform) { - Assert.notNull(transform, "transform must not null"); - return Optional.ofNullable(transform.apply(wrapping(this))); - } - -} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java new file mode 100644 index 000000000..f21a1dc0e --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java @@ -0,0 +1,570 @@ +package cn.hutool.core.stream; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.collection.iter.IterUtil; +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.util.ArrayUtil; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * {@link WrappedStream}的扩展,用于为实现类提供更多中间操作方法的增强接口, + * 该接口提供的方法,返回值类型都为{@link Stream}。 + * + * @param 流中的元素类型 + * @param {@link TransformableWrappedStream}的实现类类型 + * @author huangchengxing + * @since 6.0.0 + */ +public interface TransformableWrappedStream> extends WrappedStream { + + /** + * 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流
+ * 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分
+ * + * @param other 给定的迭代器 + * @param zipper 两个元素的合并器 + * @param 给定的迭代对象类型 + * @param 合并后的结果对象类型 + * @return 合并后的结果对象的流 + */ + default EasyStream zip(final Iterable other, + final BiFunction zipper) { + Objects.requireNonNull(zipper); + 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的值 + final MutableObj key = new MutableObj<>(); + // 保存第二个Spliterator的值 + final MutableObj value = new MutableObj<>(); + // 当两个Spliterator中都还有剩余元素时 + while (keys.tryAdvance(key::set) && values.tryAdvance(value::set)) { + list.add(zipper.apply(key.get(), value.get())); + } + return EasyStream.of(list).parallel(isParallel()).onClose(stream()::close); + } + + /** + * 按指定长度切分为双层流 + *

+ * 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]] + *

+ * + * @param batchSize 指定长度, 正整数 + * @return 切好的流 + */ + default EasyStream> split(final int batchSize) { + final List list = this.collect(Collectors.toList()); + final int size = list.size(); + // 指定长度 大于等于 列表长度 + if (size <= batchSize) { + // 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]] + return EasyStream.>of(EasyStream.of(list, isParallel())); + } + return EasyStream.iterate(0, i -> i < size, i -> i + batchSize) + .map(skip -> EasyStream.of(list.subList(skip, Math.min(size, skip + batchSize)), isParallel())) + .parallel(isParallel()) + .onClose(stream()::close); + } + + /** + * 按指定长度切分为元素为list的流 + *

+ * 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]] + *

+ * + * @param batchSize 指定长度, 正整数 + * @return 切好的流 + */ + default EasyStream> splitList(final int batchSize) { + return split(batchSize).map(EasyStream::toList); + } + + /** + * 将当前流转为键值对流 + * + * @param keyMapper 键的映射方法 + * @param valueMapper 值的映射方法 + * @param 键类型 + * @param 值类型 + * @return {@link EntryStream}实例 + */ + default EntryStream toEntries(Function keyMapper, Function valueMapper) { + Objects.requireNonNull(keyMapper); + Objects.requireNonNull(valueMapper); + return new EntryStream<>(map(t -> EntryStream.ofEntry(keyMapper.apply(t), valueMapper.apply(t)))); + } + + /** + * 将当前流转为键值对流 + * + * @param keyMapper 键的映射方法 + * @param 键类型 + * @return {@link EntryStream}实例 + */ + default EntryStream toEntries(Function keyMapper) { + return toEntries(keyMapper, Function.identity()); + } + + // region ============ generic ============ + + /** + * 反转顺序 + * + * @return 反转元素顺序 + */ + @SuppressWarnings("unchecked") + default S reverse() { + final T[] array = (T[]) toArray(); + ArrayUtil.reverse(array); + return wrapping(Stream.of(array)).parallel(isParallel()); + } + + /** + * 更改流的并行状态 + * + * @param parallel 是否并行 + * @return 流 + */ + default S parallel(final boolean parallel) { + return parallel ? parallel() : sequential(); + } + + /** + * 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。 + * 类似js的splice函数 + * + * @param start 起始下标 + * @param deleteCount 删除个数,正整数 + * @param items 放入值 + * @return 操作后的流 + */ + default S splice(final int start, final int deleteCount, final T... items) { + final List elements = stream().collect(Collectors.toList()); + return wrapping(ListUtil.splice(elements, start, deleteCount, items).stream()) + .parallel(isParallel()); + } + + /** + *

遍历流中与断言匹配的元素,当遇到第一个不匹配的元素时终止,返回由匹配的元素组成的流。
+ * eg: + *

{@code
+	 * EasyStream.of(1, 2, 3, 4, 5)
+	 * 	.takeWhile(i -> Objects.equals(3, i)) // 获取元素,一直到遇到第一个3为止
+	 * 	.toList(); // = [1, 2]
+	 * }
+ * + *

与{@code JDK9}中的{@code takeWhile}方法不太一样,此操作为顺序且有状态的中间操作。 + * 即使在并行流中,该操作仍然是顺序执行的,并且不影响后续的并行操作: + *

{@code
+	 * EasyStream.iterate(1, i -> i + 1)
+	 * 	.parallel()
+	 * 	.takeWhile(e -> e < 50) // 顺序执行
+	 * 	.map(e -> e + 1) // 并发
+	 * 	.map(String::valueOf) // 并发
+	 * 	.toList();
+	 * }
+ * 若非必要,不推荐在并行流中进行该操作。 + * + * @param predicate 断言 + * @return 与指定断言匹配的元素组成的流 + */ + default S takeWhile(final Predicate predicate) { + Objects.requireNonNull(predicate); + return wrapping(StreamUtil.takeWhile(stream(), predicate)); + } + + /** + * <

删除流中与断言匹配的元素,当遇到第一个不匹配的元素时终止,返回由剩余不匹配的元素组成的流。
+ * eg: + *

{@code
+	 * EasyStream.of(1, 2, 3, 4, 5)
+	 * 	.dropWhile(i -> !Objects.equals(3, i)) // 删除不为3的元素,一直到遇到第一个3为止
+	 * 	.toList(); // = [3, 4, 5]
+	 * }
+ * + *

与{@code JDK9}中的{@code dropWhile}方法不太一样,此操作为顺序且有状态的中间操作。 + * 即使在并行流中,该操作仍然是顺序执行的,并且不影响后续的并行操作: + *

{@code
+	 * EasyStream.iterate(1, i -> i + 1)
+	 * 	.parallel()
+	 * 	.dropWhile(e -> e < 50) // 顺序执行
+	 * 	.map(e -> e + 1) // 并发
+	 * 	.map(String::valueOf) // 并发
+	 * 	.toList();
+	 * }
+ * 若非必要,不推荐在并行流中进行该操作。 + * + * @param predicate 断言 + * @return 剩余元素组成的流 + */ + default S dropWhile(final Predicate predicate) { + Objects.requireNonNull(predicate); + return wrapping(StreamUtil.dropWhile(stream(), predicate)); + } + + /** + * 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个 + * 这是一个有状态中间操作 + * + * @param 参数类型 + * @param keyExtractor 去重依据 + * @return 一个具有去重特征的流 + */ + default EasyStream distinct(final Function keyExtractor) { + Objects.requireNonNull(keyExtractor); + if (isParallel()) { + final ConcurrentHashMap exists = MapUtil.newConcurrentHashMap(); + // 标记是否出现过null值,用于保留第一个出现的null + // 由于ConcurrentHashMap的key不能为null,所以用此变量来标记 + final AtomicBoolean hasNull = new AtomicBoolean(false); + return EasyStream.of(stream().filter(e -> { + final F key = keyExtractor.apply(e); + if (key == null) { + // 已经出现过null值,跳过该值 + if (hasNull.get()) { + return false; + } + hasNull.set(Boolean.TRUE); + return true; + } else { + // 第一次出现的key返回true + return null == exists.putIfAbsent(key, Boolean.TRUE); + } + })).parallel(); + } else { + final Set exists = new HashSet<>(); + return EasyStream.of(stream().filter(e -> exists.add(keyExtractor.apply(e)))); + } + } + + // endregion + + // region ============ peek ============ + + /** + * 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1 + * 这是一个无状态中间操作 + * @param action 指定的函数 + * @return 返回叠加操作后的FastStream + * @apiNote 该方法存在的意义主要是用来调试 + * 当你需要查看经过操作管道某处的元素和下标,可以执行以下操作: + *
{@code
+	 *     Stream.of("one", "two", "three", "four")
+	 * 				.filter(e -> e.length() > 3)
+	 * 				.peekIdx((e,i) -> System.out.println("Filtered value: " + e + " Filtered idx:" + i))
+	 * 				.map(String::toUpperCase)
+	 * 				.peekIdx((e,i) -> System.out.println("Mapped value: " + e + " Mapped idx:" + i))
+	 * 				.collect(Collectors.toList());
+	 * }
+ */ + default S peekIdx(BiConsumer action) { + Objects.requireNonNull(action); + if (isParallel()) { + return peek(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); + } else { + AtomicInteger index = new AtomicInteger(NOT_FOUND_ELEMENT_INDEX); + return peek(e -> action.accept(e, index.incrementAndGet())); + } + } + + /** + * 返回叠加调用{@link Console#log(Object)}打印出结果的流 + * + * @return 返回叠加操作后的FastStream + */ + default S log() { + return peek(Console::log); + } + + // endregion + + // region ============ concat ============ + + /** + * 与给定元素组成的流合并,成为新的流 + * + * @param obj 元素 + * @return 流 + */ + @SuppressWarnings("unchecked") + default S push(final T... obj) { + Stream result = stream(); + if (ArrayUtil.isNotEmpty(obj)) { + result = Stream.concat(stream(), Stream.of(obj)); + } + return wrapping(result); + } + + /** + * 给定元素组成的流与当前流合并,成为新的流 + * + * @param obj 元素 + * @return 流 + */ + default S unshift(final T... obj) { + Stream result = stream(); + if (ArrayUtil.isNotEmpty(obj)) { + result = Stream.concat(Stream.of(obj), stream()); + } + return wrapping(result); + } + + /** + * 将输入元素转为流,返回一个前半段为当前流,后半段为新流的新实例 + * + * @param iterable 集合 + * @return {@link EntryStream}实例 + */ + default S append(Iterable iterable) { + if (IterUtil.isEmpty(iterable)) { + return wrapping(this); + } + final Stream contacted = StreamSupport.stream(iterable.spliterator(), isParallel()); + return wrapping(Stream.concat(this, contacted)); + } + + /** + * 将输入元素转为流,返回一个前半段为新流,后半段为当前流的新实例 + * + * @param iterable 集合 + * @return {@link EntryStream}实例 + */ + default S prepend(Iterable iterable) { + if (IterUtil.isEmpty(iterable)) { + return wrapping(this); + } + final Stream contacted = StreamSupport.stream(iterable.spliterator(), isParallel()); + return wrapping(Stream.concat(contacted, this)); + } + + // endregion + + // region ============ filter ============ + + /** + * 过滤掉空元素 + * + * @return 过滤后的流 + */ + default S nonNull() { + return filter(Objects::nonNull); + } + + /** + * 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1 + * 这是一个无状态中间操作 + * + * @param predicate 断言 + * @return 返回叠加过滤操作后的流 + */ + default S filterIdx(final BiPredicate predicate) { + Objects.requireNonNull(predicate); + if (isParallel()) { + return filter(e -> predicate.test(e, NOT_FOUND_ELEMENT_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); + return filter(e -> predicate.test(e, index.incrementAndGet())); + } + } + + /** + * 过滤元素,返回与 指定操作结果 匹配 指定值 的元素组成的流 + * 这是一个无状态中间操作 + * + * @param 返回类型 + * @param mapper 操作 + * @param value 用来匹配的值 + * @return 与 指定操作结果 匹配 指定值 的元素组成的流 + */ + default S filter(final Function mapper, final R value) { + Objects.requireNonNull(mapper); + return filter(e -> Objects.equals(mapper.apply(e), value)); + } + + // endregion + + // region ============ flat ============ + + /** + * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
+ * 这是一个无状态中间操作
+ * 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: + *
{@code
+	 *     EasyStream ids = EasyStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
+	 * }
+ * + * @param mapper 操作,返回流 + * @param 拆分后流的元素类型 + * @return 返回叠加拆分操作后的流 + */ + @Override + default EasyStream flatMap(final Function> mapper) { + Objects.requireNonNull(mapper); + return new EasyStream<>(stream().flatMap(mapper)); + } + + /** + * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带下标,并行流时下标永远为-1 + * 这是一个无状态中间操作 + * + * @param mapper 操作,返回流 + * @param 拆分后流的元素类型 + * @return 返回叠加拆分操作后的流 + */ + default EasyStream flatMapIdx(final BiFunction> mapper) { + Objects.requireNonNull(mapper); + if (isParallel()) { + return flatMap(e -> mapper.apply(e, NOT_FOUND_ELEMENT_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); + return flatMap(e -> mapper.apply(e, index.incrementAndGet())); + } + } + + /** + * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素, + * 最后返回所有迭代器的所有元素组成的流
+ * 这是一个无状态中间操作
+ * 例如,将users里所有user的id和parentId组合在一起,形成一个新的流: + *
{@code
+	 *     EasyStream ids = EasyStream.of(users).flat(user -> FastStream.of(user.getId(), user.getParentId()));
+	 * }
+ * + * @param mapper 操作,返回可迭代对象 + * @param 拆分后流的元素类型 + * @return 返回叠加拆分操作后的流 + */ + default EasyStream flat(final Function> mapper) { + Objects.requireNonNull(mapper); + return flatMap(w -> EasyStream.of(mapper.apply(w))); + } + + /** + * 扩散流操作,可能影响流元素个数,对过滤后的非{@code null}元素执行mapper操作,转换为迭代器, + * 并过滤迭代器中为{@code null}的元素, 返回所有迭代器的所有非空元素组成的流
+ * 这是一个无状态中间操作
+ * + * @param mapper 操作,返回流 + * @param 拆分后流的元素类型 + * @return 返回叠加拆分操作后的流 + * @see #flat(Function) + * @see #nonNull() + */ + default EasyStream flatNonNull(final Function> mapper) { + return nonNull().flat(mapper).nonNull(); + } + + /** + * 将树递归扁平化为集合,内置一个小递归 + * 这是一个无状态中间操作
+ * eg: + *
{@code
+	 * List students = EasyStream.of(studentTree)
+	 * 	.flatTree(Student::getChildren, Student::setChildren)
+	 * 	.toList();
+	 * }
+ * + * @param childrenGetter 获取子节点的lambda,可以写作 {@code Student::getChildren} + * @param childrenSetter 设置子节点的lambda,可以写作 {@code Student::setChildren} + * @return EasyStream 一个流 + */ + default S flatTree(Function> childrenGetter, BiConsumer> childrenSetter) { + Objects.requireNonNull(childrenGetter); + Objects.requireNonNull(childrenSetter); + MutableObj>> recursiveRef = new MutableObj<>(); + Function> recursive = e -> EasyStream.of(childrenGetter.apply(e)) + .flat(recursiveRef.get()) + .unshift(e); + recursiveRef.set(recursive); + return wrapping(flatMap(recursive).peek(e -> childrenSetter.accept(e, null))); + } + + // endregion + + // region ============ map ============ + + /** + * 返回与指定函数将元素作为参数执行的结果组成的流 + * 这是一个无状态中间操作 + * + * @param mapper 指定的函数 + * @param 函数执行后返回的类型 + * @return 返回叠加操作后的流 + */ + @Override + default EasyStream map(final Function mapper) { + Objects.requireNonNull(mapper); + return new EasyStream<>(stream().map(mapper)); + } + + /** + * 返回 元素 转换后 并且不为 {@code null} 的 新元素组成的流
+ * 这是一个无状态中间操作
+ *
{@code
+	 * // 等价于先调用map再调用nonNull
+	 * .nonNull().map(...).nonNull()...
+	 * }
+ * + * @param mapper 指定的函数 + * @param 函数执行后返回的类型 + * @return 新元素组成的流 + */ + default EasyStream mapNonNull(final Function mapper) { + Objects.requireNonNull(mapper); + return new EasyStream<>(nonNull().map(mapper).nonNull()); + } + + /** + * 返回与指定函数将元素作为参数执行的结果组成的流,操作带下标,并行流时下标永远为-1 + * 这是一个无状态中间操作 + * + * @param mapper 指定的函数 + * @param 函数执行后返回的类型 + * @return 返回叠加操作后的流 + */ + default EasyStream mapIdx(final BiFunction mapper) { + Objects.requireNonNull(mapper); + if (isParallel()) { + return map(e -> mapper.apply(e, NOT_FOUND_ELEMENT_INDEX)); + } else { + final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); + return map(e -> mapper.apply(e, index.incrementAndGet())); + } + } + + /** + * 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带一个方法,调用该方法可增加元素 + * 这是一个无状态中间操作 + * + * @param mapper 操作,返回流 + * @param 拆分后流的元素类型 + * @return 返回叠加拆分操作后的流 + */ + default EasyStream mapMulti(final BiConsumer> mapper) { + Objects.requireNonNull(mapper); + return flatMap(e -> { + final EasyStream.Builder buffer = EasyStream.builder(); + mapper.accept(e, buffer); + return buffer.build(); + }); + } + + // endregion + +} diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java b/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java similarity index 87% rename from hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java rename to hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java index 8c6519f66..545797323 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/StreamWrapper.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java @@ -1,41 +1,33 @@ package cn.hutool.core.stream; -import cn.hutool.core.util.ObjUtil; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.Optional; -import java.util.Spliterator; +import java.util.*; import java.util.function.*; import java.util.stream.*; /** - *

表示一个用于增强原始{@link Stream}对象的包装器,当调用{@link Stream}中的方法时, - * 将会代理到被包装的原始流对象,并返回指定的包装器实例。 + *

{@link Stream}实例的包装器,用于增强原始的{@link Stream},提供一些额外的中间与终端操作
+ * 默认提供两个可用实现: + *

    + *
  • {@link EasyStream}:针对单元素的通用增强流实现;
  • + *
  • {@link EntryStream}:针对键值对类型元素的增强流实现;
  • + *
* * @param 流中的元素类型 - * @param 链式调用获得的实现类类型 + * @param {@link WrappedStream}的实现类类型 * @author huangchengxing - * @see TerminableStreamWrapper - * @see TransformableStreamWrapper - * @see AbstractEnhancedStreamWrapper + * @see TerminableWrappedStream + * @see TransformableWrappedStream + * @see AbstractEnhancedWrappedStream + * @see EasyStream + * @see EntryStream + * @since 6.0.0 */ -public interface StreamWrapper> extends Stream, Iterable { +public interface WrappedStream> extends Stream, Iterable { /** * 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标 */ - int NOT_FOUND_INDEX = -1; - - /** - * 将一个流包装为简单增强流,若{@code stream}为{@code null}则默认返回一个空串行流 - * - * @param stream 被包装的流 - * @return {@link SimpleStreamWrapper}实例 - */ - static SimpleStreamWrapper create(Stream stream) { - return new SimpleStreamWrapper<>(ObjUtil.defaultIfNull(stream, Stream.empty())); - } + int NOT_FOUND_ELEMENT_INDEX = -1; /** * 获取被包装的原始流 @@ -48,9 +40,9 @@ public interface StreamWrapper> extends Stream< * 将一个原始流包装为指定类型的增强流 * * @param source 被包装的流 - * @return I + * @return S */ - I wrapping(Stream source); + S wrapping(Stream source); /** * 过滤元素,返回与指定断言匹配的元素组成的流 @@ -60,7 +52,8 @@ public interface StreamWrapper> extends Stream< * @return 返回叠加过滤操作后的流 */ @Override - default I filter(Predicate predicate) { + default S filter(Predicate predicate) { + Objects.requireNonNull(predicate); return wrapping(stream().filter(predicate)); } @@ -73,6 +66,7 @@ public interface StreamWrapper> extends Stream< */ @Override default IntStream mapToInt(ToIntFunction mapper) { + Objects.requireNonNull(mapper); return stream().mapToInt(mapper); } @@ -85,6 +79,7 @@ public interface StreamWrapper> extends Stream< */ @Override default LongStream mapToLong(ToLongFunction mapper) { + Objects.requireNonNull(mapper); return stream().mapToLong(mapper); } @@ -97,6 +92,7 @@ public interface StreamWrapper> extends Stream< */ @Override default DoubleStream mapToDouble(ToDoubleFunction mapper) { + Objects.requireNonNull(mapper); return stream().mapToDouble(mapper); } @@ -109,6 +105,7 @@ public interface StreamWrapper> extends Stream< */ @Override default IntStream flatMapToInt(Function mapper) { + Objects.requireNonNull(mapper); return stream().flatMapToInt(mapper); } @@ -121,6 +118,7 @@ public interface StreamWrapper> extends Stream< */ @Override default LongStream flatMapToLong(Function mapper) { + Objects.requireNonNull(mapper); return stream().flatMapToLong(mapper); } @@ -133,6 +131,7 @@ public interface StreamWrapper> extends Stream< */ @Override default DoubleStream flatMapToDouble(Function mapper) { + Objects.requireNonNull(mapper); return stream().flatMapToDouble(mapper); } @@ -143,7 +142,7 @@ public interface StreamWrapper> extends Stream< * @return 一个具有去重特征的流 */ @Override - default I distinct() { + default S distinct() { return wrapping(stream().distinct()); } @@ -156,7 +155,7 @@ public interface StreamWrapper> extends Stream< * @return 一个元素按自然顺序排序的流 */ @Override - default I sorted() { + default S sorted() { return wrapping(stream().sorted()); } @@ -170,7 +169,8 @@ public interface StreamWrapper> extends Stream< * @return 一个元素按指定的Comparator排序的流 */ @Override - default I sorted(Comparator comparator) { + default S sorted(Comparator comparator) { + Objects.requireNonNull(comparator); return wrapping(stream().sorted(comparator)); } @@ -192,7 +192,8 @@ public interface StreamWrapper> extends Stream< * } */ @Override - default I peek(Consumer action) { + default S peek(Consumer action) { + Objects.requireNonNull(action); return wrapping(stream().peek(action)); } @@ -204,7 +205,7 @@ public interface StreamWrapper> extends Stream< * @return 截取后的流 */ @Override - default I limit(long maxSize) { + default S limit(long maxSize) { return wrapping(stream().limit(maxSize)); } @@ -216,7 +217,7 @@ public interface StreamWrapper> extends Stream< * @return 丢弃前面n个元素后的剩余元素组成的流 */ @Override - default I skip(long n) { + default S skip(long n) { return wrapping(stream().skip(n)); } @@ -228,6 +229,7 @@ public interface StreamWrapper> extends Stream< */ @Override default void forEach(Consumer action) { + Objects.requireNonNull(action); stream().forEach(action); } @@ -239,6 +241,7 @@ public interface StreamWrapper> extends Stream< */ @Override default void forEachOrdered(Consumer action) { + Objects.requireNonNull(action); stream().forEachOrdered(action); } @@ -264,6 +267,7 @@ public interface StreamWrapper> extends Stream< */ @Override default A[] toArray(IntFunction generator) { + Objects.requireNonNull(generator); return stream().toArray(generator); } @@ -289,6 +293,7 @@ public interface StreamWrapper> extends Stream< */ @Override default T reduce(T identity, BinaryOperator accumulator) { + Objects.requireNonNull(accumulator); return stream().reduce(identity, accumulator); } @@ -324,6 +329,7 @@ public interface StreamWrapper> extends Stream< */ @Override default Optional reduce(BinaryOperator accumulator) { + Objects.requireNonNull(accumulator); return stream().reduce(accumulator); } @@ -341,6 +347,8 @@ public interface StreamWrapper> extends Stream< */ @Override default U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) { + Objects.requireNonNull(accumulator); + Objects.requireNonNull(combiner); return stream().reduce(identity, accumulator, combiner); } @@ -359,6 +367,9 @@ public interface StreamWrapper> extends Stream< */ @Override default R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) { + Objects.requireNonNull(supplier); + Objects.requireNonNull(accumulator); + Objects.requireNonNull(combiner); return stream().collect(supplier, accumulator, combiner); } @@ -373,6 +384,7 @@ public interface StreamWrapper> extends Stream< */ @Override default R collect(Collector collector) { + Objects.requireNonNull(collector); return stream().collect(collector); } @@ -384,6 +396,7 @@ public interface StreamWrapper> extends Stream< */ @Override default Optional min(Comparator comparator) { + Objects.requireNonNull(comparator); return stream().min(comparator); } @@ -395,6 +408,7 @@ public interface StreamWrapper> extends Stream< */ @Override default Optional max(Comparator comparator) { + Objects.requireNonNull(comparator); return stream().max(comparator); } @@ -416,6 +430,7 @@ public interface StreamWrapper> extends Stream< */ @Override default boolean anyMatch(Predicate predicate) { + Objects.requireNonNull(predicate); return stream().anyMatch(predicate); } @@ -427,6 +442,7 @@ public interface StreamWrapper> extends Stream< */ @Override default boolean allMatch(Predicate predicate) { + Objects.requireNonNull(predicate); return stream().allMatch(predicate); } @@ -438,6 +454,7 @@ public interface StreamWrapper> extends Stream< */ @Override default boolean noneMatch(Predicate predicate) { + Objects.requireNonNull(predicate); return stream().noneMatch(predicate); } @@ -497,7 +514,7 @@ public interface StreamWrapper> extends Stream< * @return 串行流 */ @Override - default I sequential() { + default S sequential() { return wrapping(stream().sequential()); } @@ -507,7 +524,7 @@ public interface StreamWrapper> extends Stream< * @return 并行流 */ @Override - default I parallel() { + default S parallel() { return wrapping(stream().parallel()); } @@ -518,7 +535,7 @@ public interface StreamWrapper> extends Stream< * @return 无序流 */ @Override - default I unordered() { + default S unordered() { return wrapping(stream().unordered()); } @@ -529,7 +546,7 @@ public interface StreamWrapper> extends Stream< * @return 流 */ @Override - default I onClose(Runnable closeHandler) { + default S onClose(Runnable closeHandler) { return wrapping(stream().onClose(closeHandler)); } diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java new file mode 100644 index 000000000..a509d872b --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java @@ -0,0 +1,705 @@ +package cn.hutool.core.stream; + +import cn.hutool.core.collection.ListUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.stream.*; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; + +/** + * {@link AbstractEnhancedWrappedStream}、{@link TerminableWrappedStream}、{@link TransformableWrappedStream}的测试用例。 + * 此用例用于保证通过{@link AbstractEnhancedWrappedStream}获得的默认方法,在子类不重写的情况下能够按照预期效果生效 + * + * @author huangchengxing + */ +public class AbstractEnhancedWrappedStreamTest { + + @Test + public void testToList() { + List list = asList(1, 2, 3); + List toList = wrap(list).toList(); + Assert.assertEquals(list, toList); + } + + @Test + public void testToUnmodifiableList() { + List list = wrap(1, 2, 3) + .toUnmodifiableList(); + Assert.assertThrows(UnsupportedOperationException.class, () -> list.remove(0)); + } + + @Test + public void testToSet() { + List list = asList(1, 2, 3); + Set toSet = wrap(list).map(String::valueOf).toSet(); + Assert.assertEquals(new HashSet<>(asList("1", "2", "3")), toSet); + } + + @Test + public void testToUnmodifiableSet() { + Set set = wrap(1, 2, 3) + .toUnmodifiableSet(); + Assert.assertThrows(UnsupportedOperationException.class, () -> set.remove(0)); + } + + @Test + public void testToCollection() { + List list = asList(1, 2, 3); + List toCollection = wrap(list).map(String::valueOf).toColl(LinkedList::new); + Assert.assertEquals(asList("1", "2", "3"), toCollection); + } + + @Test + public void testToMap() { + List list = asList(1, 2, 3); + Map identityMap = wrap(list).toMap(String::valueOf); + Assert.assertEquals(new HashMap() {{ + put("1", 1); + put("2", 2); + put("3", 3); + }}, identityMap); + } + + @Test + public void testToUnmodifiableMap() { + Map map1 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity()); + Assert.assertThrows(UnsupportedOperationException.class, () -> map1.remove(1)); + Map map2 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity(), (t1, t2) -> t1); + Assert.assertThrows(UnsupportedOperationException.class, () -> map2.remove(1)); + } + + @Test + public void testToZip() { + List orders = asList(1, 2, 3); + List list = asList("dromara", "hutool", "sweet"); + Map toZip = wrap(orders).toZip(list); + Assert.assertEquals(new HashMap() {{ + put(1, "dromara"); + put(2, "hutool"); + put(3, "sweet"); + }}, toZip); + } + + @Test + public void testTransform() { + List list = wrap(1, 2, 3).transform(Wrapper::toList).orElse(null); + Assert.assertEquals(asList(1, 2, 3), list); + } + + @Test + public void testFindFirst() { + List list = asList(1, 2, 3); + Assert.assertEquals((Integer)1, wrap(list).findFirst(t -> (t & 1) == 1).orElse(null)); + Assert.assertEquals((Integer)1, wrap(list).filter(t -> (t & 1) == 1).findFirst().orElse(null)); + } + + @Test + public void testFindFirstIdx() { + List list = asList(1, 2, 3); + Assert.assertEquals(1, wrap(list).findFirstIdx(t -> (t & 1) == 0)); + } + + @Test + public void testFindLast() { + List list = asList(1, 2, 3); + Assert.assertEquals((Integer)3, wrap(list).findLast(t -> (t & 1) == 1).orElse(null)); + } + + @Test + public void testFindLastIdx() { + List list = asList(1, 2, 3); + Assert.assertEquals(1, wrap(list).findLastIdx(t -> (t & 1) == 0)); + } + + @Test + public void testAt() { + List list = asList(1, 2, 3); + Assert.assertEquals((Integer)3, wrap(list).at(2).orElse(null)); + } + + @Test + public void testIsEmpty() { + Assert.assertTrue(wrap(Collections.emptyList()).isEmpty()); + Assert.assertFalse(wrap(asList(1, 2, 3)).isEmpty()); + } + + @Test + public void testIsNotEmpty() { + Assert.assertFalse(wrap(Collections.emptyList()).isNotEmpty()); + Assert.assertTrue(wrap(asList(1, 2, 3)).isNotEmpty()); + } + + @Test + public void testJoining() { + List list = asList(1, 2, 3); + String joining = wrap(list).join(); + Assert.assertEquals("123", joining); + Assert.assertEquals("1,2,3", wrap(list).join(",")); + Assert.assertEquals("(1,2,3)", wrap(list).join(",", "(", ")")); + } + + @Test + public void testGrouping() { + List list = asList(1, 2, 3); + Map> map = new HashMap>() {{ + put("1", singletonList(1)); + put("2", singletonList(2)); + put("3", singletonList(3)); + }}; + + Map> group = wrap(list).group(String::valueOf, HashMap::new, Collectors.toList()); + Assert.assertEquals(map, group); + group = wrap(list).group(String::valueOf, Collectors.toList()); + Assert.assertEquals(map, group); + group = wrap(list).group(String::valueOf); + Assert.assertEquals(map, group); + } + + @Test + public void testPartitioning() { + List list = asList(1, 2, 3); + Map> map = new HashMap>() {{ + put(Boolean.TRUE, singletonList(2)); + put(Boolean.FALSE, asList(1, 3)); + }}; + + Map> partition = wrap(list).partitioning(t -> (t & 1) == 0, Collectors.toList()); + Assert.assertEquals(map, partition); + partition = wrap(list).partitioning(t -> (t & 1) == 0); + Assert.assertEquals(map, partition); + } + + @Test + public void testForEachIdx() { + List elements = new ArrayList<>(); + List indexes = new ArrayList<>(); + wrap(1, 2, 3).forEachIdx((t, i) -> { + elements.add(t); + indexes.add(i); + }); + Assert.assertEquals(asList(1, 2, 3), elements); + Assert.assertEquals(asList(0, 1, 2), indexes); + } + + @Test + public void testForEachOrderedIdx() { + List elements = new ArrayList<>(); + List indexes = new ArrayList<>(); + wrap(1, 2, 3).forEachOrderedIdx((t, i) -> { + elements.add(t); + indexes.add(i); + }); + Assert.assertEquals(asList(1, 2, 3), elements); + Assert.assertEquals(asList(0, 1, 2), indexes); + } + + @Test + public void testForEachOrdered() { + List elements = new ArrayList<>(); + wrap(1, 2, 3).forEachOrdered(elements::add); + Assert.assertEquals(asList(1, 2, 3), elements); + } + + @Test + public void testForEach() { + List elements = new ArrayList<>(); + wrap(1, 2, 3).forEach(elements::add); + Assert.assertEquals(asList(1, 2, 3), elements); + } + + @Test + public void testMapToInt() { + int[] array = wrap(1, 2, 3).mapToInt(Integer::intValue).toArray(); + Assert.assertArrayEquals(new int[] {1, 2, 3}, array); + } + + @Test + public void testMapToLong() { + long[] array = wrap(1L, 2L, 3L).mapToLong(Long::intValue).toArray(); + Assert.assertArrayEquals(new long[] {1L, 2L, 3L}, array); + } + + @Test + public void testMapToDouble() { + double[] array = wrap(1d, 2d, 3d).mapToDouble(Double::intValue).toArray(); + Assert.assertEquals(1d, array[0], 0.01); + Assert.assertEquals(2d, array[1], 0.01); + Assert.assertEquals(3d, array[2], 0.01); + } + + @Test + public void testFlatMapToInt() { + int[] array = wrap(1, 2, 3).flatMapToInt(IntStream::of).toArray(); + Assert.assertArrayEquals(new int[] {1, 2, 3}, array); + } + + @Test + public void testFlatMapToLong() { + long[] array = wrap(1L, 2L, 3L).flatMapToLong(LongStream::of).toArray(); + Assert.assertArrayEquals(new long[] {1L, 2L, 3L}, array); + } + + @Test + public void testFlatMapToDouble() { + double[] array = wrap(1d, 2d, 3d).flatMapToDouble(DoubleStream::of).toArray(); + Assert.assertEquals(1d, array[0], 0.01); + Assert.assertEquals(2d, array[1], 0.01); + Assert.assertEquals(3d, array[2], 0.01); + } + + @Test + public void testSorted() { + List list = wrap(3, 1, 2).sorted().toList(); + Assert.assertEquals(asList(1, 2, 3), list); + } + + @Test + public void testPeek() { + List elements = new ArrayList<>(); + wrap(1, 2, 3).peek(elements::add).exec(); + Assert.assertEquals(asList(1, 2, 3), elements); + } + + @Test + public void testPeekIdx() { + List elements = new ArrayList<>(); + List indexes = new ArrayList<>(); + wrap(1, 2, 3).peekIdx((t, i) -> { + elements.add(t); + indexes.add(i); + }).exec(); + Assert.assertEquals(asList(1, 2, 3), elements); + Assert.assertEquals(asList(0, 1, 2), indexes); + + Set elements2 = new HashSet<>(); + Set indexes2 = new HashSet<>(); + wrap(1, 2, null).parallel().peekIdx((t, i) -> { + elements2.add(t); + indexes2.add(i); + }).exec(); + Assert.assertEquals(new HashSet<>(asList(1, null, 2)), elements2); + Assert.assertEquals(new HashSet<>(asList(-1, -1, -1)), indexes2); + } + + @Test + public void testLimit() { + List list = wrap(1, 2, 3).limit(2L).toList(); + Assert.assertEquals(asList(1, 2), list); + } + + @Test + public void testSkip() { + List list = wrap(1, 2, 3).skip(1L).toList(); + Assert.assertEquals(asList(2, 3), list); + } + + @Test + public void testToArray() { + Object[] array1 = wrap(1, 2, 3).toArray(); + Assert.assertArrayEquals(new Object[]{1, 2, 3}, array1); + array1 = wrap(1, 2, 3).toArray(Object[]::new); + Assert.assertArrayEquals(new Object[]{1, 2, 3}, array1); + } + + @Test + public void testReduce() { + Assert.assertEquals((Integer)6, wrap(1, 2, 3).reduce(Integer::sum).orElse(null)); + Assert.assertEquals((Integer)6, wrap(1, 2, 3).reduce(0, Integer::sum)); + Assert.assertEquals((Integer)6, wrap(1, 2, 3).reduce(0, Integer::sum, Integer::sum)); + } + + @Test + public void testCollect() { + Assert.assertEquals(asList(1, 2, 3), wrap(1, 2, 3).collect(Collectors.toList())); + Assert.assertEquals( + asList(1, 2, 3), + wrap(1, 2, 3).collect(ArrayList::new, List::add, List::addAll) + ); + } + + @Test + public void testMin() { + Assert.assertEquals((Integer)1, wrap(1, 2, 3).min(Comparator.comparingInt(Integer::intValue)).orElse(null)); + } + + @Test + public void testMax() { + Assert.assertEquals((Integer)3, wrap(1, 2, 3).max(Comparator.comparingInt(Integer::intValue)).orElse(null)); + } + + @Test + public void testCount() { + Assert.assertEquals(3, wrap(1, 2, 3).count()); + } + + @Test + public void testAnyMatch() { + Assert.assertTrue(wrap(1, 2, 3).anyMatch(t -> (t & 1) == 0)); + Assert.assertFalse(wrap(1, 3).anyMatch(t -> (t & 1) == 0)); + } + + @Test + public void testAllMatch() { + Assert.assertFalse(wrap(1, 2, 3).allMatch(t -> (t & 1) == 0)); + Assert.assertTrue(wrap(2, 4).anyMatch(t -> (t & 1) == 0)); + } + + @Test + public void testNoneMatch() { + Assert.assertFalse(wrap(1, 2, 3).noneMatch(t -> (t & 1) == 0)); + Assert.assertTrue(wrap(1, 3).noneMatch(t -> (t & 1) == 0)); + } + + @Test + public void testFindAny() { + Assert.assertNotNull(wrap(1, 2, 3).findAny()); + } + + @Test + public void testIterator() { + Iterator iter1 = Stream.of(1, 2, 3).iterator(); + Iterator iter2 = wrap(1, 2, 3).iterator(); + while (iter1.hasNext() && iter2.hasNext()) { + Assert.assertEquals(iter1.next(), iter2.next()); + } + } + + @Test + public void testSpliterator() { + Spliterator iter1 = Stream.of(1, 2, 3).spliterator(); + Spliterator iter2 = wrap(1, 2, 3).spliterator(); + Assert.assertEquals(iter1.trySplit().estimateSize(), iter2.trySplit().estimateSize()); + } + + @Test + public void testIsParallel() { + Assert.assertTrue(wrap(Stream.of(1, 2, 3).parallel()).isParallel()); + } + + @Test + public void testSequential() { + Assert.assertFalse(wrap(Stream.of(1, 2, 3).parallel()).sequential().isParallel()); + } + + @Test + public void testUnordered() { + Assert.assertNotNull(wrap(Stream.of(1, 2, 3)).unordered()); + } + + @Test + public void testOnClose() { + AtomicBoolean atomicBoolean = new AtomicBoolean(false); + wrap(Stream.of(1, 2, 3).onClose(() -> atomicBoolean.set(true))).close(); + Assert.assertTrue(atomicBoolean.get()); + } + + @Test + public void testClose() { + Wrapper stream = wrap(Stream.of(1, 2, 3)); + stream.close(); + Assert.assertThrows(IllegalStateException.class, stream::exec); + } + + @Test + public void testReverse() { + Assert.assertEquals( + asList(3, 2, 1), wrap(1, 2, 3).reverse().toList() + ); + } + + @Test + public void testParallel() { + Assert.assertTrue(wrap(1, 2, 3).parallel().isParallel()); + } + + @Test + public void testSplice() { + Assert.assertEquals( + asList(1, 4, 5), wrap(1, 2, 3).splice(1, 2, 4, 5).toList() + ); + } + + @Test + public void testTakeWhile() { + Assert.assertEquals( + asList(1, 2), + wrap(1, 2, 3, 4).takeWhile(i -> !Objects.equals(i, 3)).toList() + ); + + } + + @Test + public void testDropWhile() { + Assert.assertEquals( + asList(3, 4), + wrap(1, 2, 3, 4).dropWhile(i -> !Objects.equals(i, 3)).toList() + ); + } + + @Test + public void testDistinct() { + Assert.assertEquals( + asList(1, 2, 3), wrap(1, 1, 2, 3).distinct().toList() + ); + } + + @Test + public void testLog() { + Assert.assertNotNull(wrap(1, 2, 3).log().toList()); + } + + @Test + public void testPush() { + Assert.assertEquals( + asList(1, 2, 3), wrap(1).push(2, 3).toList() + ); + } + + @Test + public void testUnshift() { + Assert.assertEquals( + asList(1, 2, 3), wrap(3).unshift(1, 2).toList() + ); + } + + @Test + public void testAppend() { + Assert.assertEquals( + asList(1, 2, 3), wrap(1).append(asList(2, 3)).toList() + ); + Assert.assertEquals( + asList(1, 2, 3), wrap(1, 2, 3).append(null).toList() + ); + } + + @Test + public void testPrepend() { + Assert.assertEquals( + asList(1, 2, 3), wrap(3).prepend(asList(1, 2)).toList() + ); + Assert.assertEquals( + asList(1, 2, 3), wrap(1, 2, 3).prepend(null).toList() + ); + } + + @Test + public void testNonNull() { + Assert.assertEquals( + asList(1, 3), wrap(1, null, 3).nonNull().toList() + ); + } + + @Test + public void testFilterIdx() { + List indexes = new ArrayList<>(); + Assert.assertEquals( + asList(1, 3), + wrap(1, 2, 3).filterIdx((t, i) -> { + indexes.add(i); + return (t & 1) == 1; + }).toList() + ); + Assert.assertEquals(asList(0, 1, 2), indexes); + } + + @Test + public void testFilter() { + Assert.assertEquals( + asList(1, 3), wrap(1, 2, 3).filter(i -> (i & 1) == 1).toList() + ); + } + + @Test + public void testFlatMap() { + Assert.assertEquals( + asList(1, 2, 3), wrap(1, 2, 3).flatMap(Stream::of).toList() + ); + } + + @Test + public void testFlatMapIdx() { + List indexes = new ArrayList<>(); + Assert.assertEquals( + asList(1, 2, 3), wrap(1, 2, 3).flatMapIdx((t, i) -> { + indexes.add(i); + return Stream.of(t); + }).toList() + ); + Assert.assertEquals(asList(0, 1, 2), indexes); + } + + @Test + public void testFlat() { + Assert.assertEquals( + asList(1, 2, 3), wrap(1, 2, 3).flat(Collections::singletonList).toList() + ); + } + + @Test + public void testFlatNonNull() { + Assert.assertEquals( + asList(2, 3), wrap(null, 2, 3).flatNonNull(Collections::singletonList).toList() + ); + } + + @Test + public void testFlatTree() { + Tree root = new Tree(1, asList(new Tree(2, asList(new Tree(3, Collections.emptyList()))))); + Assert.assertEquals(3L, wrap(root).flatTree(Tree::getChildren, Tree::setChildren).count()); + } + + @Test + public void testMap() { + Assert.assertEquals( + asList("1", "2", "3"), wrap(1, 2, 3).map(String::valueOf).toList() + ); + } + + @Test + public void testMapNonNull() { + Assert.assertEquals( + asList("3"), wrap(null, 2, 3, 4).mapNonNull(t -> ((t & 1) == 0) ? null : String.valueOf(t)).toList() + ); + } + + @Test + public void testMapIdx() { + List indexes = new ArrayList<>(); + Assert.assertEquals( + asList("1", "2", "3"), wrap(1, 2, 3).mapIdx((t, i) -> { + indexes.add(i); + return String.valueOf(t); + }).toList() + ); + Assert.assertEquals(asList(0, 1, 2), indexes); + } + + @Test + public void testMapMulti() { + Assert.assertEquals( + asList(1, 2, 3), + wrap(1, 2, 3).mapMulti((t, builder) -> { + builder.accept(t); + }).toList() + ); + } + + @Test + public void testHashCode() { + Stream stream = Stream.of(1, 2, 3); + Assert.assertEquals(stream.hashCode(), wrap(stream).hashCode()); + } + + @Test + public void testEquals() { + Stream stream = Stream.of(1, 2, 3); + Assert.assertEquals(wrap(stream), stream); + } + + @Test + public void testToString() { + Stream stream = Stream.of(1, 2, 3); + Assert.assertEquals(stream.toString(), wrap(stream).toString()); + } + + @Test + public void testToEntries() { + Map expect = new HashMap(){{ + put(1, 1); + put(2, 2); + put(3, 3); + }}; + Map map = EasyStream.of(1, 2, 3) + .toEntries(Function.identity(), Function.identity()) + .toMap(); + Assert.assertEquals(expect, map); + + map = EasyStream.of(1, 2, 3) + .toEntries(Function.identity()) + .toMap(); + Assert.assertEquals(expect, map); + } + + @Test + public void testZip() { + final List orders = Arrays.asList(1, 2, 3); + final List list = Arrays.asList("dromara", "hutool", "sweet"); + List zip = wrap(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList(); + Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); + + zip = wrap((Stream)EasyStream.iterate(1, i -> i + 1)).zip(list, (e1, e2) -> e1 + "." + e2).toList(); + Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); + } + + @Test + public void testListSplit() { + final List list = Arrays.asList(1, 2, 3, 4, 5); + List> lists = wrap(list).split(2).map(TerminableWrappedStream::toList).toList(); + Assert.assertEquals(ListUtil.split(list, 2), lists); + + // 指定长度 大于等于 列表长度 + lists = wrap(list).split(list.size()).map(TerminableWrappedStream::toList).toList(); + Assert.assertEquals(singletonList(list), lists); + } + + @Test + public void testSplitList() { + final List list = Arrays.asList(1, 2, 3, 4, 5); + List> lists = wrap(list).splitList(2).toList(); + Assert.assertEquals(ListUtil.split(list, 2), lists); + + // 指定长度 大于等于 列表长度 + lists = wrap(list).splitList(list.size()).toList(); + Assert.assertEquals(singletonList(list), lists); + } + + @SafeVarargs + private static Wrapper wrap(T... array) { + return new Wrapper<>(Stream.of(array)); + } + + private static Wrapper wrap(Iterable iterable) { + return new Wrapper<>(StreamSupport.stream(iterable.spliterator(), false)); + } + + private static Wrapper wrap(Stream stream) { + return new Wrapper<>(stream); + } + + private static class Wrapper extends AbstractEnhancedWrappedStream> { + + /** + * 创建一个流包装器 + * + * @param stream 包装的流对象 + * @throws NullPointerException 当{@code stream}为{@code null}时抛出 + */ + protected Wrapper(Stream stream) { + super(stream); + } + + @Override + public Wrapper wrapping(Stream source) { + return new Wrapper<>(source); + } + + } + + @Setter + @Getter + @AllArgsConstructor + private static class Tree { + private final Integer id; + private List children; + } + +} diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java index 6312d17e3..e1386591b 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java @@ -1,9 +1,7 @@ package cn.hutool.core.stream; - import cn.hutool.core.collection.ListUtil; import cn.hutool.core.map.MapUtil; -import lombok.Builder; import lombok.Data; import lombok.experimental.Tolerate; import org.junit.Assert; @@ -22,6 +20,13 @@ import static java.util.Collections.singletonList; */ public class EasyStreamTest { + @Test + public void testConcat() { + Stream stream1 = Stream.of(1, 2); + Stream stream2 = Stream.of(3, 4); + Assert.assertEquals(4, EasyStream.concat(stream1, stream2).count()); + } + @Test public void testBuilder() { final List list = EasyStream.builder().add(1).add(2).add(3).build().toList(); @@ -54,7 +59,7 @@ public class EasyStreamTest { } @Test - public void testToCollection() { + public void testToColl() { final List list = Arrays.asList(1, 2, 3); final List toCollection = EasyStream.of(list).map(String::valueOf).toColl(LinkedList::new); Assert.assertEquals(Arrays.asList("1", "2", "3"), toCollection); @@ -168,18 +173,22 @@ public class EasyStreamTest { final List collect1 = list.stream().distinct().collect(Collectors.toList()); final List collect2 = list.stream().parallel().distinct().collect(Collectors.toList()); - // 使用FastStream去重 + // 使用EasyStream去重 final List distinctBy1 = EasyStream.of(list).distinct().toList(); final List distinctBy2 = EasyStream.of(list).parallel().distinct(String::valueOf).toList(); Assert.assertEquals(collect1, distinctBy1); Assert.assertEquals(collect2, distinctBy2); + + Assert.assertEquals( + 4, EasyStream.of(1, 2, 2, null, 3, null).parallel(true).distinct(t -> Objects.isNull(t) ? null : t.toString()).count() + ); } @Test public void testForeachIdx() { final List list = Arrays.asList("dromara", "hutool", "sweet"); - final EasyStream.FastStreamBuilder builder = EasyStream.builder(); + final EasyStream.Builder builder = EasyStream.builder(); EasyStream.of(list).forEachIdx((e, i) -> builder.accept(i + 1 + "." + e)); Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), builder.build().toList()); // 并行流时为-1 @@ -189,11 +198,11 @@ public class EasyStreamTest { @Test public void testForEachOrderedIdx() { final List list = Arrays.asList("dromara", "hutool", "sweet"); - final EasyStream.FastStreamBuilder builder = EasyStream.builder(); + final EasyStream.Builder builder = EasyStream.builder(); EasyStream.of(list).forEachOrderedIdx((e, i) -> builder.accept(i + 1 + "." + e)); Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), builder.build().toList()); - final EasyStream.FastStreamBuilder streamBuilder = EasyStream.builder(); + final EasyStream.Builder streamBuilder = EasyStream.builder(); EasyStream.of(list).parallel().forEachOrderedIdx((e, i) -> streamBuilder.accept(i + 1 + "." + e)); Assert.assertEquals(Arrays.asList("0.dromara", "0.hutool", "0.sweet"), streamBuilder.build().toList()); @@ -360,39 +369,6 @@ public class EasyStreamTest { Assert.assertEquals(ListUtil.of((Object) null), EasyStream.of((Object) null).reverse().toList()); } - @Test - public void testZip() { - final List orders = Arrays.asList(1, 2, 3); - final List list = Arrays.asList("dromara", "hutool", "sweet"); - List zip = EasyStream.of(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList(); - Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); - - zip = EasyStream.iterate(1, i -> i + 1).zip(list, (e1, e2) -> e1 + "." + e2).toList(); - Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); - } - - @Test - public void testListSplit() { - final List list = Arrays.asList(1, 2, 3, 4, 5); - List> lists = EasyStream.of(list).split(2).map(EasyStream::toList).toList(); - Assert.assertEquals(ListUtil.split(list, 2), lists); - - // 指定长度 大于等于 列表长度 - lists = EasyStream.of(list).split(list.size()).map(EasyStream::toList).toList(); - Assert.assertEquals(singletonList(list), lists); - } - - @Test - public void testSplitList() { - final List list = Arrays.asList(1, 2, 3, 4, 5); - List> lists = EasyStream.of(list).splitList(2).toList(); - Assert.assertEquals(ListUtil.split(list, 2), lists); - - // 指定长度 大于等于 列表长度 - lists = EasyStream.of(list).splitList(list.size()).toList(); - Assert.assertEquals(singletonList(list), lists); - } - @Test public void testTakeWhile() { // 1 到 10 @@ -557,7 +533,7 @@ public class EasyStreamTest { } @Data - @Builder + @lombok.Builder public static class Student { private String name; private Integer age; diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java index 7e91c0144..532460166 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java @@ -31,6 +31,17 @@ public class EntryStreamTest { EntryStream.merge(Arrays.asList(1, 2), Arrays.asList(1, 2, 3)) .collectKeys(Collectors.toList()) ); + + Assert.assertEquals( + Arrays.asList(1, 2), + EntryStream.merge(null, Arrays.asList(1, 2)) + .collectValues(Collectors.toList()) + ); + Assert.assertEquals( + Arrays.asList(1, 2), + EntryStream.merge(Arrays.asList(1, 2), null) + .collectKeys(Collectors.toList()) + ); } @Test @@ -38,15 +49,21 @@ public class EntryStreamTest { Map map = new HashMap<>(); map.put("1", "1"); Assert.assertEquals(1, EntryStream.of(map).count()); + Assert.assertEquals(0, EntryStream.of((Map)null).count()); Set> entries = new HashSet<>(); entries.add(new Entry<>(1, 1)); entries.add(null); Assert.assertEquals(2, EntryStream.of(entries).count()); + Assert.assertEquals(0, EntryStream.of((Set>)null).count()); Assert.assertEquals(2, EntryStream.of(entries.stream()).count()); + Assert.assertEquals(0, EntryStream.of((Stream>)null).count()); + Assert.assertEquals(2, new EntryStream<>(entries.stream()).count()); + Assert.assertThrows(NullPointerException.class, () -> new EntryStream<>(null)); Iterable iterable = Arrays.asList(1, 2, null); Assert.assertEquals(3, EntryStream.of(iterable, Function.identity(), Function.identity()).count()); + Assert.assertEquals(0, EntryStream.of(null, Function.identity(), Function.identity()).count()); } @Test @@ -117,11 +134,65 @@ public class EntryStreamTest { Assert.assertEquals( 5, EntryStream.of(Arrays.asList(1, 2, 3), Function.identity(), Function.identity()) - .append(4, 4) - .append(5, 5) + .push(4, 4) + .push(5, 5) .count() ); + } + @Test + public void testUnshift() { + Assert.assertEquals( + 5, + EntryStream.of(Arrays.asList(1, 2, 3), Function.identity(), Function.identity()) + .unshift(4, 4) + .unshift(5, 5) + .count() + ); + } + + @Test + public void testAppend() { + Map map1 = new HashMap(){{ + put(1, 1); + put(2, 2); + }}; + Map map2 = new HashMap(){{ + put(3, 3); + put(4, 4); + }}; + Assert.assertEquals( + new ArrayList>(){{ + addAll(map1.entrySet()); + addAll(map2.entrySet()); + }}, + EntryStream.of(map1).append(map2.entrySet()).toList() + ); + Assert.assertEquals( + new ArrayList<>(map1.entrySet()), EntryStream.of(map1).append(null).toList() + ); + } + + @Test + public void testPrepend() { + Map map1 = new HashMap(){{ + put(1, 1); + put(2, 2); + }}; + Map map2 = new HashMap(){{ + put(3, 3); + put(4, 4); + }}; + Assert.assertEquals( + new ArrayList>(){{ + addAll(map2.entrySet()); + addAll(map1.entrySet()); + }}, + EntryStream.of(map1).prepend(map2.entrySet()).toList() + ); + Assert.assertEquals( + new ArrayList<>(map1.entrySet()), EntryStream.of(map1).prepend(null).toList() + ); } @Test @@ -230,6 +301,12 @@ public class EntryStreamTest { .map((k, v) -> k.toString() + v.toString()) .collect(Collectors.toList()) ); + Assert.assertEquals( + Arrays.asList("11", "22", "33"), + EntryStream.of(map) + .map(e -> e.getKey().toString() + e.getValue().toString()) + .collect(Collectors.toList()) + ); } @Test @@ -457,7 +534,7 @@ public class EntryStreamTest { Map map = new HashMap<>(); map.put(1, null); map.put(null, 1); - Assert.assertEquals(0, EntryStream.of(map).nonNull().count()); + Assert.assertEquals(0, EntryStream.of(map).nonNullKeyValue().count()); } @Test @@ -465,7 +542,7 @@ public class EntryStreamTest { Map map = new HashMap<>(); map.put(1, null); map.put(null, 1); - Assert.assertEquals(1, EntryStream.of(map).keyNonNull().count()); + Assert.assertEquals(1, EntryStream.of(map).nonNullKey().count()); } @Test @@ -473,7 +550,7 @@ public class EntryStreamTest { Map map = new HashMap<>(); map.put(1, null); map.put(null, 1); - Assert.assertEquals(1, EntryStream.of(map).valueNonNull().count()); + Assert.assertEquals(1, EntryStream.of(map).nonNullValue().count()); } private static class Entry implements Map.Entry { From 717305e0396b507be7ae372431ab0366b152b3d0 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 6 Sep 2022 12:21:41 +0800 Subject: [PATCH 4/7] fix code --- .../stream/AbstractEnhancedWrappedStream.java | 4 +- .../cn/hutool/core/stream/EasyStream.java | 37 ++++--- .../cn/hutool/core/stream/EntryStream.java | 103 ++++++++++-------- .../core/stream/TerminableWrappedStream.java | 50 ++++----- .../stream/TransformableWrappedStream.java | 19 ++-- .../cn/hutool/core/stream/WrappedStream.java | 50 ++++----- .../cn/hutool/core/stream/EasyStreamTest.java | 2 +- 7 files changed, 139 insertions(+), 126 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java index 5fc66d653..aed748389 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java @@ -35,7 +35,7 @@ public abstract class AbstractEnhancedWrappedStream stream) { + protected AbstractEnhancedWrappedStream(final Stream stream) { this.stream = Objects.requireNonNull(stream, "stream must not null"); } @@ -56,7 +56,7 @@ public abstract class AbstractEnhancedWrappedStream extends AbstractEnhancedWrappedStream wrapping(Stream stream) { + public EasyStream wrapping(final Stream stream) { return new EasyStream<>(stream); } @@ -281,10 +281,11 @@ public class EasyStream extends AbstractEnhancedWrappedStream */ - public > List toTree(Function idGetter, - Function pIdGetter, - BiConsumer> childrenSetter) { - Map> pIdValuesMap = group(pIdGetter); + public > List toTree( + final Function idGetter, + final Function pIdGetter, + final BiConsumer> childrenSetter) { + final Map> pIdValuesMap = group(pIdGetter); return getChildrenFromMapByPidAndSet(idGetter, childrenSetter, pIdValuesMap, pIdValuesMap.get(null)); } @@ -305,13 +306,14 @@ public class EasyStream extends AbstractEnhancedWrappedStream */ - public > List toTree(Function idGetter, - Function pIdGetter, - BiConsumer> childrenSetter, - Predicate parentPredicate) { + public > List toTree( + final Function idGetter, + final Function pIdGetter, + final BiConsumer> childrenSetter, + final Predicate parentPredicate) { Objects.requireNonNull(parentPredicate); - List list = toList(); - List parents = EasyStream.of(list).filter(e -> + final List list = toList(); + final List parents = EasyStream.of(list).filter(e -> // 此处是为了适配 parentPredicate.test空指针 情况 // 因为Predicate.test的返回值是boolean,所以如果 e -> null 这种返回null的情况,会直接抛出NPE Opt.ofTry(() -> parentPredicate.test(e)).filter(Boolean::booleanValue).isPresent()) @@ -330,15 +332,16 @@ public class EasyStream extends AbstractEnhancedWrappedStream 此处是id的泛型限制 * @return list 组装好的树 */ - private > List getChildrenFromMapByPidAndSet(Function idGetter, - BiConsumer> childrenSetter, - Map> pIdValuesMap, - List parents) { + private > List getChildrenFromMapByPidAndSet( + final Function idGetter, + final BiConsumer> childrenSetter, + final Map> pIdValuesMap, + final List parents) { Objects.requireNonNull(idGetter); Objects.requireNonNull(childrenSetter); Objects.requireNonNull(pIdValuesMap); - MutableObj>> recursiveRef = new MutableObj<>(); - Consumer> recursive = values -> EasyStream.of(values, isParallel()).forEach(value -> { + final MutableObj>> recursiveRef = new MutableObj<>(); + final Consumer> recursive = values -> EasyStream.of(values, isParallel()).forEach(value -> { List children = pIdValuesMap.get(idGetter.apply(value)); childrenSetter.accept(value, children); recursiveRef.get().accept(children); diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java index cb030f9df..322379071 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java @@ -38,7 +38,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream EntryStream merge(Iterable keys, Iterable values) { + public static EntryStream merge(final Iterable keys, final Iterable values) { final boolean hasKeys = ObjUtil.isNotNull(keys); final boolean hasValues = ObjUtil.isNotNull(values); // 皆为空 @@ -75,7 +75,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 值类型 * @return {@link EntryStream}实例 */ - public static EntryStream of(Map map) { + public static EntryStream of(final Map map) { return ObjUtil.isNull(map) ? empty() : of(map.entrySet()); } @@ -90,7 +90,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 值类型 * @return {@link EntryStream}实例 */ - public static EntryStream of(Iterable> entries) { + public static EntryStream of(final Iterable> entries) { return ObjUtil.isNull(entries) ? empty() : of(StreamSupport.stream(entries.spliterator(), false)); } @@ -106,7 +106,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream EntryStream of( - Iterable source, Function keyMapper, Function valueMapper) { + final Iterable source, final Function keyMapper, final Function valueMapper) { Objects.requireNonNull(keyMapper); Objects.requireNonNull(valueMapper); if (ObjUtil.isNull(source)) { @@ -126,7 +126,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 值类型 * @return {@link EntryStream}实例 */ - public static EntryStream of(Stream> stream) { + public static EntryStream of(final Stream> stream) { return ObjUtil.isNull(stream) ? empty() : new EntryStream<>(stream.map(EntryStream::ofEntry)); } @@ -145,7 +145,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream> stream) { + EntryStream(final Stream> stream) { super(stream); } @@ -157,9 +157,10 @@ public class EntryStream extends AbstractEnhancedWrappedStream distinctByKey() { - Set accessed = new ConcurrentHashSet<>(16); + // FIXME fix happen NPE when has null key + final Set accessed = new ConcurrentHashSet<>(16); return wrapping(stream.filter(e -> { - K key = e.getKey(); + final K key = e.getKey(); if (accessed.contains(key)) { return false; } @@ -174,9 +175,10 @@ public class EntryStream extends AbstractEnhancedWrappedStream distinctByValue() { - Set accessed = new ConcurrentHashSet<>(16); + // FIXME fix happen NPE when has null value + final Set accessed = new ConcurrentHashSet<>(16); return wrapping(stream.filter(e -> { - V val = e.getValue(); + final V val = e.getValue(); if (accessed.contains(val)) { return false; } @@ -191,7 +193,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream filter(BiPredicate filter) { + public EntryStream filter(final BiPredicate filter) { Objects.requireNonNull(filter); return super.filter(e -> filter.test(e.getKey(), e.getValue())); } @@ -202,7 +204,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream filterByKey(Predicate filter) { + public EntryStream filterByKey(final Predicate filter) { Objects.requireNonNull(filter); return super.filter(e -> filter.test(e.getKey())); } @@ -213,7 +215,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream filterByValue(Predicate filter) { + public EntryStream filterByValue(final Predicate filter) { Objects.requireNonNull(filter); return super.filter(e -> filter.test(e.getValue())); } @@ -251,7 +253,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream peekKey(Consumer consumer) { + public EntryStream peekKey(final Consumer consumer) { Objects.requireNonNull(consumer); return super.peek(e -> consumer.accept(e.getKey())); } @@ -262,7 +264,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream peekValue(Consumer consumer) { + public EntryStream peekValue(final Consumer consumer) { Objects.requireNonNull(consumer); return super.peek(e -> consumer.accept(e.getValue())); } @@ -273,7 +275,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream sortByKey(Comparator comparator) { + public EntryStream sortByKey(final Comparator comparator) { Objects.requireNonNull(comparator); return sorted(Map.Entry.comparingByKey(comparator)); } @@ -284,7 +286,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream sortByValue(Comparator comparator) { + public EntryStream sortByValue(final Comparator comparator) { Objects.requireNonNull(comparator); return sorted(Map.Entry.comparingByValue(comparator)); } @@ -298,7 +300,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream push(K key, V value) { + public EntryStream push(final K key, final V value) { return wrapping(Stream.concat(stream, Stream.of(ofEntry(key, value)))); } @@ -309,7 +311,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream unshift(K key, V value) { + public EntryStream unshift(final K key, final V value) { return wrapping(Stream.concat(Stream.of(ofEntry(key, value)), stream)); } @@ -320,7 +322,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream append(Iterable> entries) { + public EntryStream append(final Iterable> entries) { if (IterUtil.isEmpty(entries)) { return this; } @@ -336,7 +338,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream prepend(Iterable> entries) { + public EntryStream prepend(final Iterable> entries) { if (IterUtil.isEmpty(entries)) { return this; } @@ -370,7 +372,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 新的键类型 * @return {@link EntryStream}实例 */ - public EntryStream mapKeys(Function mapper) { + public EntryStream mapKeys(final Function mapper) { Objects.requireNonNull(mapper); return new EntryStream<>( stream.map(e -> ofEntry(mapper.apply(e.getKey()), e.getValue())) @@ -384,7 +386,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 新的值类型 * @return {@link EntryStream}实例 */ - public EntryStream mapValues(Function mapper) { + public EntryStream mapValues(final Function mapper) { Objects.requireNonNull(mapper); return new EntryStream<>( stream.map(e -> ofEntry(e.getKey(), mapper.apply(e.getValue()))) @@ -400,7 +402,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream EasyStream map(Function, ? extends R> mapper) { + public EasyStream map(final Function, ? extends R> mapper) { Objects.requireNonNull(mapper); return EasyStream.of(stream.map(mapper)); } @@ -412,7 +414,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 函数执行后返回流中元素的类型 * @return 映射后的单对象组成的流 */ - public EasyStream map(BiFunction mapper) { + public EasyStream map(final BiFunction mapper) { Objects.requireNonNull(mapper); return EasyStream.of(stream.map(e -> mapper.apply(e.getKey(), e.getValue()))); } @@ -430,7 +432,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream EasyStream flatMap(Function, ? extends Stream> mapper) { + public EasyStream flatMap(final Function, ? extends Stream> mapper) { Objects.requireNonNull(mapper); return EasyStream.of(stream.flatMap(mapper)); } @@ -449,7 +451,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 新的键类型 * @return 返回叠加拆分操作后的流 */ - public EntryStream flatMapKey(Function> keyMapper) { + public EntryStream flatMapKey(final Function> keyMapper) { Objects.requireNonNull(keyMapper); return new EntryStream<>( stream.flatMap(e -> keyMapper @@ -473,7 +475,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 新的值类型 * @return 返回叠加拆分操作后的流 */ - public EntryStream flatMapValue(Function> valueMapper) { + public EntryStream flatMapValue(final Function> valueMapper) { Objects.requireNonNull(valueMapper); return new EntryStream<>( stream.flatMap(e -> valueMapper @@ -493,7 +495,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream toMap(Supplier> mapFactory, BinaryOperator operator) { + public Map toMap(final Supplier> mapFactory, final BinaryOperator operator) { Objects.requireNonNull(mapFactory); Objects.requireNonNull(operator); return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, operator, mapFactory)); @@ -506,7 +508,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream toMap(Supplier> mapFactory) { + public Map toMap(final Supplier> mapFactory) { Objects.requireNonNull(mapFactory); return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (t1, t2) -> t2, mapFactory)); } @@ -533,7 +535,9 @@ public class EntryStream extends AbstractEnhancedWrappedStream Table toTable( - BiFunction rowKeyMapper, Supplier> colMapFactory, BinaryOperator operator) { + final BiFunction rowKeyMapper, + final Supplier> colMapFactory, + final BinaryOperator operator) { Objects.requireNonNull(rowKeyMapper); Objects.requireNonNull(colMapFactory); Objects.requireNonNull(operator); @@ -553,7 +557,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream Table toTable(BiFunction rowKeyMapper) { + public Table toTable(final BiFunction rowKeyMapper) { return toTable(rowKeyMapper, HashMap::new, throwingMerger()); } @@ -567,7 +571,9 @@ public class EntryStream extends AbstractEnhancedWrappedStream Table toTableByKey( - Function rowKeyMapper, Supplier> colMapFactory, BinaryOperator operator) { + final Function rowKeyMapper, + final Supplier> colMapFactory, + final BinaryOperator operator) { return toTable((k, v) -> rowKeyMapper.apply(k), colMapFactory, operator); } @@ -579,7 +585,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream Table toTableByKey(Function rowKeyMapper) { + public Table toTableByKey(final Function rowKeyMapper) { return toTable((k, v) -> rowKeyMapper.apply(k)); } @@ -593,7 +599,9 @@ public class EntryStream extends AbstractEnhancedWrappedStream Table toTableByValue( - Function rowKeyMapper, Supplier> colMapFactory, BinaryOperator operator) { + final Function rowKeyMapper, + final Supplier> colMapFactory, + final BinaryOperator operator) { return toTable((k, v) -> rowKeyMapper.apply(v), colMapFactory, operator); } @@ -605,7 +613,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream Table toTableByValue(Function rowKeyMapper) { + public Table toTableByValue(final Function rowKeyMapper) { return toTable((k, v) -> rowKeyMapper.apply(v)); } @@ -625,7 +633,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 值集合的类型 * @return 集合 */ - public > Map groupByKey(Collector collector) { + public > Map groupByKey(final Collector collector) { return groupByKey((Supplier>)HashMap::new, collector); } @@ -638,7 +646,8 @@ public class EntryStream extends AbstractEnhancedWrappedStream 返回的map集合类型 * @return 集合 */ - public , M extends Map> M groupByKey(Supplier mapFactory, Collector collector) { + public , M extends Map> M groupByKey( + final Supplier mapFactory, final Collector collector) { return super.collect(Collectors.groupingBy( Map.Entry::getKey, mapFactory, CollectorUtil.transform(ArrayList::new, s -> s.stream().map(Map.Entry::getValue).collect(collector)) @@ -650,7 +659,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream consumer) { + public void forEach(final BiConsumer consumer) { Objects.requireNonNull(consumer); super.forEach(e -> consumer.accept(e.getKey(), e.getValue())); } @@ -673,7 +682,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 返回值类型 * @return 收集容器 */ - public R collectKeys(Collector collector) { + public R collectKeys(final Collector collector) { return toKeyStream().collect(collector); } @@ -684,7 +693,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream 返回值类型 * @return 收集容器 */ - public R collectValues(Collector collector) { + public R collectValues(final Collector collector) { return toValueStream().collect(collector); } @@ -694,7 +703,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream predicate) { + public boolean anyMatch(final BiPredicate predicate) { return super.anyMatch(e -> predicate.test(e.getKey(), e.getValue())); } @@ -704,7 +713,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream predicate) { + public boolean allMatch(final BiPredicate predicate) { Objects.requireNonNull(predicate); return super.allMatch(e -> predicate.test(e.getKey(), e.getValue())); } @@ -715,7 +724,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream predicate) { + public boolean noneMatch(final BiPredicate predicate) { Objects.requireNonNull(predicate); return super.noneMatch(e -> predicate.test(e.getKey(), e.getValue())); } @@ -726,7 +735,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream Map.Entry ofEntry(Map.Entry entry) { + static Map.Entry ofEntry(final Map.Entry entry) { return ObjUtil.defaultIfNull( entry, e -> ofEntry(e.getKey(), e.getValue()), (Map.Entry)EMPTY_ENTRY ); @@ -735,7 +744,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream Map.Entry ofEntry(K key, V value) { + static Map.Entry ofEntry(final K key, final V value) { return new AbstractMap.SimpleImmutableEntry<>(key, value); } @@ -746,7 +755,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream wrapping(Stream> stream) { + public EntryStream wrapping(final Stream> stream) { return new EntryStream<>(stream); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java index 0e71feb09..96923cd94 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java @@ -71,7 +71,7 @@ public interface TerminableWrappedStream 集合类型 * @return 集合 */ - default > C toColl(Supplier collectionFactory) { + default > C toColl(final Supplier collectionFactory) { Objects.requireNonNull(collectionFactory); return stream().collect(Collectors.toCollection(collectionFactory)); } @@ -88,7 +88,7 @@ public interface TerminableWrappedStream Map toMap(Function keyMapper) { + default Map toMap(final Function keyMapper) { return this.toMap(keyMapper, Function.identity()); } @@ -103,7 +103,7 @@ public interface TerminableWrappedStream Map toMap( - Function keyMapper, Function valueMapper) { + final Function keyMapper, final Function valueMapper) { return this.toMap(keyMapper, valueMapper, (l, r) -> r); } @@ -118,7 +118,7 @@ public interface TerminableWrappedStream Map toUnmodifiableMap( - Function keyMapper, Function valueMapper) { + final Function keyMapper, final Function valueMapper) { return Collections.unmodifiableMap(this.toMap(keyMapper, valueMapper)); } @@ -134,9 +134,9 @@ public interface TerminableWrappedStream Map toMap( - Function keyMapper, - Function valueMapper, - BinaryOperator mergeFunction) { + final Function keyMapper, + final Function valueMapper, + final BinaryOperator mergeFunction) { return this.toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); } @@ -152,9 +152,9 @@ public interface TerminableWrappedStream Map toUnmodifiableMap( - Function keyMapper, - Function valueMapper, - BinaryOperator mergeFunction) { + final Function keyMapper, + final Function valueMapper, + final BinaryOperator mergeFunction) { return Collections.unmodifiableMap( this.toMap(keyMapper, valueMapper, mergeFunction, HashMap::new) ); @@ -173,9 +173,9 @@ public interface TerminableWrappedStream> M toMap( - Function keyMapper, - Function valueMapper, - BinaryOperator mergeFunction, + final Function keyMapper, + final Function valueMapper, + final BinaryOperator mergeFunction, Supplier mapSupplier) { Objects.requireNonNull(keyMapper); Objects.requireNonNull(valueMapper); @@ -198,12 +198,12 @@ public interface TerminableWrappedStream * 如果key重复, 则保留最后一个关联的value;
*/ - default Map toZip(Iterable other) { + default Map toZip(final Iterable other) { Objects.requireNonNull(other); // value对象迭代器 final Iterator iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator); if (this.isParallel()) { - List keyList = toList(); + final List keyList = toList(); final Map map = new HashMap<>(keyList.size()); for (T key : keyList) { map.put(key, iterator.hasNext() ? iterator.next() : null); @@ -364,7 +364,7 @@ public interface TerminableWrappedStream Map> group(Function classifier) { + default Map> group(final Function classifier) { return this.group(classifier, Collectors.toList()); } @@ -408,7 +408,7 @@ public interface TerminableWrappedStream Map group( - Function classifier, Collector downstream) { + final Function classifier, final Collector downstream) { return this.group(classifier, HashMap::new, downstream); } @@ -426,9 +426,9 @@ public interface TerminableWrappedStream> M group( - Function classifier, - Supplier mapFactory, - Collector downstream) { + final Function classifier, + final Supplier mapFactory, + final Collector downstream) { Objects.requireNonNull(classifier); Objects.requireNonNull(mapFactory); Objects.requireNonNull(downstream); @@ -442,7 +442,7 @@ public interface TerminableWrappedStream> partitioning(Predicate predicate) { + default Map> partitioning(final Predicate predicate) { return this.partitioning(predicate, ArrayList::new); } @@ -454,7 +454,7 @@ public interface TerminableWrappedStream> Map partitioning(Predicate predicate, Supplier collFactory) { + default > Map partitioning(final Predicate predicate, final Supplier collFactory) { return this.partitioning(predicate, Collectors.toCollection(collFactory)); } @@ -466,7 +466,7 @@ public interface TerminableWrappedStream 返回值类型 * @return map */ - default Map partitioning(Predicate predicate, Collector downstream) { + default Map partitioning(final Predicate predicate, final Collector downstream) { Objects.requireNonNull(predicate); Objects.requireNonNull(downstream); return stream().collect(Collectors.partitioningBy(predicate, downstream)); diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java index f21a1dc0e..f26357e42 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java @@ -39,7 +39,8 @@ public interface TransformableWrappedStream 合并后的结果对象类型 * @return 合并后的结果对象的流 */ - default EasyStream zip(final Iterable other, + default EasyStream zip( + final Iterable other, final BiFunction zipper) { Objects.requireNonNull(zipper); final Spliterator keys = spliterator(); @@ -104,7 +105,7 @@ public interface TransformableWrappedStream 值类型 * @return {@link EntryStream}实例 */ - default EntryStream toEntries(Function keyMapper, Function valueMapper) { + default EntryStream toEntries(final Function keyMapper, final Function valueMapper) { Objects.requireNonNull(keyMapper); Objects.requireNonNull(valueMapper); return new EntryStream<>(map(t -> EntryStream.ofEntry(keyMapper.apply(t), valueMapper.apply(t)))); @@ -117,7 +118,7 @@ public interface TransformableWrappedStream 键类型 * @return {@link EntryStream}实例 */ - default EntryStream toEntries(Function keyMapper) { + default EntryStream toEntries(final Function keyMapper) { return toEntries(keyMapper, Function.identity()); } @@ -273,7 +274,7 @@ public interface TransformableWrappedStream */ - default S peekIdx(BiConsumer action) { + default S peekIdx(final BiConsumer action) { Objects.requireNonNull(action); if (isParallel()) { return peek(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); @@ -331,7 +332,7 @@ public interface TransformableWrappedStream iterable) { + default S append(final Iterable iterable) { if (IterUtil.isEmpty(iterable)) { return wrapping(this); } @@ -345,7 +346,7 @@ public interface TransformableWrappedStream iterable) { + default S prepend(final Iterable iterable) { if (IterUtil.isEmpty(iterable)) { return wrapping(this); } @@ -484,11 +485,11 @@ public interface TransformableWrappedStream> childrenGetter, BiConsumer> childrenSetter) { + default S flatTree(final Function> childrenGetter, final BiConsumer> childrenSetter) { Objects.requireNonNull(childrenGetter); Objects.requireNonNull(childrenSetter); - MutableObj>> recursiveRef = new MutableObj<>(); - Function> recursive = e -> EasyStream.of(childrenGetter.apply(e)) + final MutableObj>> recursiveRef = new MutableObj<>(); + final Function> recursive = e -> EasyStream.of(childrenGetter.apply(e)) .flat(recursiveRef.get()) .unshift(e); recursiveRef.set(recursive); diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java index 545797323..4a53bf5d4 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java @@ -42,7 +42,7 @@ public interface WrappedStream> extends Stream< * @param source 被包装的流 * @return S */ - S wrapping(Stream source); + S wrapping(final Stream source); /** * 过滤元素,返回与指定断言匹配的元素组成的流 @@ -52,7 +52,7 @@ public interface WrappedStream> extends Stream< * @return 返回叠加过滤操作后的流 */ @Override - default S filter(Predicate predicate) { + default S filter(final Predicate predicate) { Objects.requireNonNull(predicate); return wrapping(stream().filter(predicate)); } @@ -65,7 +65,7 @@ public interface WrappedStream> extends Stream< * @return 叠加操作后元素类型全为int的流 */ @Override - default IntStream mapToInt(ToIntFunction mapper) { + default IntStream mapToInt(final ToIntFunction mapper) { Objects.requireNonNull(mapper); return stream().mapToInt(mapper); } @@ -78,7 +78,7 @@ public interface WrappedStream> extends Stream< * @return 叠加操作后元素类型全为long的流 */ @Override - default LongStream mapToLong(ToLongFunction mapper) { + default LongStream mapToLong(final ToLongFunction mapper) { Objects.requireNonNull(mapper); return stream().mapToLong(mapper); } @@ -91,7 +91,7 @@ public interface WrappedStream> extends Stream< * @return 叠加操作后元素类型全为double的流 */ @Override - default DoubleStream mapToDouble(ToDoubleFunction mapper) { + default DoubleStream mapToDouble(final ToDoubleFunction mapper) { Objects.requireNonNull(mapper); return stream().mapToDouble(mapper); } @@ -104,7 +104,7 @@ public interface WrappedStream> extends Stream< * @return 返回叠加拆分操作后的IntStream */ @Override - default IntStream flatMapToInt(Function mapper) { + default IntStream flatMapToInt(final Function mapper) { Objects.requireNonNull(mapper); return stream().flatMapToInt(mapper); } @@ -117,7 +117,7 @@ public interface WrappedStream> extends Stream< * @return 返回叠加拆分操作后的LongStream */ @Override - default LongStream flatMapToLong(Function mapper) { + default LongStream flatMapToLong(final Function mapper) { Objects.requireNonNull(mapper); return stream().flatMapToLong(mapper); } @@ -130,7 +130,7 @@ public interface WrappedStream> extends Stream< * @return 返回叠加拆分操作后的DoubleStream */ @Override - default DoubleStream flatMapToDouble(Function mapper) { + default DoubleStream flatMapToDouble(final Function mapper) { Objects.requireNonNull(mapper); return stream().flatMapToDouble(mapper); } @@ -169,7 +169,7 @@ public interface WrappedStream> extends Stream< * @return 一个元素按指定的Comparator排序的流 */ @Override - default S sorted(Comparator comparator) { + default S sorted(final Comparator comparator) { Objects.requireNonNull(comparator); return wrapping(stream().sorted(comparator)); } @@ -192,7 +192,7 @@ public interface WrappedStream> extends Stream< * } */ @Override - default S peek(Consumer action) { + default S peek(final Consumer action) { Objects.requireNonNull(action); return wrapping(stream().peek(action)); } @@ -205,7 +205,7 @@ public interface WrappedStream> extends Stream< * @return 截取后的流 */ @Override - default S limit(long maxSize) { + default S limit(final long maxSize) { return wrapping(stream().limit(maxSize)); } @@ -217,7 +217,7 @@ public interface WrappedStream> extends Stream< * @return 丢弃前面n个元素后的剩余元素组成的流 */ @Override - default S skip(long n) { + default S skip(final long n) { return wrapping(stream().skip(n)); } @@ -228,7 +228,7 @@ public interface WrappedStream> extends Stream< * @param action 操作 */ @Override - default void forEach(Consumer action) { + default void forEach(final Consumer action) { Objects.requireNonNull(action); stream().forEach(action); } @@ -240,7 +240,7 @@ public interface WrappedStream> extends Stream< * @param action 操作 */ @Override - default void forEachOrdered(Consumer action) { + default void forEachOrdered(final Consumer action) { Objects.requireNonNull(action); stream().forEachOrdered(action); } @@ -266,7 +266,7 @@ public interface WrappedStream> extends Stream< * @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常 */ @Override - default
A[] toArray(IntFunction generator) { + default A[] toArray(final IntFunction generator) { Objects.requireNonNull(generator); return stream().toArray(generator); } @@ -292,7 +292,7 @@ public interface WrappedStream> extends Stream< * @return 聚合计算后的值 */ @Override - default T reduce(T identity, BinaryOperator accumulator) { + default T reduce(final T identity, final BinaryOperator accumulator) { Objects.requireNonNull(accumulator); return stream().reduce(identity, accumulator); } @@ -328,7 +328,7 @@ public interface WrappedStream> extends Stream< * @see #max(Comparator) */ @Override - default Optional reduce(BinaryOperator accumulator) { + default Optional reduce(final BinaryOperator accumulator) { Objects.requireNonNull(accumulator); return stream().reduce(accumulator); } @@ -346,7 +346,7 @@ public interface WrappedStream> extends Stream< * @see #reduce(Object, BinaryOperator) */ @Override - default U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) { + default U reduce(final U identity, final BiFunction accumulator, final BinaryOperator combiner) { Objects.requireNonNull(accumulator); Objects.requireNonNull(combiner); return stream().reduce(identity, accumulator, combiner); @@ -366,7 +366,7 @@ public interface WrappedStream> extends Stream< * } */ @Override - default R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) { + default R collect(final Supplier supplier, final BiConsumer accumulator, final BiConsumer combiner) { Objects.requireNonNull(supplier); Objects.requireNonNull(accumulator); Objects.requireNonNull(combiner); @@ -383,7 +383,7 @@ public interface WrappedStream> extends Stream< * @return 收集后的容器 */ @Override - default R collect(Collector collector) { + default R collect(final Collector collector) { Objects.requireNonNull(collector); return stream().collect(collector); } @@ -395,7 +395,7 @@ public interface WrappedStream> extends Stream< * @return 最小值 */ @Override - default Optional min(Comparator comparator) { + default Optional min(final Comparator comparator) { Objects.requireNonNull(comparator); return stream().min(comparator); } @@ -407,7 +407,7 @@ public interface WrappedStream> extends Stream< * @return 最大值 */ @Override - default Optional max(Comparator comparator) { + default Optional max(final Comparator comparator) { Objects.requireNonNull(comparator); return stream().max(comparator); } @@ -429,7 +429,7 @@ public interface WrappedStream> extends Stream< * @return 是否有任何一个元素满足给定断言 */ @Override - default boolean anyMatch(Predicate predicate) { + default boolean anyMatch(final Predicate predicate) { Objects.requireNonNull(predicate); return stream().anyMatch(predicate); } @@ -441,7 +441,7 @@ public interface WrappedStream> extends Stream< * @return 是否所有元素满足给定断言 */ @Override - default boolean allMatch(Predicate predicate) { + default boolean allMatch(final Predicate predicate) { Objects.requireNonNull(predicate); return stream().allMatch(predicate); } @@ -453,7 +453,7 @@ public interface WrappedStream> extends Stream< * @return 是否没有元素满足给定断言 */ @Override - default boolean noneMatch(Predicate predicate) { + default boolean noneMatch(final Predicate predicate) { Objects.requireNonNull(predicate); return stream().noneMatch(predicate); } diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java index e1386591b..b32481663 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java @@ -181,7 +181,7 @@ public class EasyStreamTest { Assert.assertEquals(collect2, distinctBy2); Assert.assertEquals( - 4, EasyStream.of(1, 2, 2, null, 3, null).parallel(true).distinct(t -> Objects.isNull(t) ? null : t.toString()).count() + 4, EasyStream.of(1, 2, 2, null, 3, null).parallel(true).distinct(t -> Objects.isNull(t) ? null : t.toString()).sequential().count() ); } From 924a1c6a2213e05046954e16497f947199a59573 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 6 Sep 2022 12:44:48 +0800 Subject: [PATCH 5/7] fix code --- .../AbstractEnhancedWrappedStreamTest.java | 120 +++++++++--------- .../cn/hutool/core/stream/EasyStreamTest.java | 4 +- .../hutool/core/stream/EntryStreamTest.java | 86 ++++++------- 3 files changed, 105 insertions(+), 105 deletions(-) diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java index a509d872b..700b3779f 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/AbstractEnhancedWrappedStreamTest.java @@ -25,43 +25,43 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testToList() { - List list = asList(1, 2, 3); - List toList = wrap(list).toList(); + final List list = asList(1, 2, 3); + final List toList = wrap(list).toList(); Assert.assertEquals(list, toList); } @Test public void testToUnmodifiableList() { - List list = wrap(1, 2, 3) + final List list = wrap(1, 2, 3) .toUnmodifiableList(); Assert.assertThrows(UnsupportedOperationException.class, () -> list.remove(0)); } @Test public void testToSet() { - List list = asList(1, 2, 3); + final List list = asList(1, 2, 3); Set toSet = wrap(list).map(String::valueOf).toSet(); Assert.assertEquals(new HashSet<>(asList("1", "2", "3")), toSet); } @Test public void testToUnmodifiableSet() { - Set set = wrap(1, 2, 3) + final Set set = wrap(1, 2, 3) .toUnmodifiableSet(); Assert.assertThrows(UnsupportedOperationException.class, () -> set.remove(0)); } @Test public void testToCollection() { - List list = asList(1, 2, 3); - List toCollection = wrap(list).map(String::valueOf).toColl(LinkedList::new); + final List list = asList(1, 2, 3); + final List toCollection = wrap(list).map(String::valueOf).toColl(LinkedList::new); Assert.assertEquals(asList("1", "2", "3"), toCollection); } @Test public void testToMap() { - List list = asList(1, 2, 3); - Map identityMap = wrap(list).toMap(String::valueOf); + final List list = asList(1, 2, 3); + final Map identityMap = wrap(list).toMap(String::valueOf); Assert.assertEquals(new HashMap() {{ put("1", 1); put("2", 2); @@ -71,17 +71,17 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testToUnmodifiableMap() { - Map map1 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity()); + final Map map1 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity()); Assert.assertThrows(UnsupportedOperationException.class, () -> map1.remove(1)); - Map map2 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity(), (t1, t2) -> t1); + final Map map2 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity(), (t1, t2) -> t1); Assert.assertThrows(UnsupportedOperationException.class, () -> map2.remove(1)); } @Test public void testToZip() { - List orders = asList(1, 2, 3); - List list = asList("dromara", "hutool", "sweet"); - Map toZip = wrap(orders).toZip(list); + final List orders = asList(1, 2, 3); + final List list = asList("dromara", "hutool", "sweet"); + final Map toZip = wrap(orders).toZip(list); Assert.assertEquals(new HashMap() {{ put(1, "dromara"); put(2, "hutool"); @@ -91,38 +91,38 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testTransform() { - List list = wrap(1, 2, 3).transform(Wrapper::toList).orElse(null); + final List list = wrap(1, 2, 3).transform(Wrapper::toList).orElse(null); Assert.assertEquals(asList(1, 2, 3), list); } @Test public void testFindFirst() { - List list = asList(1, 2, 3); + final List list = asList(1, 2, 3); Assert.assertEquals((Integer)1, wrap(list).findFirst(t -> (t & 1) == 1).orElse(null)); Assert.assertEquals((Integer)1, wrap(list).filter(t -> (t & 1) == 1).findFirst().orElse(null)); } @Test public void testFindFirstIdx() { - List list = asList(1, 2, 3); + final List list = asList(1, 2, 3); Assert.assertEquals(1, wrap(list).findFirstIdx(t -> (t & 1) == 0)); } @Test public void testFindLast() { - List list = asList(1, 2, 3); + final List list = asList(1, 2, 3); Assert.assertEquals((Integer)3, wrap(list).findLast(t -> (t & 1) == 1).orElse(null)); } @Test public void testFindLastIdx() { - List list = asList(1, 2, 3); + final List list = asList(1, 2, 3); Assert.assertEquals(1, wrap(list).findLastIdx(t -> (t & 1) == 0)); } @Test public void testAt() { - List list = asList(1, 2, 3); + final List list = asList(1, 2, 3); Assert.assertEquals((Integer)3, wrap(list).at(2).orElse(null)); } @@ -140,8 +140,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testJoining() { - List list = asList(1, 2, 3); - String joining = wrap(list).join(); + final List list = asList(1, 2, 3); + final String joining = wrap(list).join(); Assert.assertEquals("123", joining); Assert.assertEquals("1,2,3", wrap(list).join(",")); Assert.assertEquals("(1,2,3)", wrap(list).join(",", "(", ")")); @@ -149,8 +149,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testGrouping() { - List list = asList(1, 2, 3); - Map> map = new HashMap>() {{ + final List list = asList(1, 2, 3); + final Map> map = new HashMap>() {{ put("1", singletonList(1)); put("2", singletonList(2)); put("3", singletonList(3)); @@ -166,8 +166,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testPartitioning() { - List list = asList(1, 2, 3); - Map> map = new HashMap>() {{ + final List list = asList(1, 2, 3); + final Map> map = new HashMap>() {{ put(Boolean.TRUE, singletonList(2)); put(Boolean.FALSE, asList(1, 3)); }}; @@ -180,8 +180,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testForEachIdx() { - List elements = new ArrayList<>(); - List indexes = new ArrayList<>(); + final List elements = new ArrayList<>(); + final List indexes = new ArrayList<>(); wrap(1, 2, 3).forEachIdx((t, i) -> { elements.add(t); indexes.add(i); @@ -192,8 +192,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testForEachOrderedIdx() { - List elements = new ArrayList<>(); - List indexes = new ArrayList<>(); + final List elements = new ArrayList<>(); + final List indexes = new ArrayList<>(); wrap(1, 2, 3).forEachOrderedIdx((t, i) -> { elements.add(t); indexes.add(i); @@ -204,33 +204,33 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testForEachOrdered() { - List elements = new ArrayList<>(); + final List elements = new ArrayList<>(); wrap(1, 2, 3).forEachOrdered(elements::add); Assert.assertEquals(asList(1, 2, 3), elements); } @Test public void testForEach() { - List elements = new ArrayList<>(); + final List elements = new ArrayList<>(); wrap(1, 2, 3).forEach(elements::add); Assert.assertEquals(asList(1, 2, 3), elements); } @Test public void testMapToInt() { - int[] array = wrap(1, 2, 3).mapToInt(Integer::intValue).toArray(); + final int[] array = wrap(1, 2, 3).mapToInt(Integer::intValue).toArray(); Assert.assertArrayEquals(new int[] {1, 2, 3}, array); } @Test public void testMapToLong() { - long[] array = wrap(1L, 2L, 3L).mapToLong(Long::intValue).toArray(); + final long[] array = wrap(1L, 2L, 3L).mapToLong(Long::intValue).toArray(); Assert.assertArrayEquals(new long[] {1L, 2L, 3L}, array); } @Test public void testMapToDouble() { - double[] array = wrap(1d, 2d, 3d).mapToDouble(Double::intValue).toArray(); + final double[] array = wrap(1d, 2d, 3d).mapToDouble(Double::intValue).toArray(); Assert.assertEquals(1d, array[0], 0.01); Assert.assertEquals(2d, array[1], 0.01); Assert.assertEquals(3d, array[2], 0.01); @@ -238,19 +238,19 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testFlatMapToInt() { - int[] array = wrap(1, 2, 3).flatMapToInt(IntStream::of).toArray(); + final int[] array = wrap(1, 2, 3).flatMapToInt(IntStream::of).toArray(); Assert.assertArrayEquals(new int[] {1, 2, 3}, array); } @Test public void testFlatMapToLong() { - long[] array = wrap(1L, 2L, 3L).flatMapToLong(LongStream::of).toArray(); + final long[] array = wrap(1L, 2L, 3L).flatMapToLong(LongStream::of).toArray(); Assert.assertArrayEquals(new long[] {1L, 2L, 3L}, array); } @Test public void testFlatMapToDouble() { - double[] array = wrap(1d, 2d, 3d).flatMapToDouble(DoubleStream::of).toArray(); + final double[] array = wrap(1d, 2d, 3d).flatMapToDouble(DoubleStream::of).toArray(); Assert.assertEquals(1d, array[0], 0.01); Assert.assertEquals(2d, array[1], 0.01); Assert.assertEquals(3d, array[2], 0.01); @@ -258,21 +258,21 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testSorted() { - List list = wrap(3, 1, 2).sorted().toList(); + final List list = wrap(3, 1, 2).sorted().toList(); Assert.assertEquals(asList(1, 2, 3), list); } @Test public void testPeek() { - List elements = new ArrayList<>(); + final List elements = new ArrayList<>(); wrap(1, 2, 3).peek(elements::add).exec(); Assert.assertEquals(asList(1, 2, 3), elements); } @Test public void testPeekIdx() { - List elements = new ArrayList<>(); - List indexes = new ArrayList<>(); + final List elements = new ArrayList<>(); + final List indexes = new ArrayList<>(); wrap(1, 2, 3).peekIdx((t, i) -> { elements.add(t); indexes.add(i); @@ -280,8 +280,8 @@ public class AbstractEnhancedWrappedStreamTest { Assert.assertEquals(asList(1, 2, 3), elements); Assert.assertEquals(asList(0, 1, 2), indexes); - Set elements2 = new HashSet<>(); - Set indexes2 = new HashSet<>(); + final Set elements2 = new HashSet<>(); + final Set indexes2 = new HashSet<>(); wrap(1, 2, null).parallel().peekIdx((t, i) -> { elements2.add(t); indexes2.add(i); @@ -292,13 +292,13 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testLimit() { - List list = wrap(1, 2, 3).limit(2L).toList(); + final List list = wrap(1, 2, 3).limit(2L).toList(); Assert.assertEquals(asList(1, 2), list); } @Test public void testSkip() { - List list = wrap(1, 2, 3).skip(1L).toList(); + final List list = wrap(1, 2, 3).skip(1L).toList(); Assert.assertEquals(asList(2, 3), list); } @@ -366,8 +366,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testIterator() { - Iterator iter1 = Stream.of(1, 2, 3).iterator(); - Iterator iter2 = wrap(1, 2, 3).iterator(); + final Iterator iter1 = Stream.of(1, 2, 3).iterator(); + final Iterator iter2 = wrap(1, 2, 3).iterator(); while (iter1.hasNext() && iter2.hasNext()) { Assert.assertEquals(iter1.next(), iter2.next()); } @@ -375,8 +375,8 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testSpliterator() { - Spliterator iter1 = Stream.of(1, 2, 3).spliterator(); - Spliterator iter2 = wrap(1, 2, 3).spliterator(); + final Spliterator iter1 = Stream.of(1, 2, 3).spliterator(); + final Spliterator iter2 = wrap(1, 2, 3).spliterator(); Assert.assertEquals(iter1.trySplit().estimateSize(), iter2.trySplit().estimateSize()); } @@ -397,14 +397,14 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testOnClose() { - AtomicBoolean atomicBoolean = new AtomicBoolean(false); + final AtomicBoolean atomicBoolean = new AtomicBoolean(false); wrap(Stream.of(1, 2, 3).onClose(() -> atomicBoolean.set(true))).close(); Assert.assertTrue(atomicBoolean.get()); } @Test public void testClose() { - Wrapper stream = wrap(Stream.of(1, 2, 3)); + final Wrapper stream = wrap(Stream.of(1, 2, 3)); stream.close(); Assert.assertThrows(IllegalStateException.class, stream::exec); } @@ -500,7 +500,7 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testFilterIdx() { - List indexes = new ArrayList<>(); + final List indexes = new ArrayList<>(); Assert.assertEquals( asList(1, 3), wrap(1, 2, 3).filterIdx((t, i) -> { @@ -527,7 +527,7 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testFlatMapIdx() { - List indexes = new ArrayList<>(); + final List indexes = new ArrayList<>(); Assert.assertEquals( asList(1, 2, 3), wrap(1, 2, 3).flatMapIdx((t, i) -> { indexes.add(i); @@ -553,7 +553,7 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testFlatTree() { - Tree root = new Tree(1, asList(new Tree(2, asList(new Tree(3, Collections.emptyList()))))); + final Tree root = new Tree(1, asList(new Tree(2, asList(new Tree(3, Collections.emptyList()))))); Assert.assertEquals(3L, wrap(root).flatTree(Tree::getChildren, Tree::setChildren).count()); } @@ -573,7 +573,7 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testMapIdx() { - List indexes = new ArrayList<>(); + final List indexes = new ArrayList<>(); Assert.assertEquals( asList("1", "2", "3"), wrap(1, 2, 3).mapIdx((t, i) -> { indexes.add(i); @@ -595,25 +595,25 @@ public class AbstractEnhancedWrappedStreamTest { @Test public void testHashCode() { - Stream stream = Stream.of(1, 2, 3); + final Stream stream = Stream.of(1, 2, 3); Assert.assertEquals(stream.hashCode(), wrap(stream).hashCode()); } @Test public void testEquals() { - Stream stream = Stream.of(1, 2, 3); + final Stream stream = Stream.of(1, 2, 3); Assert.assertEquals(wrap(stream), stream); } @Test public void testToString() { - Stream stream = Stream.of(1, 2, 3); + final Stream stream = Stream.of(1, 2, 3); Assert.assertEquals(stream.toString(), wrap(stream).toString()); } @Test public void testToEntries() { - Map expect = new HashMap(){{ + final Map expect = new HashMap(){{ put(1, 1); put(2, 2); put(3, 3); diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java index dfe056b1f..26fa81ef8 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EasyStreamTest.java @@ -22,8 +22,8 @@ public class EasyStreamTest { @Test public void testConcat() { - Stream stream1 = Stream.of(1, 2); - Stream stream2 = Stream.of(3, 4); + final Stream stream1 = Stream.of(1, 2); + final Stream stream2 = Stream.of(3, 4); Assert.assertEquals(4, EasyStream.concat(stream1, stream2).count()); } diff --git a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java index 532460166..7d23e00d1 100644 --- a/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/stream/EntryStreamTest.java @@ -46,12 +46,12 @@ public class EntryStreamTest { @Test public void testOf() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put("1", "1"); Assert.assertEquals(1, EntryStream.of(map).count()); Assert.assertEquals(0, EntryStream.of((Map)null).count()); - Set> entries = new HashSet<>(); + final Set> entries = new HashSet<>(); entries.add(new Entry<>(1, 1)); entries.add(null); Assert.assertEquals(2, EntryStream.of(entries).count()); @@ -61,7 +61,7 @@ public class EntryStreamTest { Assert.assertEquals(2, new EntryStream<>(entries.stream()).count()); Assert.assertThrows(NullPointerException.class, () -> new EntryStream<>(null)); - Iterable iterable = Arrays.asList(1, 2, null); + final Iterable iterable = Arrays.asList(1, 2, null); Assert.assertEquals(3, EntryStream.of(iterable, Function.identity(), Function.identity()).count()); Assert.assertEquals(0, EntryStream.of(null, Function.identity(), Function.identity()).count()); } @@ -73,7 +73,7 @@ public class EntryStreamTest { @Test public void testDistinctByKey() { - long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) + final long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .distinctByKey() .count(); Assert.assertEquals(2, count); @@ -81,7 +81,7 @@ public class EntryStreamTest { @Test public void testDistinctByValue() { - long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) + final long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .distinctByValue() .count(); Assert.assertEquals(2, count); @@ -89,7 +89,7 @@ public class EntryStreamTest { @Test public void testFilter() { - long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) + final long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .filter((k, v) -> k == 1 && v == 1) .count(); Assert.assertEquals(1, count); @@ -97,7 +97,7 @@ public class EntryStreamTest { @Test public void testFilterByKey() { - long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) + final long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .filterByKey(k -> k == 1) .count(); Assert.assertEquals(2, count); @@ -105,7 +105,7 @@ public class EntryStreamTest { @Test public void testFilterByValue() { - long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) + final long count = EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .filterByValue(v -> v == 1) .count(); Assert.assertEquals(2, count); @@ -113,7 +113,7 @@ public class EntryStreamTest { @Test public void testPeekKey() { - List keys = new ArrayList<>(); + final List keys = new ArrayList<>(); EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .peekKey(keys::add) .count(); @@ -122,7 +122,7 @@ public class EntryStreamTest { @Test public void testPeekValue() { - List values = new ArrayList<>(); + final List values = new ArrayList<>(); EntryStream.of(Arrays.asList(new Entry<>(1, 1), new Entry<>(1, 2), new Entry<>(2, 1), new Entry<>(2, 2))) .peekValue(values::add) .count(); @@ -153,11 +153,11 @@ public class EntryStreamTest { @Test public void testAppend() { - Map map1 = new HashMap(){{ + final Map map1 = new HashMap(){{ put(1, 1); put(2, 2); }}; - Map map2 = new HashMap(){{ + final Map map2 = new HashMap(){{ put(3, 3); put(4, 4); }}; @@ -175,11 +175,11 @@ public class EntryStreamTest { @Test public void testPrepend() { - Map map1 = new HashMap(){{ + final Map map1 = new HashMap(){{ put(1, 1); put(2, 2); }}; - Map map2 = new HashMap(){{ + final Map map2 = new HashMap(){{ put(3, 3); put(4, 4); }}; @@ -197,7 +197,7 @@ public class EntryStreamTest { @Test public void testSortByKey() { - List> entries = EntryStream.of(Arrays.asList(new Entry<>(3, 1), new Entry<>(2, 1), new Entry<>(4, 1), new Entry<>(1, 1))) + final List> entries = EntryStream.of(Arrays.asList(new Entry<>(3, 1), new Entry<>(2, 1), new Entry<>(4, 1), new Entry<>(1, 1))) .sortByKey(Comparator.comparingInt(Integer::intValue)) .collect(Collectors.toList()); Assert.assertEquals( @@ -208,7 +208,7 @@ public class EntryStreamTest { @Test public void testSortByValue() { - List> entries = EntryStream.of(Arrays.asList(new Entry<>(4, 4), new Entry<>(2, 2), new Entry<>(1, 1), new Entry<>(3, 3))) + final List> entries = EntryStream.of(Arrays.asList(new Entry<>(4, 4), new Entry<>(2, 2), new Entry<>(1, 1), new Entry<>(3, 3))) .sortByValue(Comparator.comparingInt(Integer::intValue)) .collect(Collectors.toList()); Assert.assertEquals( @@ -219,7 +219,7 @@ public class EntryStreamTest { @Test public void testToValueStream() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -230,7 +230,7 @@ public class EntryStreamTest { @Test public void testToKeyStream() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -241,7 +241,7 @@ public class EntryStreamTest { @Test public void testCollectKey() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -251,7 +251,7 @@ public class EntryStreamTest { @Test public void testCollectValue() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -261,7 +261,7 @@ public class EntryStreamTest { @Test public void testMapKeys() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -276,7 +276,7 @@ public class EntryStreamTest { @Test public void testMapValues() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -291,7 +291,7 @@ public class EntryStreamTest { @Test public void testMap() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -311,7 +311,7 @@ public class EntryStreamTest { @Test public void testFlatMap() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -323,11 +323,11 @@ public class EntryStreamTest { @Test public void testFlatMapValue() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put("class1", 1); map.put("class2", 2); map.put("class3", 3); - List values = EntryStream.of(map) + final List values = EntryStream.of(map) .flatMapKey(k -> Stream.of(k + "'s student1", k + "'s student2")) .map((k, v) -> v + "=" + k) .sorted() @@ -344,11 +344,11 @@ public class EntryStreamTest { @Test public void testInverse() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); - List results = EntryStream.of(map) + final List results = EntryStream.of(map) .inverse() .map((k, v) -> k + "=" + v) .collect(Collectors.toList()); @@ -360,11 +360,11 @@ public class EntryStreamTest { @Test public void testFlatMapKey() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, "class1"); map.put(2, "class2"); map.put(3, "class3"); - List values = EntryStream.of(map) + final List values = EntryStream.of(map) .flatMapValue(v -> Stream.of(v + "'s student1", v + "'s student2")) .map((k, v) -> k + "=" + v) .collect(Collectors.toList()); @@ -380,13 +380,13 @@ public class EntryStreamTest { @Test public void testForEach() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); - List keys = new ArrayList<>(); - List values = new ArrayList<>(); + final List keys = new ArrayList<>(); + final List values = new ArrayList<>(); EntryStream.of(map).forEach((k ,v) -> { keys.add(k); values.add(v); @@ -397,7 +397,7 @@ public class EntryStreamTest { @Test public void testToMap() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -414,7 +414,7 @@ public class EntryStreamTest { @Test public void testToTable() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -438,7 +438,7 @@ public class EntryStreamTest { @Test public void testToTableByKey() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -462,7 +462,7 @@ public class EntryStreamTest { @Test public void testToTableByValue() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, 1); map.put(2, 2); map.put(3, 3); @@ -486,19 +486,19 @@ public class EntryStreamTest { @Test public void testGroupByKey() { - Map> map1 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity()) + final Map> map1 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity()) .groupByKey(); Assert.assertEquals(2, map1.size()); Assert.assertEquals(Arrays.asList(1, 1), map1.get(1)); Assert.assertEquals(Arrays.asList(2, 2), map1.get(2)); - Map> map2 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity()) + final Map> map2 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity()) .groupByKey(Collectors.toSet()); Assert.assertEquals(2, map2.size()); Assert.assertEquals(Collections.singleton(1), map2.get(1)); Assert.assertEquals(Collections.singleton(2), map2.get(2)); - Map> map3 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity()) + final Map> map3 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity()) .groupByKey(LinkedHashMap::new, Collectors.toSet()); Assert.assertEquals(2, map3.size()); Assert.assertEquals(Collections.singleton(1), map3.get(1)); @@ -531,7 +531,7 @@ public class EntryStreamTest { @Test public void testNonNull() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, null); map.put(null, 1); Assert.assertEquals(0, EntryStream.of(map).nonNullKeyValue().count()); @@ -539,7 +539,7 @@ public class EntryStreamTest { @Test public void testKeyNonNull() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, null); map.put(null, 1); Assert.assertEquals(1, EntryStream.of(map).nonNullKey().count()); @@ -547,7 +547,7 @@ public class EntryStreamTest { @Test public void testValueNonNull() { - Map map = new HashMap<>(); + final Map map = new HashMap<>(); map.put(1, null); map.put(null, 1); Assert.assertEquals(1, EntryStream.of(map).nonNullValue().count()); From d58502ad76b833692504f56bf98debaaad77d539 Mon Sep 17 00:00:00 2001 From: huangchengxing <841396397@qq.com> Date: Tue, 6 Sep 2022 13:32:32 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stream/AbstractEnhancedWrappedStream.java | 6 +- .../cn/hutool/core/stream/CollectorUtil.java | 2 +- .../cn/hutool/core/stream/EasyStream.java | 8 +- .../cn/hutool/core/stream/EntryStream.java | 26 +++--- .../core/stream/TerminableWrappedStream.java | 38 ++++---- .../stream/TransformableWrappedStream.java | 44 ++++----- .../cn/hutool/core/stream/WrappedStream.java | 93 ++++++++++--------- .../AbstractEnhancedWrappedStreamTest.java | 8 +- 8 files changed, 113 insertions(+), 112 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java index aed748389..25e4bc2b3 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/AbstractEnhancedWrappedStream.java @@ -25,7 +25,7 @@ public abstract class AbstractEnhancedWrappedStream stream() { + public Stream unwrap() { return stream; } @@ -33,10 +33,10 @@ public abstract class AbstractEnhancedWrappedStream stream) { - this.stream = Objects.requireNonNull(stream, "stream must not null"); + this.stream = Objects.requireNonNull(stream, "unwrap must not null"); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java index 7ec8c70c2..0899a63e6 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/CollectorUtil.java @@ -102,7 +102,7 @@ public class CollectorUtil { final K key = Opt.ofNullable(t).map(classifier).orElse(null); final A container = m.computeIfAbsent(key, k -> downstreamSupplier.get()); if (ArrayUtil.isArray(container) || Objects.nonNull(t)) { - // 如果是数组类型,不需要判空,场景——分组后需要使用:java.util.stream.Collectors.counting 求null元素个数 + // 如果是数组类型,不需要判空,场景——分组后需要使用:java.util.unwrap.Collectors.counting 求null元素个数 downstreamAccumulator.accept(container, t); } }; diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java index 1603788d7..0d5b22f14 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EasyStream.java @@ -15,7 +15,7 @@ import java.util.stream.StreamSupport; /** *

单元素的扩展流实现。基于原生Stream进行了封装和增强。
- * 作者经对比了vavr、eclipse-collection、stream-ex以及其他语言的api,结合日常使用习惯,进行封装和拓展 + * 作者经对比了vavr、eclipse-collection、unwrap-ex以及其他语言的api,结合日常使用习惯,进行封装和拓展 * Stream为集合提供了一些易用api,它让开发人员能使用声明式编程的方式去编写代码。 * *

中间操作和结束操作

@@ -67,7 +67,7 @@ public class EasyStream extends AbstractEnhancedWrappedStream 元素的类型 - * @return a stream builder + * @return a unwrap builder */ public static Builder builder() { return new Builder() { @@ -262,7 +262,7 @@ public class EasyStream extends AbstractEnhancedWrappedStream wrapping(final Stream stream) { + public EasyStream wrap(final Stream stream) { return new EasyStream<>(stream); } @@ -359,7 +359,7 @@ public class EasyStream extends AbstractEnhancedWrappedStream extends Consumer, cn.hutool.core.builder.Builder> { /** - * Adds an element to the stream being built. + * Adds an element to the unwrap being built. * * @param t the element to add * @return {@code this} builder diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java index 322379071..47c7ba24e 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java @@ -159,7 +159,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream distinctByKey() { // FIXME fix happen NPE when has null key final Set accessed = new ConcurrentHashSet<>(16); - return wrapping(stream.filter(e -> { + return wrap(stream.filter(e -> { final K key = e.getKey(); if (accessed.contains(key)) { return false; @@ -177,7 +177,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream distinctByValue() { // FIXME fix happen NPE when has null value final Set accessed = new ConcurrentHashSet<>(16); - return wrapping(stream.filter(e -> { + return wrap(stream.filter(e -> { final V val = e.getValue(); if (accessed.contains(val)) { return false; @@ -301,7 +301,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream push(final K key, final V value) { - return wrapping(Stream.concat(stream, Stream.of(ofEntry(key, value)))); + return wrap(Stream.concat(stream, Stream.of(ofEntry(key, value)))); } /** @@ -312,7 +312,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream unshift(final K key, final V value) { - return wrapping(Stream.concat(Stream.of(ofEntry(key, value)), stream)); + return wrap(Stream.concat(Stream.of(ofEntry(key, value)), stream)); } /** @@ -328,7 +328,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream> contacted = StreamSupport.stream(entries.spliterator(), isParallel()) .map(EntryStream::ofEntry); - return wrapping(Stream.concat(stream, contacted)); + return wrap(Stream.concat(stream, contacted)); } /** @@ -344,7 +344,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream> contacted = StreamSupport.stream(entries.spliterator(), isParallel()) .map(EntryStream::ofEntry); - return wrapping(Stream.concat(contacted, stream)); + return wrap(Stream.concat(contacted, stream)); } /** @@ -442,9 +442,9 @@ public class EntryStream extends AbstractEnhancedWrappedStream * 效果类似: *
{@code
-	 * // stream = [{a = 1}, {b = 2}, {c = 3}]
-	 * stream.flatMapKey(key -> Stream.of(key + "1", key + "2"));
-	 * // stream = [{a1 = 1}, {a2 = 1}, {b1 = 2}, {b2 = 2}, {c1 = 3}, {c2 = 3}]
+	 * // unwrap = [{a = 1}, {b = 2}, {c = 3}]
+	 * unwrap.flatMapKey(key -> Stream.of(key + "1", key + "2"));
+	 * // unwrap = [{a1 = 1}, {a2 = 1}, {b1 = 2}, {b2 = 2}, {c1 = 3}, {c2 = 3}]
 	 * }
* * @param keyMapper 值转映射方法 @@ -466,9 +466,9 @@ public class EntryStream extends AbstractEnhancedWrappedStream * 效果类似: *
{@code
-	 * // stream = [{a = 1}, {b = 2}, {c = 3}]
-	 * stream.flatMapValue(num -> Stream.of(num, num+1));
-	 * // stream = [{a = 1}, {a = 2}, {b = 2}, {b = 3}, {c = 3}, {c = 4}]
+	 * // unwrap = [{a = 1}, {b = 2}, {c = 3}]
+	 * unwrap.flatMapValue(num -> Stream.of(num, num+1));
+	 * // unwrap = [{a = 1}, {a = 2}, {b = 2}, {b = 3}, {c = 3}, {c = 4}]
 	 * }
* * @param valueMapper 值转映射方法 @@ -755,7 +755,7 @@ public class EntryStream extends AbstractEnhancedWrappedStream wrapping(final Stream> stream) { + public EntryStream wrap(final Stream> stream) { return new EntryStream<>(stream); } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java index 96923cd94..4439d73d5 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TerminableWrappedStream.java @@ -73,7 +73,7 @@ public interface TerminableWrappedStream> C toColl(final Supplier collectionFactory) { Objects.requireNonNull(collectionFactory); - return stream().collect(Collectors.toCollection(collectionFactory)); + return unwrap().collect(Collectors.toCollection(collectionFactory)); } // endregion @@ -181,7 +181,7 @@ public interface TerminableWrappedStream Optional transform(final Function transform) { Objects.requireNonNull(transform); - return Optional.ofNullable(transform.apply(wrapping(this))); + return Optional.ofNullable(transform.apply(wrap(this))); } /** @@ -238,7 +238,7 @@ public interface TerminableWrappedStream findFirst(final Predicate predicate) { Objects.requireNonNull(predicate); - return stream().filter(predicate).findFirst(); + return unwrap().filter(predicate).findFirst(); } /** @@ -253,7 +253,7 @@ public interface TerminableWrappedStream { + unwrap().filter(e -> { index.increment(); return predicate.test(e); }).findFirst(); @@ -377,7 +377,7 @@ public interface TerminableWrappedStream> partitioning(final Predicate predicate) { - return this.partitioning(predicate, ArrayList::new); + default Map> partition(final Predicate predicate) { + return this.partition(predicate, ArrayList::new); } /** @@ -452,10 +452,10 @@ public interface TerminableWrappedStream> Map partitioning(final Predicate predicate, final Supplier collFactory) { - return this.partitioning(predicate, Collectors.toCollection(collFactory)); + default > Map partition(final Predicate predicate, final Supplier collFactory) { + return this.partition(predicate, Collectors.toCollection(collFactory)); } /** @@ -466,10 +466,10 @@ public interface TerminableWrappedStream 返回值类型 * @return map */ - default Map partitioning(final Predicate predicate, final Collector downstream) { + default Map partition(final Predicate predicate, final Collector downstream) { Objects.requireNonNull(predicate); Objects.requireNonNull(downstream); - return stream().collect(Collectors.partitioningBy(predicate, downstream)); + return unwrap().collect(Collectors.partitioningBy(predicate, downstream)); } // endregion @@ -485,10 +485,10 @@ public interface TerminableWrappedStream action) { Objects.requireNonNull(action); if (isParallel()) { - stream().forEach(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); + unwrap().forEach(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); } else { final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); - stream().forEach(e -> action.accept(e, index.incrementAndGet())); + unwrap().forEach(e -> action.accept(e, index.incrementAndGet())); } } @@ -501,10 +501,10 @@ public interface TerminableWrappedStream action) { Objects.requireNonNull(action); if (isParallel()) { - stream().forEachOrdered(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); + unwrap().forEachOrdered(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX)); } else { final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX); - stream().forEachOrdered(e -> action.accept(e, index.incrementAndGet())); + unwrap().forEachOrdered(e -> action.accept(e, index.incrementAndGet())); } } diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java index f26357e42..b689db3fa 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/TransformableWrappedStream.java @@ -57,7 +57,7 @@ public interface TransformableWrappedStream i < size, i -> i + batchSize) .map(skip -> EasyStream.of(list.subList(skip, Math.min(size, skip + batchSize)), isParallel())) .parallel(isParallel()) - .onClose(stream()::close); + .onClose(unwrap()::close); } /** @@ -133,7 +133,7 @@ public interface TransformableWrappedStream elements = stream().collect(Collectors.toList()); - return wrapping(ListUtil.splice(elements, start, deleteCount, items).stream()) + final List elements = unwrap().collect(Collectors.toList()); + return wrap(ListUtil.splice(elements, start, deleteCount, items).stream()) .parallel(isParallel()); } @@ -187,7 +187,7 @@ public interface TransformableWrappedStream predicate) { Objects.requireNonNull(predicate); - return wrapping(StreamUtil.takeWhile(stream(), predicate)); + return wrap(StreamUtil.takeWhile(unwrap(), predicate)); } /** @@ -216,7 +216,7 @@ public interface TransformableWrappedStream predicate) { Objects.requireNonNull(predicate); - return wrapping(StreamUtil.dropWhile(stream(), predicate)); + return wrap(StreamUtil.dropWhile(unwrap(), predicate)); } /** @@ -234,7 +234,7 @@ public interface TransformableWrappedStream { + return EasyStream.of(unwrap().filter(e -> { final F key = keyExtractor.apply(e); if (key == null) { // 已经出现过null值,跳过该值 @@ -250,7 +250,7 @@ public interface TransformableWrappedStream exists = new HashSet<>(); - return EasyStream.of(stream().filter(e -> exists.add(keyExtractor.apply(e)))); + return EasyStream.of(unwrap().filter(e -> exists.add(keyExtractor.apply(e)))); } } @@ -305,11 +305,11 @@ public interface TransformableWrappedStream result = stream(); + Stream result = unwrap(); if (ArrayUtil.isNotEmpty(obj)) { - result = Stream.concat(stream(), Stream.of(obj)); + result = Stream.concat(unwrap(), Stream.of(obj)); } - return wrapping(result); + return wrap(result); } /** @@ -319,11 +319,11 @@ public interface TransformableWrappedStream result = stream(); + Stream result = unwrap(); if (ArrayUtil.isNotEmpty(obj)) { - result = Stream.concat(Stream.of(obj), stream()); + result = Stream.concat(Stream.of(obj), unwrap()); } - return wrapping(result); + return wrap(result); } /** @@ -334,10 +334,10 @@ public interface TransformableWrappedStream iterable) { if (IterUtil.isEmpty(iterable)) { - return wrapping(this); + return wrap(this); } final Stream contacted = StreamSupport.stream(iterable.spliterator(), isParallel()); - return wrapping(Stream.concat(this, contacted)); + return wrap(Stream.concat(this, contacted)); } /** @@ -348,10 +348,10 @@ public interface TransformableWrappedStream iterable) { if (IterUtil.isEmpty(iterable)) { - return wrapping(this); + return wrap(this); } final Stream contacted = StreamSupport.stream(iterable.spliterator(), isParallel()); - return wrapping(Stream.concat(contacted, this)); + return wrap(Stream.concat(contacted, this)); } // endregion @@ -417,7 +417,7 @@ public interface TransformableWrappedStream EasyStream flatMap(final Function> mapper) { Objects.requireNonNull(mapper); - return new EasyStream<>(stream().flatMap(mapper)); + return new EasyStream<>(unwrap().flatMap(mapper)); } /** @@ -493,7 +493,7 @@ public interface TransformableWrappedStream childrenSetter.accept(e, null))); + return wrap(flatMap(recursive).peek(e -> childrenSetter.accept(e, null))); } // endregion @@ -511,7 +511,7 @@ public interface TransformableWrappedStream EasyStream map(final Function mapper) { Objects.requireNonNull(mapper); - return new EasyStream<>(stream().map(mapper)); + return new EasyStream<>(unwrap().map(mapper)); } /** diff --git a/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java b/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java index 4a53bf5d4..8f9399f16 100644 --- a/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java +++ b/hutool-core/src/main/java/cn/hutool/core/stream/WrappedStream.java @@ -5,7 +5,7 @@ import java.util.function.*; import java.util.stream.*; /** - *

{@link Stream}实例的包装器,用于增强原始的{@link Stream},提供一些额外的中间与终端操作
+ *

{@link Stream}实例的包装器,用于增强原始的{@link Stream},提供一些额外的中间与终端操作。
* 默认提供两个可用实现: *