:trollface: 审查、优化

This commit is contained in:
achao 2022-08-06 21:28:05 +08:00 committed by VampireAchao
parent 3608f9dbe0
commit db0e590fb1
9 changed files with 214 additions and 199 deletions

View File

@ -6,7 +6,6 @@ import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.mutable.MutableInt; import cn.hutool.core.lang.mutable.MutableInt;
import cn.hutool.core.lang.mutable.MutableObj; import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.stream.support.StreamHelper;
import cn.hutool.core.text.StrUtil; import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
@ -158,7 +157,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
public static <T> FastStream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) { public static <T> FastStream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
Objects.requireNonNull(next); Objects.requireNonNull(next);
Objects.requireNonNull(hasNext); Objects.requireNonNull(hasNext);
return new FastStream<>(StreamHelper.iterate(seed, hasNext, next)); return new FastStream<>(StreamUtil.iterate(seed, hasNext, next));
} }
/** /**
@ -674,8 +673,8 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
* @param <A> 给定的数组类型 * @param <A> 给定的数组类型
* @return 包含此流元素的指定的数组 * @return 包含此流元素的指定的数组
* @throws ArrayStoreException 如果元素转换失败例如不是该元素类型及其父类则抛出该异常 * @throws ArrayStoreException 如果元素转换失败例如不是该元素类型及其父类则抛出该异常
* 例如以下代码编译正常但运行时会抛出 {@link ArrayStoreException} * 例如以下代码编译正常但运行时会抛出 {@link ArrayStoreException}
* <pre>{@code String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new); }</pre> * <pre>{@code String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new); }</pre>
*/ */
@Override @Override
public <A> A[] toArray(IntFunction<A[]> generator) { public <A> A[] toArray(IntFunction<A[]> generator) {
@ -1423,8 +1422,8 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
* <p> jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p> * <p> jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p>
* <pre>本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code * <pre>本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
* FastStream.iterate(1, i -> i + 1) * FastStream.iterate(1, i -> i + 1)
* .parallel() * .parallel()
* // 顺序执行 * // 顺序执行
* .takeWhile(e -> e < 50) * .takeWhile(e -> e < 50)
* // 并发 * // 并发
* .map(e -> e + 1) * .map(e -> e + 1)
@ -1439,7 +1438,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
*/ */
public FastStream<T> takeWhile(Predicate<? super T> predicate) { public FastStream<T> takeWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate); Objects.requireNonNull(predicate);
return of(StreamHelper.takeWhile(stream, predicate)); return of(StreamUtil.takeWhile(stream, predicate));
} }
/** /**
@ -1459,8 +1458,8 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
* <p> jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p> * <p> jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p>
* <pre>本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code * <pre>本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
* FastStream.iterate(1, i <= 100, i -> i + 1) * FastStream.iterate(1, i <= 100, i -> i + 1)
* .parallel() * .parallel()
* // 顺序执行 * // 顺序执行
* .dropWhile(e -> e < 50) * .dropWhile(e -> e < 50)
* // 并发 * // 并发
* .map(e -> e + 1) * .map(e -> e + 1)
@ -1475,7 +1474,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
*/ */
public FastStream<T> dropWhile(Predicate<? super T> predicate) { public FastStream<T> dropWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate); Objects.requireNonNull(predicate);
return of(StreamHelper.dropWhile(stream, predicate)); return of(StreamUtil.dropWhile(stream, predicate));
} }
/** /**
@ -1490,6 +1489,24 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
return dropWhile(predicate); return dropWhile(predicate);
} }
/**
* 流是否为空
*
* @return 流是否为空
*/
public boolean isEmpty() {
return !findAny().isPresent();
}
/**
* 流是否不为空
*
* @return 流是否不为空
*/
public boolean isNotEmpty() {
return !isEmpty();
}
public interface FastStreamBuilder<T> extends Consumer<T>, cn.hutool.core.builder.Builder<FastStream<T>> { public interface FastStreamBuilder<T> extends Consumer<T>, cn.hutool.core.builder.Builder<FastStream<T>> {
/** /**

View File

@ -3,6 +3,9 @@ package cn.hutool.core.stream;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.stream.spliterators.DropWhileSpliterator;
import cn.hutool.core.stream.spliterators.IterateSpliterator;
import cn.hutool.core.stream.spliterators.TakeWhileSpliterator;
import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.CharsetUtil;
import java.io.File; import java.io.File;
@ -10,16 +13,20 @@ import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport; import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;
/** /**
* {@link Stream} 工具类 * {@link Stream} 工具类
* *
* @author looly * @author looly emptypoint VampireAchao
* @since 5.6.7 * @since 5.6.7
*/ */
public class StreamUtil { public class StreamUtil {
@ -141,4 +148,84 @@ public class StreamUtil {
final Function<T, ? extends CharSequence> toStringFunc) { final Function<T, ? extends CharSequence> toStringFunc) {
return stream.collect(CollectorUtil.joining(delimiter, toStringFunc)); return stream.collect(CollectorUtil.joining(delimiter, toStringFunc));
} }
/**
* 返回无限有序流
* 该流由 初始值 然后判断条件 以及执行 迭代函数 进行迭代获取到元素
*
* @param <T> 元素类型
* @param seed 初始值
* @param hasNext 条件值
* @param next 用上一个元素作为参数执行并返回一个新的元素
* @return 无限有序流
*/
public static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
requireNonNull(next);
requireNonNull(hasNext);
return StreamSupport.stream(IterateSpliterator.create(seed, hasNext, next), false);
}
/**
* 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素
* <p> jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p>
* <p>本环节中是顺序执行的, 但是后续操作可以支持并行流</p>
* <p>但是不建议在并行流中使用, 除非你确定 takeWhile 之后的操作能在并行流中受益很多</p>
*
* @param source 源流
* @param <T> 元素类型
* @param predicate 断言
* @return 与指定断言匹配的元素组成的流
*/
public static <T> Stream<T> takeWhile(Stream<T> source, Predicate<? super T> predicate) {
requireNonNull(source);
requireNonNull(predicate);
return createStatefulNewStream(source, TakeWhileSpliterator.create(source.spliterator(), predicate));
}
/**
* 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流
* <p> jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p>
* <p>本环节中是顺序执行的, 但是后续操作可以支持并行流</p>
* <p>但是不建议在并行流中使用, 除非你确定 dropWhile 之后的操作能在并行流中受益很多</p>
*
* @param source 源流
* @param <T> 元素类型
* @param predicate 断言
* @return 剩余元素组成的流
*/
public static <T> Stream<T> dropWhile(Stream<T> source, Predicate<? super T> predicate) {
requireNonNull(source);
requireNonNull(predicate);
return createStatefulNewStream(source, DropWhileSpliterator.create(source.spliterator(), predicate));
}
// region 私有方法
/* ================================================== 私有方法 =================================================== */
/**
* 根据 源流 新的Spliterator 生成新的流
* <p>这是一个 顺序的有状态的流</p>
* <p>在新流的第一个节点是顺序执行的, 但是后续操作可以支持并行流</p>
*
* @param source 源流
* @param newSpliterator 新流的Spliterator
* @param <T> 旧流的元素类型
* @param <R> 新流的元素类型
* @return 新流
*/
private static <T, R> Stream<R> createStatefulNewStream(Stream<T> source, Spliterator<R> newSpliterator) {
// 创建新流
Stream<R> newStream = StreamSupport.stream(newSpliterator, source.isParallel());
// 如果旧流是并行流, 新流主动调用一个有状态的操作, 虽然没有意义, 但是可以让后续的无状态节点正常并发
if (source.isParallel()) {
newStream = newStream.limit(Long.MAX_VALUE);
}
// 由于新流不与旧流的节点关联, 所以需要主动设置旧流的close方法, 哪怕几乎不可能有人在旧流上设置onClose操作
return newStream.onClose(source::close);
}
/* ============================================================================================================== */
// endregion
} }

