mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
!742 优化FastStream, 新增takeWhile、dropWhile方法
Merge pull request !742 from emptypoint/FastStream-2
This commit is contained in:
commit
04542f9a16
@ -13,6 +13,7 @@ import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -692,4 +693,51 @@ public class ListUtil {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类似js的<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice">splice</a>函数
|
||||
*
|
||||
* @param start 起始下标, 可以为负数, -1代表最后一个元素
|
||||
* @param deleteCount 删除个数,必须是正整数
|
||||
* @param items 放入值
|
||||
* @return 操作后的流
|
||||
* @since 6.0.0
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <T> List<T> splice(List<T> list, int start, int deleteCount, T... items) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return zero();
|
||||
}
|
||||
final int size = list.size();
|
||||
// 从后往前查找
|
||||
if (start < 0) {
|
||||
start += size;
|
||||
} else if (start >= size) {
|
||||
// 直接在尾部追加,不删除
|
||||
start = size;
|
||||
deleteCount = 0;
|
||||
}
|
||||
// 起始位置 加上 删除的数量 超过 数据长度,需要重新计算需要删除的数量
|
||||
if (start + deleteCount > size) {
|
||||
deleteCount = size - start;
|
||||
}
|
||||
|
||||
// 新列表的长度
|
||||
final int newSize = size - deleteCount + items.length;
|
||||
List<T> resList = list;
|
||||
// 新列表的长度 大于 旧列表,创建新列表
|
||||
if (newSize > size) {
|
||||
resList = new ArrayList<>(newSize);
|
||||
resList.addAll(list);
|
||||
}
|
||||
// 需要删除的部分
|
||||
if (deleteCount > 0) {
|
||||
resList.subList(start, start + deleteCount).clear();
|
||||
}
|
||||
// 新增的部分
|
||||
if (ArrayUtil.isNotEmpty(items)) {
|
||||
resList.addAll(start, Arrays.asList(items));
|
||||
}
|
||||
return resList;
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +127,19 @@ public class Opt<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 {@link Optional} 构造 {@code Opt}
|
||||
*
|
||||
* @param optional optional
|
||||
* @param <T> 包裹的元素类型
|
||||
* @return 一个包裹里元素可能为空的 {@code Opt}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static <T> Opt<T> of(Optional<T> optional) {
|
||||
return ofNullable(optional).flattedMap(Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 包裹里实际的元素
|
||||
*/
|
||||
|
@ -81,6 +81,47 @@ public class MutableInt extends Number implements Comparable<MutableInt>, Mutabl
|
||||
return this;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* 先加1, 再获取值
|
||||
*
|
||||
* @return +1后的值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public int incrementAndGet() {
|
||||
return ++value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 先获取原来的值, 再加1
|
||||
*
|
||||
* @return 原始值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public int getAndIncrement() {
|
||||
return value++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 先减1, 再获取值
|
||||
*
|
||||
* @return -1后的值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public int decrementAndGet() {
|
||||
return --value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 先获取原来的值, 再减1
|
||||
*
|
||||
* @return 原始值
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public int getAndDecrement() {
|
||||
return value--;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* 增加值
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.hutool.core.lang.mutable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 可变{@code Object}
|
||||
@ -8,7 +9,7 @@ import java.io.Serializable;
|
||||
* @param <T> 可变的类型
|
||||
* @since 3.0.1
|
||||
*/
|
||||
public class MutableObj<T> implements Mutable<T>, Serializable {
|
||||
public class MutableObj<T> implements Mutable<T>, Serializable, Consumer<T> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
@ -50,6 +51,17 @@ public class MutableObj<T> implements Mutable<T>, Serializable {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费元素
|
||||
*
|
||||
* @param t t
|
||||
* @since 6.0.0
|
||||
*/
|
||||
@Override
|
||||
public void accept(T t) {
|
||||
this.value = t;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.hutool.core.stream;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
@ -61,6 +60,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
// region Static method
|
||||
// --------------------------------------------------------------- Static method start
|
||||
|
||||
/**
|
||||
@ -157,51 +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) {
|
||||
Objects.requireNonNull(next);
|
||||
Objects.requireNonNull(hasNext);
|
||||
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE,
|
||||
Spliterator.ORDERED | Spliterator.IMMUTABLE) {
|
||||
T prev;
|
||||
boolean started;
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
return new FastStream<>(StreamSupport.stream(spliterator, false));
|
||||
return new FastStream<>(StreamUtil.iterate(seed, hasNext, next));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -279,6 +235,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- Static method end
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 过滤元素,返回与指定断言匹配的元素组成的流
|
||||
@ -303,10 +260,9 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
*/
|
||||
public <R> FastStream<T> filter(Function<? super T, ? extends R> mapper, R value) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return filter(e -> Objects.equals(Opt.ofNullable(e).map(mapper).get(), value));
|
||||
return filter(e -> Objects.equals(mapper.apply(e), value));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
@ -320,7 +276,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
return filter(e -> predicate.test(e, NOT_FOUND_INDEX));
|
||||
} else {
|
||||
MutableInt index = new MutableInt(NOT_FOUND_INDEX);
|
||||
return filter(e -> predicate.test(e, index.increment().get()));
|
||||
return filter(e -> predicate.test(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,6 +302,22 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
return new FastStream<>(stream.map(mapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 元素 转换后 并且不为 {@code null} 的 新元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* <pre>{@code
|
||||
* // 等价于先调用map再调用nonNull
|
||||
* .map(...).nonNull()...
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 指定的函数
|
||||
* @param <R> 函数执行后返回的类型
|
||||
* @return 新元素组成的流
|
||||
*/
|
||||
public <R> FastStream<R> mapNonNull(Function<? super T, ? extends R> mapper) {
|
||||
return nonNull().<R>map(mapper).nonNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行的结果组成的流,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
@ -360,13 +332,13 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
return map(e -> mapper.apply(e, NOT_FOUND_INDEX));
|
||||
} else {
|
||||
MutableInt index = new MutableInt(NOT_FOUND_INDEX);
|
||||
return map(e -> mapper.apply(e, index.increment().get()));
|
||||
return map(e -> mapper.apply(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
||||
* 这是一个无状态中间操作
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流:
|
||||
* <pre>{@code
|
||||
* FastStream<Long> ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
@ -395,7 +367,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
return flatMap(e -> mapper.apply(e, NOT_FOUND_INDEX));
|
||||
} else {
|
||||
MutableInt index = new MutableInt(NOT_FOUND_INDEX);
|
||||
return flatMap(e -> mapper.apply(e, index.increment().get()));
|
||||
return flatMap(e -> mapper.apply(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -436,20 +408,36 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
||||
* 这是一个无状态中间操作
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素,
|
||||
* 最后返回所有迭代器的所有元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流:
|
||||
* <pre>{@code
|
||||
* FastStream<Long> ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
* FastStream<Long> ids = FastStream.of(users).flat(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 操作,返回可迭代对象
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
public <R> FastStream<R> flatMapIter(Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
public <R> FastStream<R> flat(Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMap(w -> Opt.of(w).map(mapper).map(FastStream::of).orElseGet(FastStream::empty));
|
||||
return flatMap(w -> of(mapper.apply(w)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,对过滤后的非{@code null}元素执行mapper操作,转换为迭代器,
|
||||
* 并过滤迭代器中为{@code null}的元素, 返回所有迭代器的所有非空元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
* @see #flat(Function)
|
||||
* @see #nonNull()
|
||||
*/
|
||||
public <R> FastStream<R> flatNonNull(Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
return nonNull().flat(mapper).nonNull();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -668,7 +656,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
stream.forEach(e -> action.accept(e, NOT_FOUND_INDEX));
|
||||
} else {
|
||||
MutableInt index = new MutableInt(NOT_FOUND_INDEX);
|
||||
stream.forEach(e -> action.accept(e, index.increment().get()));
|
||||
stream.forEach(e -> action.accept(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -695,7 +683,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
stream.forEachOrdered(e -> action.accept(e, NOT_FOUND_INDEX));
|
||||
} else {
|
||||
MutableInt index = new MutableInt(NOT_FOUND_INDEX);
|
||||
stream.forEachOrdered(e -> action.accept(e, index.increment().get()));
|
||||
stream.forEachOrdered(e -> action.accept(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -712,13 +700,15 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
|
||||
/**
|
||||
* 返回一个包含此流元素的指定的数组
|
||||
* <p>
|
||||
* 例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException}
|
||||
* <pre>{@code String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new); }</pre>
|
||||
* </p>
|
||||
*
|
||||
* @param generator 这里的IntFunction的参数是元素的个数,返回值为数组类型
|
||||
* @param <A> 给定的数组类型
|
||||
* @return 包含此流元素的指定的数组
|
||||
* @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常
|
||||
* 例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException}
|
||||
* <pre>{@code String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new); }</pre>
|
||||
*/
|
||||
@Override
|
||||
public <A> A[] toArray(IntFunction<A[]> generator) {
|
||||
@ -916,8 +906,8 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的第一个元素
|
||||
*/
|
||||
public T findFirst(Predicate<? super T> predicate) {
|
||||
return stream.filter(predicate).findFirst().orElse(null);
|
||||
public Optional<T> findFirst(Predicate<? super T> predicate) {
|
||||
return stream.filter(predicate).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -926,7 +916,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1
|
||||
*/
|
||||
public Integer findFirstIdx(Predicate<? super T> predicate) {
|
||||
public int findFirstIdx(Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
if (isParallel()) {
|
||||
return NOT_FOUND_INDEX;
|
||||
@ -947,13 +937,9 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @return 最后一个元素
|
||||
*/
|
||||
public Optional<T> findLast() {
|
||||
if (isParallel()) {
|
||||
return Optional.of(toList()).filter(CollUtil::isNotEmpty).map(l -> l.get(l.size() - 1));
|
||||
} else {
|
||||
MutableObj<T> last = new MutableObj<>(null);
|
||||
forEach(last::set);
|
||||
return Optional.ofNullable(last.get());
|
||||
}
|
||||
MutableObj<T> last = new MutableObj<>(null);
|
||||
spliterator().forEachRemaining(last);
|
||||
return Optional.ofNullable(last.get());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -962,21 +948,15 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的最后一个元素
|
||||
*/
|
||||
public T findLast(Predicate<? super T> predicate) {
|
||||
public Optional<T> findLast(Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
if (isParallel()) {
|
||||
List<T> list = toList();
|
||||
final int index = ListUtil.lastIndexOf(list, predicate);
|
||||
return index == NOT_FOUND_INDEX ? null : list.get(index);
|
||||
} else {
|
||||
MutableObj<T> last = new MutableObj<>(null);
|
||||
forEach(e -> {
|
||||
if (predicate.test(e)) {
|
||||
last.set(e);
|
||||
}
|
||||
});
|
||||
return last.get();
|
||||
}
|
||||
MutableObj<T> last = new MutableObj<>(null);
|
||||
spliterator().forEachRemaining(e -> {
|
||||
if (predicate.test(e)) {
|
||||
last.set(e);
|
||||
}
|
||||
});
|
||||
return Optional.ofNullable(last.get());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -985,7 +965,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的最后一个元素的下标,如果不存在则返回-1
|
||||
*/
|
||||
public Integer findLastIdx(Predicate<? super T> predicate) {
|
||||
public int findLastIdx(Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
if (isParallel()) {
|
||||
return NOT_FOUND_INDEX;
|
||||
@ -1006,9 +986,10 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @return 反转元素顺序
|
||||
*/
|
||||
public FastStream<T> reverse() {
|
||||
List<T> list = toList();
|
||||
Collections.reverse(list);
|
||||
return of(list, isParallel());
|
||||
//noinspection unchecked
|
||||
final T[] array = (T[]) toArray();
|
||||
ArrayUtil.reverse(array);
|
||||
return of(array).parallel(isParallel()).onClose(stream::close);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1065,6 +1046,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
|
||||
/**
|
||||
* 返回一个无序流(无手动排序)
|
||||
* <p>标记一个流是不在意元素顺序的, 在并行流的某些情况下可以提高性能</p>
|
||||
*
|
||||
* @return 无序流
|
||||
*/
|
||||
@ -1134,11 +1116,11 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @param idx 下标
|
||||
* @return 指定下标的元素
|
||||
*/
|
||||
public T at(Integer idx) {
|
||||
if (Objects.isNull(idx)) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.get(toList(), idx);
|
||||
public Optional<T> at(Integer idx) {
|
||||
return Opt.ofNullable(idx).map(i -> {
|
||||
//noinspection unchecked
|
||||
return (T) ArrayUtil.get(toArray(), i);
|
||||
}).toOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1225,28 +1207,29 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 与给定的可迭代对象转换成map,key为现有元素,value为给定可迭代对象迭代的元素<br>
|
||||
* 至少包含全部的key,如果对应位置上的value不存在,则为null
|
||||
* 与给定的可迭代对象转换成Map,key为现有元素,value为给定可迭代对象迭代的元素<br>
|
||||
* Map的大小与两个集合中较小的数量一致, 即, 只合并下标位置相同的部分
|
||||
*
|
||||
* @param other 可迭代对象
|
||||
* @param <R> 可迭代对象迭代的元素类型
|
||||
* @return map,key为现有元素,value为给定可迭代对象迭代的元素;<br>
|
||||
* 至少包含全部的key,如果对应位置上的value不存在,则为null;<br>
|
||||
* 如果key重复, 则保留最后一个关联的value;<br>
|
||||
* @return map,key为现有元素,value为给定可迭代对象迭代的元素
|
||||
*/
|
||||
public <R> Map<T, R> toZip(Iterable<R> other) {
|
||||
// value对象迭代器
|
||||
final Iterator<R> iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator);
|
||||
if (isParallel()) {
|
||||
List<T> keyList = toList();
|
||||
final Map<T, R> map = MapUtil.newHashMap(keyList.size());
|
||||
for (T key : keyList) {
|
||||
map.put(key, iterator.hasNext() ? iterator.next() : null);
|
||||
}
|
||||
return map;
|
||||
} else {
|
||||
return toMap(Function.identity(), e -> iterator.hasNext() ? iterator.next() : null);
|
||||
final Spliterator<T> keys = spliterator();
|
||||
final Spliterator<R> 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<T, R> map = MapUtil.newHashMap(sizeIfKnown);
|
||||
// 保存第一个Spliterator的值
|
||||
MutableObj<T> key = new MutableObj<>();
|
||||
// 保存第二个Spliterator的值
|
||||
MutableObj<R> value = new MutableObj<>();
|
||||
// 当两个Spliterator中都还有剩余元素时
|
||||
while (keys.tryAdvance(key) && values.tryAdvance(value)) {
|
||||
map.put(key.get(), value.get());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1389,8 +1372,7 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
|
||||
/**
|
||||
* 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流<br>
|
||||
* 新流的数量等于旧流元素的数量<br>
|
||||
* 使用 zipper 转换时, 如果对应位置上已经没有other元素,则other元素为null<br>
|
||||
* 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分<br>
|
||||
*
|
||||
* @param other 给定的迭代器
|
||||
* @param zipper 两个元素的合并器
|
||||
@ -1401,15 +1383,21 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
public <U, R> FastStream<R> zip(Iterable<U> other,
|
||||
BiFunction<? super T, ? super U, ? extends R> zipper) {
|
||||
Objects.requireNonNull(zipper);
|
||||
// 给定对象迭代器
|
||||
final Iterator<U> iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator);
|
||||
Stream<T> resStream = this.stream;
|
||||
if (isParallel()) {
|
||||
resStream = toList().stream();
|
||||
final Spliterator<T> keys = spliterator();
|
||||
final Spliterator<U> 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<R> list = new ArrayList<>(sizeIfKnown);
|
||||
// 保存第一个Spliterator的值
|
||||
MutableObj<T> key = new MutableObj<>();
|
||||
// 保存第二个Spliterator的值
|
||||
MutableObj<U> value = new MutableObj<>();
|
||||
// 当两个Spliterator中都还有剩余元素时
|
||||
while (keys.tryAdvance(key) && values.tryAdvance(value)) {
|
||||
list.add(zipper.apply(key.get(), value.get()));
|
||||
}
|
||||
final FastStream<R> newStream = of(resStream.map(e -> zipper.apply(e, iterator.hasNext() ? iterator.next() : null)));
|
||||
newStream.parallel(isParallel());
|
||||
return newStream;
|
||||
return of(list).parallel(isParallel()).onClose(stream::close);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1420,40 +1408,11 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
* @param items 放入值
|
||||
* @return 操作后的流
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public FastStream<T> splice(int start, int deleteCount, T... items) {
|
||||
List<T> list = toList();
|
||||
final int size = list.size();
|
||||
// 从后往前查找
|
||||
if (start < 0) {
|
||||
start += size;
|
||||
} else if (start >= size) {
|
||||
// 直接在尾部追加,不删除
|
||||
start = size;
|
||||
deleteCount = 0;
|
||||
}
|
||||
// 起始位置 加上 删除的数量 超过 数据长度,需要重新计算需要删除的数量
|
||||
if (start + deleteCount > size) {
|
||||
deleteCount = size - start;
|
||||
}
|
||||
|
||||
// 新列表的长度
|
||||
final int newSize = size - deleteCount + items.length;
|
||||
List<T> resList = list;
|
||||
// 新列表的长度 大于 旧列表,创建新列表
|
||||
if (newSize > size) {
|
||||
resList = new ArrayList<>(newSize);
|
||||
resList.addAll(list);
|
||||
}
|
||||
// 需要删除的部分
|
||||
if (deleteCount > 0) {
|
||||
resList.subList(start, start + deleteCount).clear();
|
||||
}
|
||||
// 新增的部分
|
||||
if (items.length > 0) {
|
||||
resList.addAll(start, Arrays.asList(items));
|
||||
}
|
||||
return FastStream.of(resList);
|
||||
@SafeVarargs
|
||||
public final FastStream<T> splice(int start, int deleteCount, T... items) {
|
||||
return of(ListUtil.splice(toList(), start, deleteCount, items))
|
||||
.parallel(isParallel())
|
||||
.onClose(stream::close);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1473,9 +1432,10 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
// 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]]
|
||||
return FastStream.<FastStream<T>>of(of(list, isParallel()));
|
||||
}
|
||||
return FastStream.iterate(0, i -> i < size, i -> i + batchSize)
|
||||
return iterate(0, i -> i < size, i -> i + batchSize)
|
||||
.map(skip -> of(list.subList(skip, Math.min(size, skip + batchSize)), isParallel()))
|
||||
.parallel(isParallel());
|
||||
.parallel(isParallel())
|
||||
.onClose(stream::close);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1491,6 +1451,73 @@ public class FastStream<T> implements Stream<T>, Iterable<T> {
|
||||
return split(batchSize).map(FastStream::toList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保留 与指定断言 匹配时的元素, 在第一次不匹配时终止, 抛弃当前(第一个不匹配元素)及后续所有元素
|
||||
* <p>与 jdk9 中的 takeWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作</p>
|
||||
* <pre>本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
|
||||
* FastStream.iterate(1, i -> i + 1)
|
||||
* .parallel()
|
||||
* // 顺序执行
|
||||
* .takeWhile(e -> e < 50)
|
||||
* // 并发
|
||||
* .map(e -> e + 1)
|
||||
* // 并发
|
||||
* .map(String::valueOf)
|
||||
* .toList();
|
||||
* }</pre>
|
||||
* <p>但是不建议在并行流中使用, 除非你确定 takeWhile 之后的操作能在并行流中受益很多</p>
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与指定断言匹配的元素组成的流
|
||||
*/
|
||||
public FastStream<T> takeWhile(Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return of(StreamUtil.takeWhile(stream, predicate));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 删除 与指定断言 匹配的元素, 在第一次不匹配时终止, 返回当前(第一个不匹配元素)及剩余元素组成的新流
|
||||
* <p>与 jdk9 中的 dropWhile 方法不太一样, 这里的实现是个 顺序的、有状态的中间操作</p>
|
||||
* <pre>本环节中是顺序执行的, 但是后续操作可以支持并行流: {@code
|
||||
* FastStream.iterate(1, i <= 100, i -> i + 1)
|
||||
* .parallel()
|
||||
* // 顺序执行
|
||||
* .dropWhile(e -> e < 50)
|
||||
* // 并发
|
||||
* .map(e -> e + 1)
|
||||
* // 并发
|
||||
* .map(String::valueOf)
|
||||
* .toList();
|
||||
* }</pre>
|
||||
* <p>但是不建议在并行流中使用, 除非你确定 dropWhile 之后的操作能在并行流中受益很多</p>
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 剩余元素组成的流
|
||||
*/
|
||||
public FastStream<T> dropWhile(Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return of(StreamUtil.dropWhile(stream, 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>> {
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,9 @@ package cn.hutool.core.stream;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.stream.spliterators.DropWhileSpliterator;
|
||||
import cn.hutool.core.stream.spliterators.IterateSpliterator;
|
||||
import cn.hutool.core.stream.spliterators.TakeWhileSpliterator;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
|
||||
import java.io.File;
|
||||
@ -10,16 +13,20 @@ import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* {@link Stream} 工具类
|
||||
*
|
||||
* @author looly
|
||||
* @author looly emptypoint VampireAchao
|
||||
* @since 5.6.7
|
||||
*/
|
||||
public class StreamUtil {
|
||||
@ -141,4 +148,84 @@ public class StreamUtil {
|
||||
final Function<T, ? extends CharSequence> 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
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
package cn.hutool.core.stream.spliterators;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* dropWhile 的 Spliterator
|
||||
* <p>借鉴自StreamEx</p>
|
||||
*
|
||||
* @author emptypoint
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class DropWhileSpliterator<T> implements Spliterator<T> {
|
||||
|
||||
public static <T> DropWhileSpliterator<T> create(Spliterator<T> source, Predicate<? super T> predicate) {
|
||||
return new DropWhileSpliterator<>(source, predicate);
|
||||
}
|
||||
|
||||
private final Spliterator<T> source;
|
||||
private final Predicate<? super T> predicate;
|
||||
private boolean isFound = false;
|
||||
|
||||
private DropWhileSpliterator(Spliterator<T> source, Predicate<? super T> predicate) {
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super T> action) {
|
||||
boolean hasNext = true;
|
||||
// 如果 还没找到 并且 流中还有元素 继续找
|
||||
while (!isFound && hasNext) {
|
||||
hasNext = source.tryAdvance(e -> {
|
||||
if (!predicate.test(e)) {
|
||||
// 第一次不匹配
|
||||
isFound = true;
|
||||
action.accept(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 对找到的元素进行后续处理
|
||||
if (isFound) {
|
||||
source.forEachRemaining(action);
|
||||
}
|
||||
|
||||
// 该环节已经处理完成
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<T> trySplit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return source.characteristics() & ~Spliterator.SIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super T> getComparator() {
|
||||
return source.getComparator();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package cn.hutool.core.stream.spliterators;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* takeWhile 的 Spliterator
|
||||
* <p>借鉴自StreamEx</p>
|
||||
*
|
||||
* @author emptypoint
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class TakeWhileSpliterator<T> implements Spliterator<T> {
|
||||
|
||||
public static <T> TakeWhileSpliterator<T> create(Spliterator<T> source, Predicate<? super T> predicate) {
|
||||
return new TakeWhileSpliterator<>(source, predicate);
|
||||
}
|
||||
|
||||
private final Spliterator<T> source;
|
||||
private final Predicate<? super T> predicate;
|
||||
private boolean isContinue = true;
|
||||
|
||||
TakeWhileSpliterator(Spliterator<T> source, Predicate<? super T> predicate) {
|
||||
this.source = source;
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super T> action) {
|
||||
boolean hasNext = true;
|
||||
// 如果 还可以继续 并且 流中还有元素 则继续遍历
|
||||
while (isContinue && hasNext) {
|
||||
hasNext = source.tryAdvance(e -> {
|
||||
if (predicate.test(e)) {
|
||||
action.accept(e);
|
||||
} else {
|
||||
// 终止遍历剩下的元素
|
||||
isContinue = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 该环节已经处理完成
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<T> trySplit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return isContinue ? source.estimateSize() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return source.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparator<? super T> getComparator() {
|
||||
return source.getComparator();
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,7 @@ import lombok.NoArgsConstructor;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@ -24,6 +18,12 @@ import java.util.stream.Stream;
|
||||
*/
|
||||
public class OptTest {
|
||||
|
||||
@Test
|
||||
public void ofTest() {
|
||||
Assert.assertTrue(Opt.of(Optional.empty()).isEmpty());
|
||||
Assert.assertTrue(Opt.of(Optional.of(1)).isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofBlankAbleTest() {
|
||||
// ofBlankAble相对于ofNullable考虑了字符串为空串的情况
|
||||
|
@ -23,6 +23,12 @@ public class FastStreamTest {
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 3), list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerate() {
|
||||
List<Integer> list = FastStream.generate(() -> 0).limit(3).toList();
|
||||
Assert.assertEquals(Arrays.asList(0, 0, 0), list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOf() {
|
||||
Assert.assertEquals(3, FastStream.of(Arrays.asList(1, 2, 3), true).count());
|
||||
@ -76,7 +82,7 @@ public class FastStreamTest {
|
||||
Map<Integer, String> toZip = FastStream.of(orders).toZip(list);
|
||||
Assert.assertEquals(map, toZip);
|
||||
|
||||
Map<Integer, String> toZipParallel = FastStream.of(orders).parallel().toZip(list);
|
||||
Map<Integer, String> toZipParallel = FastStream.of(orders).parallel().nonNull().toZip(list);
|
||||
Assert.assertEquals(map, toZipParallel);
|
||||
}
|
||||
|
||||
@ -140,6 +146,13 @@ public class FastStreamTest {
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 2, 3, 3, 3), mapMulti);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapNonNull() {
|
||||
List<Integer> list = Arrays.asList(1, 2, 3, null);
|
||||
List<String> mapNonNull = FastStream.of(list).mapNonNull(String::valueOf).toList();
|
||||
Assert.assertEquals(Arrays.asList("1", "2", "3"), mapNonNull);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistinct() {
|
||||
List<Integer> list = ListUtil.of(3, 2, 2, 1, null, null);
|
||||
@ -174,6 +187,11 @@ public class FastStreamTest {
|
||||
FastStream.FastStreamBuilder<String> builder = FastStream.builder();
|
||||
FastStream.of(list).forEachOrderedIdx((e, i) -> builder.accept(i + 1 + "." + e));
|
||||
Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), builder.build().toList());
|
||||
|
||||
FastStream.FastStreamBuilder<String> streamBuilder = FastStream.builder();
|
||||
FastStream.of(list).parallel().forEachOrderedIdx((e, i) -> streamBuilder.accept(i + 1 + "." + e));
|
||||
Assert.assertEquals(Arrays.asList("0.dromara", "0.hutool", "0.sweet"), streamBuilder.build().toList());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -186,13 +204,22 @@ public class FastStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlatMapIter() {
|
||||
public void testFlat() {
|
||||
List<Integer> list = Arrays.asList(1, 2, 3);
|
||||
List<Integer> flatMapIter = FastStream.of(list).<Integer>flatMapIter(e -> null).toList();
|
||||
Assert.assertEquals(Collections.emptyList(), flatMapIter);
|
||||
|
||||
flatMapIter = FastStream.of(list).flatMapIter(e -> Arrays.asList(e, e * 10)).toList();
|
||||
Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flatMapIter);
|
||||
// 一个元素 扩散为 多个元素(迭代器)
|
||||
List<Integer> flat = FastStream.of(list).flat(e -> Arrays.asList(e, e * 10)).toList();
|
||||
Assert.assertEquals(ListUtil.of(1, 10, 2, 20, 3, 30), flat);
|
||||
|
||||
// 过滤迭代器为null的元素
|
||||
flat = FastStream.of(list).<Integer>flat(e -> null).toList();
|
||||
Assert.assertEquals(Collections.emptyList(), flat);
|
||||
|
||||
// 自动过滤null元素
|
||||
flat = FastStream.of(list).flat(e -> Arrays.asList(e, e > 2 ? e : null)).toList();
|
||||
Assert.assertEquals(ListUtil.of(1, null, 2, null, 3, 3), flat);
|
||||
// 不报npe测试
|
||||
Assert.assertTrue(FastStream.of(list).flat(e -> null).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -229,6 +256,8 @@ public class FastStreamTest {
|
||||
List<Integer> list = Arrays.asList(1, 2);
|
||||
List<Integer> push = FastStream.of(list).push(3).toList();
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 3), push);
|
||||
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 3, 4), FastStream.of(list).push(3, 4).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -236,15 +265,19 @@ public class FastStreamTest {
|
||||
List<Integer> list = Arrays.asList(2, 3);
|
||||
List<Integer> unshift = FastStream.of(list).unshift(1).toList();
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 3), unshift);
|
||||
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 2, 3), FastStream.of(list).unshift(1, 2).toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAt() {
|
||||
List<Integer> list = Arrays.asList(1, 2, 3);
|
||||
Assert.assertEquals(1, (Object) FastStream.of(list).at(0));
|
||||
Assert.assertEquals(1, (Object) FastStream.of(list).at(-3));
|
||||
Assert.assertEquals(3, (Object) FastStream.of(list).at(-1));
|
||||
Assert.assertNull(FastStream.of(list).at(-4));
|
||||
Assert.assertEquals(1, (Object) FastStream.of(list).at(0).orElse(null));
|
||||
Assert.assertEquals(2, (Object) FastStream.of(list).at(1).orElse(null));
|
||||
Assert.assertEquals(3, (Object) FastStream.of(list).at(2).orElse(null));
|
||||
Assert.assertEquals(1, (Object) FastStream.of(list).at(-3).orElse(null));
|
||||
Assert.assertEquals(3, (Object) FastStream.of(list).at(-1).orElse(null));
|
||||
Assert.assertNull(FastStream.of(list).at(-4).orElse(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -262,40 +295,55 @@ public class FastStreamTest {
|
||||
@Test
|
||||
public void testFindFirst() {
|
||||
List<Integer> list = Arrays.asList(1, 2, 3);
|
||||
Integer find = FastStream.of(list).findFirst(Objects::nonNull);
|
||||
Integer find = FastStream.of(list).findFirst(Objects::nonNull).orElse(null);
|
||||
Assert.assertEquals(1, (Object) find);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFirstIdx() {
|
||||
List<Integer> list = Arrays.asList(null, 2, 3);
|
||||
Integer idx = FastStream.of(list).findFirstIdx(Objects::nonNull);
|
||||
Assert.assertEquals(1, (Object) idx);
|
||||
Assert.assertEquals(1, FastStream.of(list).findFirstIdx(Objects::nonNull));
|
||||
Assert.assertEquals(-1, (Object) FastStream.of(list).parallel().findFirstIdx(Objects::nonNull));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindLast() {
|
||||
List<Integer> list = ListUtil.of(1, null, 3);
|
||||
Integer find = FastStream.of(list).parallel().findLast(Objects::nonNull);
|
||||
Assert.assertEquals(3, (Object) find);
|
||||
List<Integer> list = ListUtil.of(1, 2, 4, 5, 6, 7, 8, 9, 10, 3);
|
||||
Assert.assertEquals(3, (Object) FastStream.of(list).findLast().orElse(null));
|
||||
Assert.assertEquals(3, (Object) FastStream.of(list).parallel().findLast().orElse(null));
|
||||
|
||||
List<Integer> list2 = ListUtil.of(1, 2, 4, 5, 6, 7, 8, 9, 10, 3, null);
|
||||
Assert.assertEquals(3, (Object) FastStream.of(list2).parallel().findLast(Objects::nonNull).orElse(null));
|
||||
|
||||
Assert.assertNull(FastStream.of().parallel().findLast(Objects::nonNull).orElse(null));
|
||||
Assert.assertNull(FastStream.of((Object) null).parallel().findLast(Objects::nonNull).orElse(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindLastIdx() {
|
||||
List<Integer> list = Arrays.asList(1, null, 3);
|
||||
Integer idx = FastStream.of(list).findLastIdx(Objects::nonNull);
|
||||
Assert.assertEquals(2, (Object) idx);
|
||||
Assert.assertEquals(2, (Object) FastStream.of(list).findLastIdx(Objects::nonNull));
|
||||
Assert.assertEquals(-1, (Object) FastStream.of(list).parallel().findLastIdx(Objects::nonNull));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReverse() {
|
||||
final List<Integer> list = ListUtil.of(Stream.iterate(1, i -> i + 1).limit(1000).collect(Collectors.toList()));
|
||||
|
||||
Assert.assertEquals(ListUtil.reverseNew(list), FastStream.of(list).reverse().toList());
|
||||
Assert.assertEquals(ListUtil.empty(), FastStream.of().reverse().toList());
|
||||
Assert.assertEquals(ListUtil.of((Object) null), FastStream.of((Object) null).reverse().toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testZip() {
|
||||
List<Integer> orders = Arrays.asList(1, 2, 3);
|
||||
List<String> list = Arrays.asList("dromara", "hutool", "sweet");
|
||||
List<String> zip = FastStream.of(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList();
|
||||
Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip);
|
||||
|
||||
zip = FastStream.iterate(1, i -> i + 1).zip(list, (e1, e2) -> e1 + "." + e2).toList();
|
||||
Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -319,4 +367,64 @@ public class FastStreamTest {
|
||||
lists = FastStream.of(list).splitList(list.size()).toList();
|
||||
Assert.assertEquals(singletonList(list), lists);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTakeWhile() {
|
||||
// 1 到 10
|
||||
final List<Integer> list = FastStream.iterate(1, i -> i <= 10, i -> i + 1).toList();
|
||||
|
||||
final List<String> res1 = FastStream.of(list)
|
||||
// 舍弃 5
|
||||
.takeWhile(e -> e < 5)
|
||||
// 过滤奇数
|
||||
.filter(e -> (e & 1) == 0)
|
||||
// 反序
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(String::valueOf)
|
||||
.toList();
|
||||
Assert.assertEquals(Arrays.asList("4", "2"), res1);
|
||||
|
||||
final List<Integer> res2 = FastStream.iterate(1, i -> i + 1)
|
||||
.parallel()
|
||||
.takeWhile(e -> e < 5)
|
||||
.map(String::valueOf)
|
||||
.map(Integer::valueOf)
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.toList();
|
||||
Assert.assertEquals(Arrays.asList(1, 2, 3, 4), res2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropWhile() {
|
||||
// 1 到 10
|
||||
final List<Integer> list = FastStream.iterate(1, i -> i <= 10, i -> i + 1).toList();
|
||||
|
||||
final List<String> res1 = FastStream.of(list)
|
||||
// 舍弃 5之前的数字
|
||||
.dropWhile(e -> e < 5)
|
||||
// 过滤偶数
|
||||
.filter(e -> (e & 1) == 1)
|
||||
// 反序
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(String::valueOf)
|
||||
.toList();
|
||||
Assert.assertEquals(Arrays.asList("9", "7", "5"), res1);
|
||||
|
||||
final List<Integer> res2 = FastStream.of(list)
|
||||
.parallel()
|
||||
.dropWhile(e -> e < 5)
|
||||
// 过滤偶数
|
||||
.filter(e -> (e & 1) == 1)
|
||||
.map(String::valueOf)
|
||||
.map(Integer::valueOf)
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.toList();
|
||||
Assert.assertEquals(Arrays.asList(5, 7, 9), res2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNotEmpty() {
|
||||
Assert.assertTrue(FastStream.of(1).isNotEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user