View File

@ -1,4 +1,4 @@
package cn.hutool.core.stream.support; package cn.hutool.core.stream.spliterators;
import java.util.Comparator; import java.util.Comparator;
import java.util.Spliterator; import java.util.Spliterator;
@ -12,9 +12,9 @@ import java.util.function.Predicate;
* @author emptypoint * @author emptypoint
* @since 6.0.0 * @since 6.0.0
*/ */
class DropWhileSpliterator<T> implements Spliterator<T> { public class DropWhileSpliterator<T> implements Spliterator<T> {
static <T> DropWhileSpliterator<T> create(Spliterator<T> source, Predicate<? super T> predicate) { public static <T> DropWhileSpliterator<T> create(Spliterator<T> source, Predicate<? super T> predicate) {
return new DropWhileSpliterator<>(source, predicate); return new DropWhileSpliterator<>(source, predicate);
} }

View File

@ -0,0 +1,76 @@
package cn.hutool.core.stream.spliterators;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* 无限有序流 的Spliterator
*
* @author VampireAchao
* @since 6.0.0
*/
public class IterateSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
private final T seed;
private final Predicate<? super T> hasNext;
private final UnaryOperator<T> next;
private T prev;
private boolean started;
private boolean finished;
/**
* Creates a spliterator reporting the given estimated size and
* additionalCharacteristics.
*/
IterateSpliterator(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE);
this.seed = seed;
this.hasNext = hasNext;
this.next = next;
}
public static <T> IterateSpliterator<T> create(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
return new IterateSpliterator<>(seed, hasNext, next);
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished) {
return false;
}
T t;
if (started) {
t = next.apply(prev);
} else {
t = seed;
started = true;
}
if (!hasNext.test(t)) {
prev = null;
finished = true;
return false;
}
prev = t;
action.accept(prev);
return true;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished) {
return;
}
finished = true;
T t = started ? next.apply(prev) : seed;
prev = null;
while (hasNext.test(t)) {
action.accept(t);
t = next.apply(t);
}
}
}

View File

@ -1,4 +1,4 @@
package cn.hutool.core.stream.support; package cn.hutool.core.stream.spliterators;
import java.util.Comparator; import java.util.Comparator;
import java.util.Spliterator; import java.util.Spliterator;
@ -12,9 +12,9 @@ import java.util.function.Predicate;
* @author emptypoint * @author emptypoint
* @since 6.0.0 * @since 6.0.0
*/ */
class TakeWhileSpliterator<T> implements Spliterator<T> { public class TakeWhileSpliterator<T> implements Spliterator<T> {
static <T> TakeWhileSpliterator<T> create(Spliterator<T> source, Predicate<? super T> predicate) { public static <T> TakeWhileSpliterator<T> create(Spliterator<T> source, Predicate<? super T> predicate) {
return new TakeWhileSpliterator<>(source, predicate); return new TakeWhileSpliterator<>(source, predicate);
} }

View File

@ -1,76 +0,0 @@
package cn.hutool.core.stream.support;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* 无限有序流 的Spliterator
*
* @author VampireAchao
* @since 6.0.0
*/
class IterateSpliterator<T> extends Spliterators.AbstractSpliterator<T> {
public static <T> IterateSpliterator<T> create(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
return new IterateSpliterator<>(seed, hasNext, next);
}
/**
* Creates a spliterator reporting the given estimated size and
* additionalCharacteristics.
*/
IterateSpliterator(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
super(Long.MAX_VALUE, Spliterator.ORDERED | Spliterator.IMMUTABLE);
this.seed = seed;
this.hasNext = hasNext;
this.next = next;
}
private final T seed;
private final Predicate<? super T> hasNext;
private final UnaryOperator<T> next;
private T prev;
private boolean started;
private boolean finished;
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished) {
return false;
}
T t;
if (started) {
t = next.apply(prev);
} else {
t = seed;
started = true;
}
if (!hasNext.test(t)) {
prev = null;
finished = true;
return false;
}
prev = t;
action.accept(prev);
return true;
}
@Override
public void forEachRemaining(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished) {
return;
}
finished = true;
T t = started ? next.apply(prev) : seed;
prev = null;
while (hasNext.test(t)) {
action.accept(t);
t = next.apply(t);
}
}
}

View File

@ -1,99 +0,0 @@
package cn.hutool.core.stream.support;
import java.util.Spliterator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;
/**
* FastStream 辅助工具类
*
* @author emptypoint
* @since 6.0.0
*/
public final class StreamHelper {
private StreamHelper() {
}
/**
* 返回无限有序流
* 该流由 初始值 然后判断条件 以及执行 迭代函数 进行迭代获取到元素
*
* @param <T> 元素类型
* @param seed 初始值
* @param hasNext 条件值
* @param next 用上一个元素作为参数执行并返回一个新的元素
* @return 无限有序流
*/
public static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
requireNonNull(next);
requireNonNull(hasNext);
return StreamSupport.stream(IterateSpliterator.create(seed, hasNext, next), false);
}
/**
* 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素
* <p> jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p>
* <p>本环节中是顺序执行的, 但是后续操作可以支持并行流</p>
* <p>但是不建议在并行流中使用, 除非你确定 takeWhile 之后的操作能在并行流中受益很多</p>
*
* @param source 源流
* @param <T> 元素类型
* @param predicate 断言
* @return 与指定断言匹配的元素组成的流
*/
public static <T> Stream<T> takeWhile(Stream<T> source, Predicate<? super T> predicate) {
requireNonNull(source);
requireNonNull(predicate);
return createStatefulNewStream(source, TakeWhileSpliterator.create(source.spliterator(), predicate));
}
/**
* 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流
* <p> jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的有状态的中间操作</p>
* <p>本环节中是顺序执行的, 但是后续操作可以支持并行流</p>
* <p>但是不建议在并行流中使用, 除非你确定 dropWhile 之后的操作能在并行流中受益很多</p>
*
* @param source 源流
* @param <T> 元素类型
* @param predicate 断言
* @return 剩余元素组成的流
*/
public static <T> Stream<T> dropWhile(Stream<T> source, Predicate<? super T> predicate) {
requireNonNull(source);
requireNonNull(predicate);
return createStatefulNewStream(source, DropWhileSpliterator.create(source.spliterator(), predicate));
}
// region 私有方法
/* ================================================== 私有方法 =================================================== */
/**
* 根据 源流 新的Spliterator 生成新的流
* <p>这是一个 顺序的有状态的流</p>
* <p>在新流的第一个节点是顺序执行的, 但是后续操作可以支持并行流</p>
*
* @param source 源流
* @param newSpliterator 新流的Spliterator
* @param <T> 旧流的元素类型
* @param <R> 新流的元素类型
* @return 新流
*/
private static <T, R> Stream<R> createStatefulNewStream(Stream<T> source, Spliterator<R> newSpliterator) {
// 创建新流
Stream<R> newStream = StreamSupport.stream(newSpliterator, source.isParallel());
// 如果旧流是并行流, 新流主动调用一个有状态的操作, 虽然没有意义, 但是可以让后续的无状态节点正常并发
if (source.isParallel()) {
newStream = newStream.limit(Long.MAX_VALUE);
}
// 由于新流不与旧流的节点关联, 所以需要主动设置旧流的close方法, 哪怕几乎不可能有人在旧流上设置onClose操作
return newStream.onClose(source::close);
}
/* ============================================================================================================== */
// endregion
}

View File

@ -8,13 +8,7 @@ import lombok.NoArgsConstructor;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
@ -24,6 +18,12 @@ import java.util.stream.Stream;
*/ */
public class OptTest { public class OptTest {
@Test
public void ofTest() {
Assert.assertTrue(Opt.of(Optional.empty()).isEmpty());
Assert.assertTrue(Opt.of(Optional.of(1)).isPresent());
}
@Test @Test
public void ofBlankAbleTest() { public void ofBlankAbleTest() {
// ofBlankAble相对于ofNullable考虑了字符串为空串的情况 // ofBlankAble相对于ofNullable考虑了字符串为空串的情况

View File

@ -193,6 +193,8 @@ public class FastStreamTest {
flatMapIter = FastStream.of(list).flatMapIter(e -> Arrays.asList(e, e * 10)).toList(); flatMapIter = FastStream.of(list).flatMapIter(e -> Arrays.asList(e, e * 10)).toList();
Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter); Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter);
// 不报npe测试
Assert.assertTrue(FastStream.of(list).flatMapIter(e -> null).isEmpty());
} }
@Test @Test
@ -310,6 +312,9 @@ public class FastStreamTest {
List<String> list = Arrays.asList("dromara", "hutool", "sweet"); List<String> list = Arrays.asList("dromara", "hutool", "sweet");
List<String> zip = FastStream.of(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList(); List<String> zip = FastStream.of(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList();
Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip); Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip);
zip = FastStream.iterate(1, i -> i + 1).zip(list, (e1, e2) -> e1 + "." + e2).toList();
Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip);
} }
@Test @Test
@ -388,4 +393,9 @@ public class FastStreamTest {
Assert.assertEquals(Arrays.asList(5, 7, 9), res2); Assert.assertEquals(Arrays.asList(5, 7, 9), res2);
} }
@Test
public void testIsNotEmpty() {
Assert.assertTrue(FastStream.of(1).isNotEmpty());
}
} }