mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
!798 【6.x】重构增强流体系
Merge pull request !798 from Createsequence/refactor-stream
This commit is contained in:
commit
e21ca277c3
@ -0,0 +1,80 @@
|
||||
package cn.hutool.core.stream;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* {@link WrappedStream}接口的公共实现,用于包装并增强一个已有的流实例
|
||||
*
|
||||
* @param <T> 流中的元素类型
|
||||
* @param <S> {@link AbstractEnhancedWrappedStream}的实现类类型
|
||||
* @author huangchengxing
|
||||
* @see EasyStream
|
||||
* @see EntryStream
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public abstract class AbstractEnhancedWrappedStream<T, S extends AbstractEnhancedWrappedStream<T, S>>
|
||||
implements TerminableWrappedStream<T, S>, TransformableWrappedStream<T, S> {
|
||||
|
||||
/**
|
||||
* 原始的流实例
|
||||
*/
|
||||
protected final Stream<T> stream;
|
||||
|
||||
/**
|
||||
* 获取被包装的元素流实例
|
||||
*/
|
||||
@Override
|
||||
public Stream<T> unwrap() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个流包装器
|
||||
*
|
||||
* @param stream 包装的流对象
|
||||
* @throws NullPointerException 当{@code unwrap}为{@code null}时抛出
|
||||
*/
|
||||
protected AbstractEnhancedWrappedStream(final Stream<T> stream) {
|
||||
this.stream = Objects.requireNonNull(stream, "unwrap must not null");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前被包装的实例的哈希值
|
||||
*
|
||||
* @return 哈希值
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return stream.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较被包装的实例是否相等
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 是否相等
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return obj instanceof Stream && stream.equals(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将被包装的实例转为字符串
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return stream.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发流的执行,这是一个终端操作
|
||||
*/
|
||||
public void exec() {
|
||||
stream.forEach(t -> {});
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
@ -100,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);
|
||||
}
|
||||
};
|
||||
@ -268,6 +270,20 @@ public class CollectorUtil {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将流转为{@link EntryStream}
|
||||
*
|
||||
* @param keyMapper 键的映射方法
|
||||
* @param <T> 输入元素类型
|
||||
* @param <K> 元素的键类型
|
||||
* @return 收集器
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static <T, K> Collector<T, List<T>, EntryStream<K, T>> toEntryStream(
|
||||
Function<? super T, ? extends K> keyMapper) {
|
||||
return toEntryStream(keyMapper, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将流转为{@link EntryStream}
|
||||
*
|
||||
@ -277,6 +293,7 @@ public class CollectorUtil {
|
||||
* @param <K> 元素的键类型
|
||||
* @param <V> 元素的值类型
|
||||
* @return 收集器
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static <T, K, V> Collector<T, List<T>, EntryStream<K, V>> toEntryStream(
|
||||
final Function<? super T, ? extends K> keyMapper, final Function<? super T, ? extends V> valueMapper) {
|
||||
@ -290,6 +307,7 @@ public class CollectorUtil {
|
||||
*
|
||||
* @param <T> 输入元素类型
|
||||
* @return 收集器
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static <T> Collector<T, ?, EasyStream<T>> toEasyStream() {
|
||||
return transform(ArrayList::new, EasyStream::of);
|
||||
@ -300,7 +318,7 @@ public class CollectorUtil {
|
||||
* 返回的收集器的效果等同于:
|
||||
* <pre>{@code
|
||||
* Collection<T> coll = Stream.of(a, b, c, d)
|
||||
* .collect(Collectors.toCollection(collFactory));
|
||||
* .collect(Collectors.toColl(collFactory));
|
||||
* R result = mapper.apply(coll);
|
||||
* }</pre>
|
||||
*
|
||||
@ -310,6 +328,7 @@ public class CollectorUtil {
|
||||
* @param <T> 输入元素类型
|
||||
* @param <C> 中间收集输入元素的集合类型
|
||||
* @return 收集器
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static <T, R, C extends Collection<T>> Collector<T, C, R> transform(
|
||||
final Supplier<C> collFactory, final Function<C, R> mapper) {
|
||||
@ -336,6 +355,7 @@ public class CollectorUtil {
|
||||
* @param <R> 返回值类型
|
||||
* @param <T> 输入元素类型
|
||||
* @return 收集器
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static <T, R> Collector<T, List<T>, R> transform(final Function<List<T>, R> mapper) {
|
||||
return transform(ArrayList::new, mapper);
|
||||
|
@ -1,29 +1,21 @@
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* <p>{@link Stream}的扩展实现,基于原生Stream进行了封装和增强。<br>
|
||||
* 作者经对比了vavr、eclipse-collection、stream-ex以及其他语言的api,结合日常使用习惯,进行封装和拓展
|
||||
* <p>单元素的扩展流实现。基于原生Stream进行了封装和增强。<br>
|
||||
* 作者经对比了vavr、eclipse-collection、unwrap-ex以及其他语言的api,结合日常使用习惯,进行封装和拓展
|
||||
* Stream为集合提供了一些易用api,它让开发人员能使用声明式编程的方式去编写代码。
|
||||
*
|
||||
* <p>中间操作和结束操作</p>
|
||||
@ -53,14 +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<T> extends StreamWrapper<T, EasyStream<T>> implements Stream<T>, Iterable<T> {
|
||||
/**
|
||||
* 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标
|
||||
*/
|
||||
private static final int NOT_FOUND_INDEX = -1;
|
||||
public class EasyStream<T> extends AbstractEnhancedWrappedStream<T, EasyStream<T>> {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@ -78,12 +67,12 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
* 返回{@code FastStream}的建造器
|
||||
*
|
||||
* @param <T> 元素的类型
|
||||
* @return a stream builder
|
||||
* @return a unwrap builder
|
||||
*/
|
||||
public static <T> FastStreamBuilder<T> builder() {
|
||||
return new FastStreamBuilder<T>() {
|
||||
public static <T> Builder<T> builder() {
|
||||
return new Builder<T>() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Builder<T> streamBuilder = Stream.builder();
|
||||
private final Stream.Builder<T> streamBuilder = Stream.builder();
|
||||
|
||||
@Override
|
||||
public void accept(final T t) {
|
||||
@ -252,46 +241,6 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
// --------------------------------------------------------------- Static method end
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* 过滤元素,返回与 指定操作结果 匹配 指定值 的元素组成的流
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param <R> 返回类型
|
||||
* @param mapper 操作
|
||||
* @param value 用来匹配的值
|
||||
* @return 与 指定操作结果 匹配 指定值 的元素组成的流
|
||||
*/
|
||||
public <R> EasyStream<T> filter(final Function<? super T, ? extends R> mapper, final R value) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return filter(e -> Objects.equals(mapper.apply(e), value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 返回叠加过滤操作后的流
|
||||
*/
|
||||
public EasyStream<T> filterIdx(final BiPredicate<? super T, Integer> 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<T> nonNull() {
|
||||
return new EasyStream<>(stream.filter(Objects::nonNull));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行的结果组成的流
|
||||
* 这是一个无状态中间操作
|
||||
@ -302,386 +251,10 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
*/
|
||||
@Override
|
||||
public <R> EasyStream<R> map(final Function<? super T, ? extends R> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return new EasyStream<>(stream.map(mapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 元素 转换后 并且不为 {@code null} 的 新元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* <pre>{@code
|
||||
* // 等价于先调用map再调用nonNull
|
||||
* .map(...).nonNull()...
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 指定的函数
|
||||
* @param <R> 函数执行后返回的类型
|
||||
* @return 新元素组成的流
|
||||
*/
|
||||
public <R> EasyStream<R> mapNonNull(final Function<? super T, ? extends R> mapper) {
|
||||
return nonNull().<R>map(mapper).nonNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行的结果组成的流,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param mapper 指定的函数
|
||||
* @param <R> 函数执行后返回的类型
|
||||
* @return 返回叠加操作后的流
|
||||
*/
|
||||
public <R> EasyStream<R> mapIdx(final BiFunction<? super T, Integer, ? extends R> 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操作,返回多个流所有元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流:
|
||||
* <pre>{@code
|
||||
* FastStream<Long> ids = FastStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
@Override
|
||||
public <R> EasyStream<R> flatMap(final Function<? super T, ? extends Stream<? extends R>> mapper) {
|
||||
return new EasyStream<>(stream.flatMap(mapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
public <R> EasyStream<R> flatMapIdx(final BiFunction<? super T, Integer, ? extends Stream<? extends R>> 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操作, 转换为迭代器元素,
|
||||
* 最后返回所有迭代器的所有元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流:
|
||||
* <pre>{@code
|
||||
* FastStream<Long> ids = FastStream.of(users).flat(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 操作,返回可迭代对象
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
public <R> EasyStream<R> flat(final Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
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> EasyStream<R> flatNonNull(final Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
return nonNull().flat(mapper).nonNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带一个方法,调用该方法可增加元素
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
public <R> EasyStream<R> mapMulti(final BiConsumer<? super T, ? super Consumer<R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMap(e -> {
|
||||
final FastStreamBuilder<R> buffer = EasyStream.builder();
|
||||
mapper.accept(e, buffer);
|
||||
return buffer.build();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个
|
||||
* 这是一个有状态中间操作
|
||||
*
|
||||
* @param <F> 参数类型
|
||||
* @param keyExtractor 去重依据
|
||||
* @return 一个具有去重特征的流
|
||||
*/
|
||||
public <F> EasyStream<T> distinct(final Function<? super T, F> keyExtractor) {
|
||||
Objects.requireNonNull(keyExtractor);
|
||||
if (isParallel()) {
|
||||
final ConcurrentHashMap<F, Boolean> 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<F> exists = new HashSet<>();
|
||||
return of(stream.filter(e -> exists.add(keyExtractor.apply(e))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param action 指定的函数
|
||||
* @return 返回叠加操作后的FastStream
|
||||
* @apiNote 该方法存在的意义主要是用来调试
|
||||
* 当你需要查看经过操作管道某处的元素和下标,可以执行以下操作:
|
||||
* <pre>{@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());
|
||||
* }</pre>
|
||||
*/
|
||||
public EasyStream<T> peekIdx(final BiConsumer<? super T, Integer> action) {
|
||||
Objects.requireNonNull(action);
|
||||
if (isParallel()) {
|
||||
return peek(e -> action.accept(e, NOT_FOUND_INDEX));
|
||||
} else {
|
||||
final AtomicInteger index = new AtomicInteger(NOT_FOUND_INDEX);
|
||||
return peek(e -> action.accept(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回叠加调用{@link Console#log(Object)}打印出结果的流
|
||||
*
|
||||
* @return 返回叠加操作后的FastStream
|
||||
*/
|
||||
public EasyStream<T> log() {
|
||||
return peek(Console::log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个终端操作
|
||||
*
|
||||
* @param action 操作
|
||||
*/
|
||||
public void forEachIdx(final BiConsumer<? super T, Integer> 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<? super T, Integer> 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<T> findFirst(final Predicate<? super T> predicate) {
|
||||
return stream.filter(predicate).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与给定断言匹配的第一个元素的下标,并行流下标永远为-1
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1
|
||||
*/
|
||||
public int findFirstIdx(final Predicate<? super T> 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<T> findLast() {
|
||||
final MutableObj<T> last = new MutableObj<>(null);
|
||||
spliterator().forEachRemaining(last::set);
|
||||
return Optional.ofNullable(last.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与给定断言匹配的最后一个元素
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的最后一个元素
|
||||
*/
|
||||
public Optional<T> findLast(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
final MutableObj<T> 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<? super T> 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<T> reverse() {
|
||||
final T[] array = (T[]) toArray();
|
||||
ArrayUtil.reverse(array);
|
||||
return of(array).parallel(isParallel()).onClose(stream::close);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改流的并行状态
|
||||
*
|
||||
* @param parallel 是否并行
|
||||
* @return 流
|
||||
*/
|
||||
public EasyStream<T> parallel(final boolean parallel) {
|
||||
return parallel ? parallel() : sequential();
|
||||
}
|
||||
|
||||
/**
|
||||
* 与给定元素组成的流合并,成为新的流
|
||||
*
|
||||
* @param obj 元素
|
||||
* @return 流
|
||||
*/
|
||||
public EasyStream<T> push(final T obj) {
|
||||
return EasyStream.concat(this.stream, of(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* 与给定元素组成的流合并,成为新的流
|
||||
*
|
||||
* @param obj 元素
|
||||
* @return 流
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public EasyStream<T> push(final T... obj) {
|
||||
return EasyStream.concat(this.stream, of(obj));
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定元素组成的流与当前流合并,成为新的流
|
||||
*
|
||||
* @param obj 元素
|
||||
* @return 流
|
||||
*/
|
||||
public EasyStream<T> unshift(final T obj) {
|
||||
return EasyStream.concat(of(obj), this.stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定元素组成的流与当前流合并,成为新的流
|
||||
*
|
||||
* @param obj 元素
|
||||
* @return 流
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final EasyStream<T> unshift(final T... obj) {
|
||||
return EasyStream.concat(of(obj), this.stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流中指定下标的元素,如果是负数,则从最后一个开始数起
|
||||
*
|
||||
* @param idx 下标
|
||||
* @return 指定下标的元素
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<T> at(final Integer idx) {
|
||||
return Opt.ofNullable(idx).map(i -> (T) ArrayUtil.get(toArray(), i)).toOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据一个原始的流,返回一个新包装类实例
|
||||
*
|
||||
@ -689,173 +262,29 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
* @return 实现类
|
||||
*/
|
||||
@Override
|
||||
protected EasyStream<T> convertToStreamImpl(final Stream<T> stream) {
|
||||
public EasyStream<T> wrap(final Stream<T> stream) {
|
||||
return new EasyStream<>(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成集合
|
||||
*
|
||||
* @param collectionFactory 集合工厂(可以是集合构造器)
|
||||
* @param <C> 集合类型
|
||||
* @return 集合
|
||||
*/
|
||||
public <C extends Collection<T>> C toColl(final Supplier<C> collectionFactory) {
|
||||
return collect(Collectors.toCollection(collectionFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为ArrayList
|
||||
*
|
||||
* @return list
|
||||
*/
|
||||
public List<T> toList() {
|
||||
return collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为HashSet
|
||||
*
|
||||
* @return hashSet
|
||||
*/
|
||||
public Set<T> toSet() {
|
||||
return collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 与给定的可迭代对象转换成Map,key为现有元素,value为给定可迭代对象迭代的元素<br>
|
||||
* Map的大小与两个集合中较小的数量一致, 即, 只合并下标位置相同的部分
|
||||
*
|
||||
* @param other 可迭代对象
|
||||
* @param <R> 可迭代对象迭代的元素类型
|
||||
* @return map,key为现有元素,value为给定可迭代对象迭代的元素
|
||||
*/
|
||||
public <R> Map<T, R> toZip(final Iterable<R> other) {
|
||||
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的值
|
||||
final MutableObj<T> key = new MutableObj<>();
|
||||
// 保存第二个Spliterator的值
|
||||
final MutableObj<R> 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(CollectorUtil.joining(delimiter, prefix, suffix, Function.identity()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map,key为给定操作执行后的返回值,value为当前元素
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param <K> key类型
|
||||
* @return map
|
||||
*/
|
||||
public <K> Map<K, T> toMap(final Function<? super T, ? extends K> keyMapper) {
|
||||
return toMap(keyMapper, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @return map
|
||||
*/
|
||||
public <K, U> Map<K, U> toMap(final Function<? super T, ? extends K> keyMapper,
|
||||
final Function<? super T, ? extends U> valueMapper) {
|
||||
return toMap(keyMapper, valueMapper, (l, r) -> r);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param mergeFunction 合并操作
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @return map
|
||||
*/
|
||||
public <K, U> Map<K, U> toMap(final Function<? super T, ? extends K> keyMapper,
|
||||
final Function<? super T, ? extends U> valueMapper,
|
||||
final BinaryOperator<U> mergeFunction) {
|
||||
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param mergeFunction 合并操作
|
||||
* @param mapSupplier map工厂
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @param <M> map类型
|
||||
* @return map
|
||||
*/
|
||||
public <K, U, M extends Map<K, U>> M toMap(final Function<? super T, ? extends K> keyMapper,
|
||||
final Function<? super T, ? extends U> valueMapper,
|
||||
final BinaryOperator<U> mergeFunction,
|
||||
final Supplier<M> mapSupplier) {
|
||||
return collect(CollectorUtil.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将集合转换为树,默认用 {@code parentId == null} 作为顶部,内置一个小递归
|
||||
* 因为需要在当前传入数据里查找,所以这是一个结束操作
|
||||
* <p>将集合转换为树,默认用 {@code parentId == null} 作为顶部,内置一个小递归
|
||||
* 因为需要在当前传入数据里查找,所以这是一个结束操作 <br>
|
||||
*
|
||||
* @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 <R> 此处是id、parentId的泛型限制
|
||||
* @return list 组装好的树
|
||||
* @return list 组装好的树 <br>
|
||||
* eg:
|
||||
* {@code List studentTree = EasyStream.of(students).toTree(Student::getId, Student::getParentId, Student::setChildren) }
|
||||
* <pre>{@code
|
||||
* List<Student> studentTree = EasyStream.of(students).
|
||||
* toTree(Student::getId, Student::getParentId, Student::setChildren);
|
||||
* }</pre>
|
||||
*/
|
||||
public <R extends Comparable<R>> List<T> toTree(final Function<T, R> idGetter,
|
||||
final Function<T, R> pIdGetter,
|
||||
final BiConsumer<T, List<T>> childrenSetter) {
|
||||
public <R extends Comparable<R>> List<T> toTree(
|
||||
final Function<T, R> idGetter,
|
||||
final Function<T, R> pIdGetter,
|
||||
final BiConsumer<T, List<T>> childrenSetter) {
|
||||
final Map<R, List<T>> pIdValuesMap = group(pIdGetter);
|
||||
return getChildrenFromMapByPidAndSet(idGetter, childrenSetter, pIdValuesMap, pIdValuesMap.get(null));
|
||||
}
|
||||
@ -869,15 +298,20 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
* @param childrenSetter children的setter对应的lambda,可以写作 {@code Student::setChildren}
|
||||
* @param parentPredicate 树顶部的判断条件,可以写作 {@code s -> Objects.equals(s.getParentId(),0L) }
|
||||
* @param <R> 此处是id、parentId的泛型限制
|
||||
* @return list 组装好的树
|
||||
* @return list 组装好的树 <br>
|
||||
* eg:
|
||||
* {@code List studentTree = EasyStream.of(students).toTree(Student::getId, Student::getParentId, Student::setChildren, Student::getMatchParent) }
|
||||
* <pre>{@code
|
||||
* List<Student> studentTree = EasyStream.of(students).
|
||||
* .toTree(Student::getId, Student::getParentId, Student::setChildren, Student::getMatchParent);
|
||||
* }</pre>
|
||||
*/
|
||||
|
||||
public <R extends Comparable<R>> List<T> toTree(final Function<T, R> idGetter,
|
||||
final Function<T, R> pIdGetter,
|
||||
final BiConsumer<T, List<T>> childrenSetter,
|
||||
final Predicate<T> parentPredicate) {
|
||||
public <R extends Comparable<R>> List<T> toTree(
|
||||
final Function<T, R> idGetter,
|
||||
final Function<T, R> pIdGetter,
|
||||
final BiConsumer<T, List<T>> childrenSetter,
|
||||
final Predicate<T> parentPredicate) {
|
||||
Objects.requireNonNull(parentPredicate);
|
||||
final List<T> list = toList();
|
||||
final List<T> parents = EasyStream.of(list).filter(e ->
|
||||
// 此处是为了适配 parentPredicate.test空指针 情况
|
||||
@ -898,13 +332,17 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
* @param <R> 此处是id的泛型限制
|
||||
* @return list 组装好的树
|
||||
*/
|
||||
private <R extends Comparable<R>> List<T> getChildrenFromMapByPidAndSet(final Function<T, R> idGetter,
|
||||
final BiConsumer<T, List<T>> childrenSetter,
|
||||
final Map<R, List<T>> pIdValuesMap,
|
||||
final List<T> parents) {
|
||||
private <R extends Comparable<R>> List<T> getChildrenFromMapByPidAndSet(
|
||||
final Function<T, R> idGetter,
|
||||
final BiConsumer<T, List<T>> childrenSetter,
|
||||
final Map<R, List<T>> pIdValuesMap,
|
||||
final List<T> parents) {
|
||||
Objects.requireNonNull(idGetter);
|
||||
Objects.requireNonNull(childrenSetter);
|
||||
Objects.requireNonNull(pIdValuesMap);
|
||||
final MutableObj<Consumer<List<T>>> recursiveRef = new MutableObj<>();
|
||||
final Consumer<List<T>> recursive = values -> EasyStream.of(values, isParallel()).forEach(value -> {
|
||||
final List<T> children = pIdValuesMap.get(idGetter.apply(value));
|
||||
List<T> children = pIdValuesMap.get(idGetter.apply(value));
|
||||
childrenSetter.accept(value, children);
|
||||
recursiveRef.get().accept(children);
|
||||
});
|
||||
@ -914,231 +352,14 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
}
|
||||
|
||||
/**
|
||||
* 将树递归扁平化为集合,内置一个小递归(没错,lambda可以写递归)
|
||||
* 这是一个无状态中间操作
|
||||
* 建造者
|
||||
*
|
||||
* @param childrenGetter 获取子节点的lambda,可以写作 {@code Student::getChildren}
|
||||
* @param childrenSetter 设置子节点的lambda,可以写作 {@code Student::setChildren}
|
||||
* @return EasyStream 一个流
|
||||
* eg:
|
||||
* {@code List students = EasyStream.of(studentTree).flatTree(Student::getChildren, Student::setChildren).toList() }
|
||||
* @author VampireAchao
|
||||
*/
|
||||
public EasyStream<T> flatTree(final Function<T, List<T>> childrenGetter, final BiConsumer<T, List<T>> childrenSetter) {
|
||||
final MutableObj<Function<T, EasyStream<T>>> recursiveRef = new MutableObj<>();
|
||||
final Function<T, EasyStream<T>> recursive = e -> EasyStream.of(childrenGetter.apply(e)).flat(recursiveRef.get()).unshift(e);
|
||||
recursiveRef.set(recursive);
|
||||
return flat(recursive).peek(e -> childrenSetter.accept(e, null));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过给定分组依据进行分组
|
||||
*
|
||||
* @param classifier 分组依据
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @return {@link Collector}
|
||||
*/
|
||||
public <K> Map<K, List<T>> group(final Function<? super T, ? extends K> classifier) {
|
||||
return group(classifier, Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过给定分组依据进行分组
|
||||
*
|
||||
* @param classifier 分组依据
|
||||
* @param downstream 下游操作
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @param <D> 下游操作对应返回类型,也是Map中value的类型
|
||||
* @param <A> 下游操作在进行中间操作时对应类型
|
||||
* @return {@link Collector}
|
||||
*/
|
||||
public <K, A, D> Map<K, D> group(final Function<? super T, ? extends K> classifier,
|
||||
final Collector<? super T, A, D> downstream) {
|
||||
return group(classifier, HashMap::new, downstream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过给定分组依据进行分组
|
||||
*
|
||||
* @param classifier 分组依据
|
||||
* @param mapFactory 提供的map
|
||||
* @param downstream 下游操作
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @param <D> 下游操作对应返回类型,也是Map中value的类型
|
||||
* @param <A> 下游操作在进行中间操作时对应类型
|
||||
* @param <M> 最后返回结果Map类型
|
||||
* @return {@link Collector}
|
||||
*/
|
||||
public <K, D, A, M extends Map<K, D>> M group(final Function<? super T, ? extends K> classifier,
|
||||
final Supplier<M> mapFactory,
|
||||
final Collector<? super T, A, D> downstream) {
|
||||
return collect(CollectorUtil.groupingBy(classifier, mapFactory, downstream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流<br>
|
||||
* 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分<br>
|
||||
*
|
||||
* @param other 给定的迭代器
|
||||
* @param zipper 两个元素的合并器
|
||||
* @param <U> 给定的迭代对象类型
|
||||
* @param <R> 合并后的结果对象类型
|
||||
* @return 合并后的结果对象的流
|
||||
*/
|
||||
public <U, R> EasyStream<R> zip(final Iterable<U> other,
|
||||
final BiFunction<? super T, ? super U, ? extends R> zipper) {
|
||||
Objects.requireNonNull(zipper);
|
||||
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的值
|
||||
final MutableObj<T> key = new MutableObj<>();
|
||||
// 保存第二个Spliterator的值
|
||||
final MutableObj<U> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类似js的<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice">splice</a>函数
|
||||
*
|
||||
* @param start 起始下标
|
||||
* @param deleteCount 删除个数,正整数
|
||||
* @param items 放入值
|
||||
* @return 操作后的流
|
||||
*/
|
||||
@SafeVarargs
|
||||
public final EasyStream<T> splice(final int start, final int deleteCount, final T... items) {
|
||||
return of(ListUtil.splice(toList(), start, deleteCount, items))
|
||||
.parallel(isParallel())
|
||||
.onClose(stream::close);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定长度切分为双层流
|
||||
* <p>
|
||||
* 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]]
|
||||
* </p>
|
||||
*
|
||||
* @param batchSize 指定长度, 正整数
|
||||
* @return 切好的流
|
||||
*/
|
||||
public EasyStream<EasyStream<T>> split(final int batchSize) {
|
||||
final List<T> list = toList();
|
||||
final int size = list.size();
|
||||
// 指定长度 大于等于 列表长度
|
||||
if (size <= batchSize) {
|
||||
// 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]]
|
||||
return EasyStream.<EasyStream<T>>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的流
|
||||
* <p>
|
||||
* 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]]
|
||||
* </p>
|
||||
*
|
||||
* @param batchSize 指定长度, 正整数
|
||||
* @return 切好的流
|
||||
*/
|
||||
public EasyStream<List<T>> splitList(final int batchSize) {
|
||||
return split(batchSize).map(EasyStream::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 EasyStream<T> takeWhile(final 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 EasyStream<T> dropWhile(final 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前流转为另一对象。用于提供针对流本身而非流中元素的操作
|
||||
*
|
||||
* @param <R> 转换类型
|
||||
* @param transform 转换
|
||||
* @return 转换后的流
|
||||
*/
|
||||
public <R> Optional<R> transform(final Function<EasyStream<T>, R> transform) {
|
||||
Assert.notNull(transform, "transform must not null");
|
||||
return Optional.ofNullable(transform.apply(this));
|
||||
}
|
||||
|
||||
public interface FastStreamBuilder<T> extends Consumer<T>, cn.hutool.core.builder.Builder<EasyStream<T>> {
|
||||
public interface Builder<T> extends Consumer<T>, cn.hutool.core.builder.Builder<EasyStream<T>> {
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -1150,7 +371,7 @@ public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements St
|
||||
* return this;
|
||||
* }</pre>
|
||||
*/
|
||||
default FastStreamBuilder<T> add(final T t) {
|
||||
default Builder<T> add(final T t) {
|
||||
accept(t);
|
||||
return this;
|
||||
}
|
||||
|
@ -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,15 +14,21 @@ import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* <p>针对键值对对象{@link Map.Entry}特化的增强流,
|
||||
* 本身可视为一个元素类型为{@link Map.Entry}的{@link Stream}。<br>
|
||||
* 用于支持流式处理{@link Map}集合中的、或具有潜在可能转为{@link Map}集合的数据。
|
||||
* <p>参考StreamEx的EntryStream与vavr的Map,是针对键值对对象{@link Map.Entry}特化的单元素增强流实现。<br>
|
||||
* 本身可视为一个元素类型为{@link Map.Entry}的{@link Stream},
|
||||
* 用于支持流式处理{@link Map}集合中的、或其他键值对类型的数据。
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author huangchengxing
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStream<K, V>> {
|
||||
public class EntryStream<K, V> extends AbstractEnhancedWrappedStream<Map.Entry<K, V>, EntryStream<K, V>> {
|
||||
|
||||
/**
|
||||
* 默认的空键值对
|
||||
*/
|
||||
private static final Map.Entry<?, ?> EMPTY_ENTRY = new AbstractMap.SimpleImmutableEntry<>(null, null);
|
||||
|
||||
/**
|
||||
* 根据键与值的集合创建键值对流,若两集合在相同下标的位置找不到对应的键或值,则使用{@code null}填充。<br>
|
||||
@ -31,7 +38,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param values 值集合
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public static <A, B> EntryStream<A, B> merge(Iterable<A> keys, Iterable<B> values) {
|
||||
public static <A, B> EntryStream<A, B> merge(final Iterable<A> keys, final Iterable<B> values) {
|
||||
final boolean hasKeys = ObjUtil.isNotNull(keys);
|
||||
final boolean hasValues = ObjUtil.isNotNull(values);
|
||||
// 皆为空
|
||||
@ -51,7 +58,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
final Iterator<A> keyItr = keys.iterator();
|
||||
final Iterator<B> 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
|
||||
));
|
||||
@ -68,7 +75,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <B> 值类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public static <A, B> EntryStream<A, B> of(Map<A, B> map) {
|
||||
public static <A, B> EntryStream<A, B> of(final Map<A, B> map) {
|
||||
return ObjUtil.isNull(map) ?
|
||||
empty() : of(map.entrySet());
|
||||
}
|
||||
@ -83,7 +90,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <B> 值类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public static <A, B> EntryStream<A, B> of(Iterable<? extends Map.Entry<A, B>> entries) {
|
||||
public static <A, B> EntryStream<A, B> of(final Iterable<? extends Map.Entry<A, B>> entries) {
|
||||
return ObjUtil.isNull(entries) ?
|
||||
empty() : of(StreamSupport.stream(entries.spliterator(), false));
|
||||
}
|
||||
@ -99,14 +106,14 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public static <T, A, B> EntryStream<A, B> of(
|
||||
Iterable<T> source, Function<? super T, ? extends A> keyMapper, Function<? super T, ? extends B> valueMapper) {
|
||||
final Iterable<T> source, final Function<? super T, ? extends A> keyMapper, final Function<? super T, ? extends B> valueMapper) {
|
||||
Objects.requireNonNull(keyMapper);
|
||||
Objects.requireNonNull(valueMapper);
|
||||
if (ObjUtil.isNull(source)) {
|
||||
return empty();
|
||||
}
|
||||
final Stream<Map.Entry<A, B>> 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);
|
||||
}
|
||||
|
||||
@ -119,9 +126,9 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <B> 值类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public static <A, B> EntryStream<A, B> of(Stream<? extends Map.Entry<A, B>> stream) {
|
||||
public static <A, B> EntryStream<A, B> of(final Stream<? extends Map.Entry<A, B>> stream) {
|
||||
return ObjUtil.isNull(stream) ?
|
||||
empty() : new EntryStream<>(stream.map(Entry::new));
|
||||
empty() : new EntryStream<>(stream.map(EntryStream::ofEntry));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,23 +145,10 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
EntryStream(Stream<Map.Entry<K, V>> stream) {
|
||||
EntryStream(final Stream<Map.Entry<K, V>> stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
// ================================ override ================================
|
||||
|
||||
/**
|
||||
* 根据一个原始的流,返回一个新包装类实例
|
||||
*
|
||||
* @param stream 流
|
||||
* @return 实现类
|
||||
*/
|
||||
@Override
|
||||
protected EntryStream<K, V> convertToStreamImpl(Stream<Map.Entry<K, V>> stream) {
|
||||
return new EntryStream<>(stream);
|
||||
}
|
||||
|
||||
// ================================ 中间操作 ================================
|
||||
|
||||
/**
|
||||
@ -163,9 +157,10 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> distinctByKey() {
|
||||
Set<K> accessed = new ConcurrentHashSet<>(16);
|
||||
return new EntryStream<>(stream.filter(e -> {
|
||||
K key = e.getKey();
|
||||
// FIXME fix happen NPE when has null key
|
||||
final Set<K> accessed = new ConcurrentHashSet<>(16);
|
||||
return wrap(stream.filter(e -> {
|
||||
final K key = e.getKey();
|
||||
if (accessed.contains(key)) {
|
||||
return false;
|
||||
}
|
||||
@ -180,9 +175,10 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> distinctByValue() {
|
||||
Set<V> accessed = new ConcurrentHashSet<>(16);
|
||||
return new EntryStream<>(stream.filter(e -> {
|
||||
V val = e.getValue();
|
||||
// FIXME fix happen NPE when has null value
|
||||
final Set<V> accessed = new ConcurrentHashSet<>(16);
|
||||
return wrap(stream.filter(e -> {
|
||||
final V val = e.getValue();
|
||||
if (accessed.contains(val)) {
|
||||
return false;
|
||||
}
|
||||
@ -197,7 +193,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param filter 判断条件
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> filter(BiPredicate<? super K, ? super V> filter) {
|
||||
public EntryStream<K, V> filter(final BiPredicate<? super K, ? super V> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
return super.filter(e -> filter.test(e.getKey(), e.getValue()));
|
||||
}
|
||||
@ -208,7 +204,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param filter 判断条件
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> filterByKey(Predicate<? super K> filter) {
|
||||
public EntryStream<K, V> filterByKey(final Predicate<? super K> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
return super.filter(e -> filter.test(e.getKey()));
|
||||
}
|
||||
@ -219,7 +215,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param filter 判断条件
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> filterByValue(Predicate<? super V> filter) {
|
||||
public EntryStream<K, V> filterByValue(final Predicate<? super V> filter) {
|
||||
Objects.requireNonNull(filter);
|
||||
return super.filter(e -> filter.test(e.getValue()));
|
||||
}
|
||||
@ -229,7 +225,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
*
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> nonNull() {
|
||||
public EntryStream<K, V> nonNullKeyValue() {
|
||||
return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey()) && ObjUtil.isNotNull(e.getValue()));
|
||||
}
|
||||
|
||||
@ -238,7 +234,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
*
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> keyNonNull() {
|
||||
public EntryStream<K, V> nonNullKey() {
|
||||
return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey()));
|
||||
}
|
||||
|
||||
@ -247,7 +243,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
*
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> valueNonNull() {
|
||||
public EntryStream<K, V> nonNullValue() {
|
||||
return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getValue()));
|
||||
}
|
||||
|
||||
@ -257,7 +253,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param consumer 操作
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> peekKey(Consumer<? super K> consumer) {
|
||||
public EntryStream<K, V> peekKey(final Consumer<? super K> consumer) {
|
||||
Objects.requireNonNull(consumer);
|
||||
return super.peek(e -> consumer.accept(e.getKey()));
|
||||
}
|
||||
@ -268,7 +264,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param consumer 操作
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> peekValue(Consumer<? super V> consumer) {
|
||||
public EntryStream<K, V> peekValue(final Consumer<? super V> consumer) {
|
||||
Objects.requireNonNull(consumer);
|
||||
return super.peek(e -> consumer.accept(e.getValue()));
|
||||
}
|
||||
@ -279,7 +275,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param comparator 排序器
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> sortByKey(Comparator<? super K> comparator) {
|
||||
public EntryStream<K, V> sortByKey(final Comparator<? super K> comparator) {
|
||||
Objects.requireNonNull(comparator);
|
||||
return sorted(Map.Entry.comparingByKey(comparator));
|
||||
}
|
||||
@ -290,7 +286,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param comparator 排序器
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> sortByValue(Comparator<? super V> comparator) {
|
||||
public EntryStream<K, V> sortByValue(final Comparator<? super V> comparator) {
|
||||
Objects.requireNonNull(comparator);
|
||||
return sorted(Map.Entry.comparingByValue(comparator));
|
||||
}
|
||||
@ -304,8 +300,51 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param value 值
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> push(K key, V value) {
|
||||
return new EntryStream<>(Stream.concat(stream, Stream.of(new Entry<>(key, value))));
|
||||
public EntryStream<K, V> push(final K key, final V value) {
|
||||
return wrap(Stream.concat(stream, Stream.of(ofEntry(key, value))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 项当前流队首追加元素
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public EntryStream<K, V> unshift(final K key, final V value) {
|
||||
return wrap(Stream.concat(Stream.of(ofEntry(key, value)), stream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入元素转为流,返回一个前半段为当前流,后半段为新流的新{@link EasyStream}实例
|
||||
*
|
||||
* @param entries 键值对
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
@Override
|
||||
public EntryStream<K, V> append(final Iterable<? extends Map.Entry<K, V>> entries) {
|
||||
if (IterUtil.isEmpty(entries)) {
|
||||
return this;
|
||||
}
|
||||
final Stream<Map.Entry<K, V>> contacted = StreamSupport.stream(entries.spliterator(), isParallel())
|
||||
.map(EntryStream::ofEntry);
|
||||
return wrap(Stream.concat(stream, contacted));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入元素转为流,返回一个前半段为新流,后半段为当前流的新{@link EasyStream}实例
|
||||
*
|
||||
* @param entries 键值对
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
@Override
|
||||
public EntryStream<K, V> prepend(final Iterable<? extends Map.Entry<K, V>> entries) {
|
||||
if (IterUtil.isEmpty(entries)) {
|
||||
return this;
|
||||
}
|
||||
final Stream<Map.Entry<K, V>> contacted = StreamSupport.stream(entries.spliterator(), isParallel())
|
||||
.map(EntryStream::ofEntry);
|
||||
return wrap(Stream.concat(contacted, stream));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,10 +372,10 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <N> 新的键类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public <N> EntryStream<N, V> mapKeys(Function<? super K, ? extends N> mapper) {
|
||||
public <N> EntryStream<N, V> mapKeys(final Function<? super K, ? extends N> 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()))
|
||||
);
|
||||
}
|
||||
|
||||
@ -347,10 +386,10 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <N> 新的值类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
public <N> EntryStream<K, N> mapValues(Function<? super V, ? extends N> mapper) {
|
||||
public <N> EntryStream<K, N> mapValues(final Function<? super V, ? extends N> 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())))
|
||||
);
|
||||
}
|
||||
|
||||
@ -363,7 +402,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 返回叠加操作后的流
|
||||
*/
|
||||
@Override
|
||||
public <R> EasyStream<R> map(Function<? super Map.Entry<K, V>, ? extends R> mapper) {
|
||||
public <R> EasyStream<R> map(final Function<? super Map.Entry<K, V>, ? extends R> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return EasyStream.of(stream.map(mapper));
|
||||
}
|
||||
@ -375,7 +414,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <N> 函数执行后返回流中元素的类型
|
||||
* @return 映射后的单对象组成的流
|
||||
*/
|
||||
public <N> EasyStream<N> map(BiFunction<? super K, ? super V, ? extends N> mapper) {
|
||||
public <N> EasyStream<N> map(final BiFunction<? super K, ? super V, ? extends N> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return EasyStream.of(stream.map(e -> mapper.apply(e.getKey(), e.getValue())));
|
||||
}
|
||||
@ -393,7 +432,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
@Override
|
||||
public <R> EasyStream<R> flatMap(Function<? super Map.Entry<K, V>, ? extends Stream<? extends R>> mapper) {
|
||||
public <R> EasyStream<R> flatMap(final Function<? super Map.Entry<K, V>, ? extends Stream<? extends R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return EasyStream.of(stream.flatMap(mapper));
|
||||
}
|
||||
@ -403,21 +442,21 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* 然后再返回由这些流中所有元素组成的流新{@link EntryStream}串行流。<br>
|
||||
* 效果类似:
|
||||
* <pre>{@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}]
|
||||
* }</pre>
|
||||
*
|
||||
* @param keyMapper 值转映射方法
|
||||
* @param <N> 新的键类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
public <N> EntryStream<N, V> flatMapKey(Function<? super K, Stream<? extends N>> keyMapper) {
|
||||
public <N> EntryStream<N, V> flatMapKey(final Function<? super K, Stream<? extends N>> keyMapper) {
|
||||
Objects.requireNonNull(keyMapper);
|
||||
return new EntryStream<>(
|
||||
stream.flatMap(e -> keyMapper
|
||||
.apply(e.getKey())
|
||||
.map(newKey -> new Entry<>(newKey, e.getValue()))
|
||||
.map(newKey -> ofEntry(newKey, e.getValue()))
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -427,21 +466,21 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* 然后再返回由这些流中所有元素组成的流新{@link EntryStream}串行流。<br>
|
||||
* 效果类似:
|
||||
* <pre>{@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}]
|
||||
* }</pre>
|
||||
*
|
||||
* @param valueMapper 值转映射方法
|
||||
* @param <N> 新的值类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
public <N> EntryStream<K, N> flatMapValue(Function<? super V, Stream<? extends N>> valueMapper) {
|
||||
public <N> EntryStream<K, N> flatMapValue(final Function<? super V, Stream<? extends N>> valueMapper) {
|
||||
Objects.requireNonNull(valueMapper);
|
||||
return new EntryStream<>(
|
||||
stream.flatMap(e -> valueMapper
|
||||
.apply(e.getValue())
|
||||
.map(newVal -> new Entry<>(e.getKey(), newVal))
|
||||
.map(newVal -> ofEntry(e.getKey(), newVal))
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -456,7 +495,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
* @see Collectors#toMap(Function, Function, BinaryOperator, Supplier)
|
||||
*/
|
||||
public Map<K, V> toMap(Supplier<Map<K, V>> mapFactory, BinaryOperator<V> operator) {
|
||||
public Map<K, V> toMap(final Supplier<Map<K, V>> mapFactory, final BinaryOperator<V> operator) {
|
||||
Objects.requireNonNull(mapFactory);
|
||||
Objects.requireNonNull(operator);
|
||||
return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, operator, mapFactory));
|
||||
@ -469,7 +508,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
* @see Collectors#toMap(Function, Function, BinaryOperator)
|
||||
*/
|
||||
public Map<K, V> toMap(Supplier<Map<K, V>> mapFactory) {
|
||||
public Map<K, V> toMap(final Supplier<Map<K, V>> mapFactory) {
|
||||
Objects.requireNonNull(mapFactory);
|
||||
return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (t1, t2) -> t2, mapFactory));
|
||||
}
|
||||
@ -496,7 +535,9 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @see Collectors#groupingBy(Function, Supplier, Collector)
|
||||
*/
|
||||
public <N> Table<N, K, V> toTable(
|
||||
BiFunction<? super K, ? super V, ? extends N> rowKeyMapper, Supplier<Map<K, V>> colMapFactory, BinaryOperator<V> operator) {
|
||||
final BiFunction<? super K, ? super V, ? extends N> rowKeyMapper,
|
||||
final Supplier<Map<K, V>> colMapFactory,
|
||||
final BinaryOperator<V> operator) {
|
||||
Objects.requireNonNull(rowKeyMapper);
|
||||
Objects.requireNonNull(colMapFactory);
|
||||
Objects.requireNonNull(operator);
|
||||
@ -516,7 +557,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
* @throws IllegalArgumentException 当父集合或子集合中的键重复时抛出
|
||||
*/
|
||||
public <N> Table<N, K, V> toTable(BiFunction<? super K, ? super V, ? extends N> rowKeyMapper) {
|
||||
public <N> Table<N, K, V> toTable(final BiFunction<? super K, ? super V, ? extends N> rowKeyMapper) {
|
||||
return toTable(rowKeyMapper, HashMap::new, throwingMerger());
|
||||
}
|
||||
|
||||
@ -530,7 +571,9 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
*/
|
||||
public <N> Table<N, K, V> toTableByKey(
|
||||
Function<? super K, ? extends N> rowKeyMapper, Supplier<Map<K, V>> colMapFactory, BinaryOperator<V> operator) {
|
||||
final Function<? super K, ? extends N> rowKeyMapper,
|
||||
final Supplier<Map<K, V>> colMapFactory,
|
||||
final BinaryOperator<V> operator) {
|
||||
return toTable((k, v) -> rowKeyMapper.apply(k), colMapFactory, operator);
|
||||
}
|
||||
|
||||
@ -542,7 +585,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
* @throws IllegalArgumentException 当父集合或子集合中的键重复时抛出
|
||||
*/
|
||||
public <N> Table<N, K, V> toTableByKey(Function<? super K, ? extends N> rowKeyMapper) {
|
||||
public <N> Table<N, K, V> toTableByKey(final Function<? super K, ? extends N> rowKeyMapper) {
|
||||
return toTable((k, v) -> rowKeyMapper.apply(k));
|
||||
}
|
||||
|
||||
@ -556,7 +599,9 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
*/
|
||||
public <N> Table<N, K, V> toTableByValue(
|
||||
Function<? super V, ? extends N> rowKeyMapper, Supplier<Map<K, V>> colMapFactory, BinaryOperator<V> operator) {
|
||||
final Function<? super V, ? extends N> rowKeyMapper,
|
||||
final Supplier<Map<K, V>> colMapFactory,
|
||||
final BinaryOperator<V> operator) {
|
||||
return toTable((k, v) -> rowKeyMapper.apply(v), colMapFactory, operator);
|
||||
}
|
||||
|
||||
@ -568,7 +613,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @return 集合
|
||||
* @throws IllegalArgumentException 当父集合或子集合中的键重复时抛出
|
||||
*/
|
||||
public <N> Table<N, K, V> toTableByValue(Function<? super V, ? extends N> rowKeyMapper) {
|
||||
public <N> Table<N, K, V> toTableByValue(final Function<? super V, ? extends N> rowKeyMapper) {
|
||||
return toTable((k, v) -> rowKeyMapper.apply(v));
|
||||
}
|
||||
|
||||
@ -588,7 +633,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <C> 值集合的类型
|
||||
* @return 集合
|
||||
*/
|
||||
public <C extends Collection<V>> Map<K, C> groupByKey(Collector<V, ?, C> collector) {
|
||||
public <C extends Collection<V>> Map<K, C> groupByKey(final Collector<V, ?, C> collector) {
|
||||
return groupByKey((Supplier<Map<K,C>>)HashMap::new, collector);
|
||||
}
|
||||
|
||||
@ -601,7 +646,8 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <M> 返回的map集合类型
|
||||
* @return 集合
|
||||
*/
|
||||
public <C extends Collection<V>, M extends Map<K, C>> M groupByKey(Supplier<M> mapFactory, Collector<V, ?, C> collector) {
|
||||
public <C extends Collection<V>, M extends Map<K, C>> M groupByKey(
|
||||
final Supplier<M> mapFactory, final Collector<V, ?, C> collector) {
|
||||
return super.collect(Collectors.groupingBy(
|
||||
Map.Entry::getKey, mapFactory,
|
||||
CollectorUtil.transform(ArrayList::new, s -> s.stream().map(Map.Entry::getValue).collect(collector))
|
||||
@ -613,7 +659,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
*
|
||||
* @param consumer 操作
|
||||
*/
|
||||
public void forEach(BiConsumer<K, V> consumer) {
|
||||
public void forEach(final BiConsumer<K, V> consumer) {
|
||||
Objects.requireNonNull(consumer);
|
||||
super.forEach(e -> consumer.accept(e.getKey(), e.getValue()));
|
||||
}
|
||||
@ -625,7 +671,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
*/
|
||||
public EntryStream<V, K> inverse() {
|
||||
return new EntryStream<>(
|
||||
stream.map(e -> new Entry<>(e.getValue(), e.getKey()))
|
||||
stream.map(e -> ofEntry(e.getValue(), e.getKey()))
|
||||
);
|
||||
}
|
||||
|
||||
@ -636,7 +682,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <R> 返回值类型
|
||||
* @return 收集容器
|
||||
*/
|
||||
public <R> R collectKeys(Collector<K, ?, R> collector) {
|
||||
public <R> R collectKeys(final Collector<K, ?, R> collector) {
|
||||
return toKeyStream().collect(collector);
|
||||
}
|
||||
|
||||
@ -647,7 +693,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param <R> 返回值类型
|
||||
* @return 收集容器
|
||||
*/
|
||||
public <R> R collectValues(Collector<V, ?, R> collector) {
|
||||
public <R> R collectValues(final Collector<V, ?, R> collector) {
|
||||
return toValueStream().collect(collector);
|
||||
}
|
||||
|
||||
@ -657,7 +703,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param predicate 判断条件
|
||||
* @return 是否
|
||||
*/
|
||||
public boolean anyMatch(BiPredicate<? super K, ? super V> predicate) {
|
||||
public boolean anyMatch(final BiPredicate<? super K, ? super V> predicate) {
|
||||
return super.anyMatch(e -> predicate.test(e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
@ -667,7 +713,7 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param predicate 判断条件
|
||||
* @return 是否
|
||||
*/
|
||||
public boolean allMatch(BiPredicate<? super K, ? super V> predicate) {
|
||||
public boolean allMatch(final BiPredicate<? super K, ? super V> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return super.allMatch(e -> predicate.test(e.getKey(), e.getValue()));
|
||||
}
|
||||
@ -678,90 +724,39 @@ public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStrea
|
||||
* @param predicate 判断条件
|
||||
* @return 是否
|
||||
*/
|
||||
public boolean noneMatch(BiPredicate<? super K, ? super V> predicate) {
|
||||
public boolean noneMatch(final BiPredicate<? super K, ? super V> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return super.noneMatch(e -> predicate.test(e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
// ========================= private =========================
|
||||
|
||||
/**
|
||||
* {@link Map.Entry}的基本实现
|
||||
* 将键值对转为{@link AbstractMap.SimpleImmutableEntry}
|
||||
*/
|
||||
static class Entry<K, V> implements Map.Entry<K, V> {
|
||||
@SuppressWarnings("unchecked")
|
||||
static <K, V> Map.Entry<K, V> ofEntry(final Map.Entry<K, V> entry) {
|
||||
return ObjUtil.defaultIfNull(
|
||||
entry, e -> ofEntry(e.getKey(), e.getValue()), (Map.Entry<K, V>)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<K, V> 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}
|
||||
*/
|
||||
static <K, V> Map.Entry<K, V> ofEntry(final K key, final V value) {
|
||||
return new AbstractMap.SimpleImmutableEntry<>(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据一个原始的流,返回一个新包装类实例
|
||||
*
|
||||
* @param stream 流
|
||||
* @return 实现类
|
||||
*/
|
||||
@Override
|
||||
public EntryStream<K, V> wrap(final Stream<Map.Entry<K, V>> stream) {
|
||||
return new EntryStream<>(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,513 @@
|
||||
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;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* {@link WrappedStream}的扩展,用于为实现类提供更多终端操作方法的增强接口,
|
||||
* 该接口提供的方法,返回值类型都不为{@link Stream}。
|
||||
*
|
||||
* @param <T> 流中的元素类型
|
||||
* @param <S> {@link TerminableWrappedStream}的实现类类型
|
||||
* @author huangchengxing
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface TerminableWrappedStream<T, S extends TerminableWrappedStream<T, S>> extends WrappedStream<T, S> {
|
||||
|
||||
// region ============ to collection ============
|
||||
|
||||
/**
|
||||
* 转换为{@link ArrayList}
|
||||
*
|
||||
* @return 集合
|
||||
* @see #toColl(Supplier)
|
||||
*/
|
||||
default List<T> toList() {
|
||||
return this.toColl(ArrayList::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 换为不可变集合
|
||||
*
|
||||
* @return 集合
|
||||
* @see #toColl(Supplier)
|
||||
*/
|
||||
default List<T> toUnmodifiableList() {
|
||||
return Collections.unmodifiableList(this.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为HashSet
|
||||
*
|
||||
* @return 集合
|
||||
* @see #toColl(Supplier)
|
||||
*/
|
||||
default Set<T> toSet() {
|
||||
return this.toColl(HashSet::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 换为不可变集合
|
||||
*
|
||||
* @return 集合
|
||||
* @see #toColl(Supplier)
|
||||
*/
|
||||
default Set<T> toUnmodifiableSet() {
|
||||
return Collections.unmodifiableSet(this.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成集合
|
||||
*
|
||||
* @param collectionFactory 集合工厂(可以是集合构造器)
|
||||
* @param <C> 集合类型
|
||||
* @return 集合
|
||||
*/
|
||||
default <C extends Collection<T>> C toColl(final Supplier<C> collectionFactory) {
|
||||
Objects.requireNonNull(collectionFactory);
|
||||
return unwrap().collect(Collectors.toCollection(collectionFactory));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ to map ============
|
||||
|
||||
/**
|
||||
* 转换为map,key为给定操作执行后的返回值,value为当前元素
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param <K> key类型
|
||||
* @return map
|
||||
* @see #toMap(Function, Function, BinaryOperator, Supplier)
|
||||
*/
|
||||
default <K> Map<K, T> toMap(final Function<? super T, ? extends K> keyMapper) {
|
||||
return this.toMap(keyMapper, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @return map
|
||||
* @see #toMap(Function, Function, BinaryOperator, Supplier)
|
||||
*/
|
||||
default <K, U> Map<K, U> toMap(
|
||||
final Function<? super T, ? extends K> keyMapper, final Function<? super T, ? extends U> valueMapper) {
|
||||
return this.toMap(keyMapper, valueMapper, (l, r) -> r);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为不可变map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @return map
|
||||
* @see #toMap(Function, Function, BinaryOperator, Supplier)
|
||||
*/
|
||||
default <K, U> Map<K, U> toUnmodifiableMap(
|
||||
final Function<? super T, ? extends K> keyMapper, final Function<? super T, ? extends U> valueMapper) {
|
||||
return Collections.unmodifiableMap(this.toMap(keyMapper, valueMapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param mergeFunction 合并操作
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @return map
|
||||
* @see #toMap(Function, Function, BinaryOperator, Supplier)
|
||||
*/
|
||||
default <K, U> Map<K, U> toMap(
|
||||
final Function<? super T, ? extends K> keyMapper,
|
||||
final Function<? super T, ? extends U> valueMapper,
|
||||
final BinaryOperator<U> mergeFunction) {
|
||||
return this.toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为不可变map,key,value为给定操作执行后的返回值
|
||||
*
|
||||
* @param keyMapper 指定的key操作
|
||||
* @param valueMapper 指定value操作
|
||||
* @param mergeFunction 合并操作
|
||||
* @param <K> key类型
|
||||
* @param <U> value类型
|
||||
* @return map
|
||||
* @see #toMap(Function, Function, BinaryOperator, Supplier)
|
||||
*/
|
||||
default <K, U> Map<K, U> toUnmodifiableMap(
|
||||
final Function<? super T, ? extends K> keyMapper,
|
||||
final Function<? super T, ? extends U> valueMapper,
|
||||
final BinaryOperator<U> 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 <K> key类型
|
||||
* @param <U> value类型
|
||||
* @param <M> map类型
|
||||
* @return map
|
||||
*/
|
||||
default <K, U, M extends Map<K, U>> M toMap(
|
||||
final Function<? super T, ? extends K> keyMapper,
|
||||
final Function<? super T, ? extends U> valueMapper,
|
||||
final BinaryOperator<U> mergeFunction,
|
||||
Supplier<M> mapSupplier) {
|
||||
Objects.requireNonNull(keyMapper);
|
||||
Objects.requireNonNull(valueMapper);
|
||||
Objects.requireNonNull(mergeFunction);
|
||||
Objects.requireNonNull(mapSupplier);
|
||||
return unwrap().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ to zip ============
|
||||
|
||||
/**
|
||||
* 与给定的可迭代对象转换成map,key为现有元素,value为给定可迭代对象迭代的元素<br>
|
||||
* 至少包含全部的key,如果对应位置上的value不存在,则为null
|
||||
*
|
||||
* @param other 可迭代对象
|
||||
* @param <R> 可迭代对象迭代的元素类型
|
||||
* @return map,key为现有元素,value为给定可迭代对象迭代的元素;<br>
|
||||
* 至少包含全部的key,如果对应位置上的value不存在,则为null;<br>
|
||||
* 如果key重复, 则保留最后一个关联的value;<br>
|
||||
*/
|
||||
default <R> Map<T, R> toZip(final Iterable<R> other) {
|
||||
Objects.requireNonNull(other);
|
||||
// value对象迭代器
|
||||
final Iterator<R> iterator = Opt.ofNullable(other).map(Iterable::iterator).orElseGet(Collections::emptyIterator);
|
||||
if (this.isParallel()) {
|
||||
final List<T> keyList = toList();
|
||||
final Map<T, R> 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 ============ to optional ============
|
||||
|
||||
/**
|
||||
* 将当前流转为另一对象。用于提供针对流本身而非流中元素的操作
|
||||
*
|
||||
* @param <R> 转换类型
|
||||
* @param transform 转换
|
||||
* @return 转换后的流
|
||||
*/
|
||||
default <R> Optional<R> transform(final Function<? super S, R> transform) {
|
||||
Objects.requireNonNull(transform);
|
||||
return Optional.ofNullable(transform.apply(wrap(this)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与给定断言匹配的第一个元素
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的第一个元素
|
||||
*/
|
||||
default Optional<T> findFirst(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return unwrap().filter(predicate).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与给定断言匹配的第一个元素的下标,并行流下标永远为-1
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的第一个元素的下标,如果不存在则返回-1
|
||||
*/
|
||||
default int findFirstIdx(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
if (isParallel()) {
|
||||
return NOT_FOUND_ELEMENT_INDEX;
|
||||
} else {
|
||||
final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX);
|
||||
unwrap().filter(e -> {
|
||||
index.increment();
|
||||
return predicate.test(e);
|
||||
}).findFirst();
|
||||
return index.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最后一个元素
|
||||
*
|
||||
* @return 最后一个元素
|
||||
*/
|
||||
default Optional<T> findLast() {
|
||||
final MutableObj<T> last = new MutableObj<>(null);
|
||||
spliterator().forEachRemaining(last::set);
|
||||
return Optional.ofNullable(last.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与给定断言匹配的最后一个元素
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与给定断言匹配的最后一个元素
|
||||
*/
|
||||
default Optional<T> findLast(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
final MutableObj<T> 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<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
if (isParallel()) {
|
||||
return NOT_FOUND_ELEMENT_INDEX;
|
||||
} else {
|
||||
final MutableInt idxRef = new MutableInt(NOT_FOUND_ELEMENT_INDEX);
|
||||
forEachIdx((e, i) -> {
|
||||
if (predicate.test(e)) {
|
||||
idxRef.set(i);
|
||||
}
|
||||
});
|
||||
return idxRef.get();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取流中指定下标的元素,如果是负数,则从最后一个开始数起
|
||||
*
|
||||
* @param idx 下标
|
||||
* @return 指定下标的元素
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default Optional<T> at(final Integer idx) {
|
||||
return Opt.ofNullable(idx).map(i -> (T) ArrayUtil.get(toArray(), i)).toOptional();
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ to boolean ============
|
||||
|
||||
/**
|
||||
* 流是否为空
|
||||
*
|
||||
* @return 流是否为空
|
||||
*/
|
||||
default boolean isEmpty() {
|
||||
return !findAny().isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 流是否不为空
|
||||
*
|
||||
* @return 流是否不为空
|
||||
*/
|
||||
default boolean isNotEmpty() {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ join ============
|
||||
|
||||
/**
|
||||
* 返回拼接后的字符串
|
||||
*
|
||||
* @return 拼接后的字符串
|
||||
* @see #join(CharSequence, CharSequence, CharSequence)
|
||||
*/
|
||||
default String join() {
|
||||
return this.join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回拼接后的字符串
|
||||
*
|
||||
* @param delimiter 分隔符
|
||||
* @return 拼接后的字符串
|
||||
* @see #join(CharSequence, CharSequence, CharSequence)
|
||||
*/
|
||||
default String join(final CharSequence delimiter) {
|
||||
return this.join(delimiter, "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回拼接后的字符串
|
||||
*
|
||||
* @param delimiter 分隔符
|
||||
* @param prefix 前缀
|
||||
* @param suffix 后缀
|
||||
* @return 拼接后的字符串
|
||||
*/
|
||||
default String join(final CharSequence delimiter, final CharSequence prefix, final CharSequence suffix) {
|
||||
return unwrap().map(String::valueOf).collect(CollectorUtil.joining(delimiter, prefix, suffix, Function.identity()));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ group ============
|
||||
|
||||
/**
|
||||
* 通过给定分组依据进行分组
|
||||
*
|
||||
* @param classifier 分组依据,得到的键为{@code null}时不会抛出异常
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @return map
|
||||
* @see #group(Function, Supplier, Collector)
|
||||
*/
|
||||
default <K> Map<K, List<T>> group(final Function<? super T, ? extends K> classifier) {
|
||||
return this.group(classifier, Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过给定分组依据进行分组
|
||||
*
|
||||
* @param classifier 分组依据,得到的键为{@code null}时不会抛出异常
|
||||
* @param downstream 下游操作
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @param <D> 下游操作对应返回类型,也是Map中value的类型
|
||||
* @param <A> 下游操作在进行中间操作时对应类型
|
||||
* @return map
|
||||
* @see #group(Function, Supplier, Collector)
|
||||
*/
|
||||
default <K, A, D> Map<K, D> group(
|
||||
final Function<? super T, ? extends K> classifier, final Collector<? super T, A, D> downstream) {
|
||||
return this.group(classifier, HashMap::new, downstream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过给定分组依据进行分组
|
||||
*
|
||||
* @param classifier 分组依据,得到的键为{@code null}时不会抛出异常
|
||||
* @param mapFactory 提供的map
|
||||
* @param downstream 下游操作
|
||||
* @param <K> 实体中的分组依据对应类型,也是Map中key的类型
|
||||
* @param <D> 下游操作对应返回类型,也是Map中value的类型
|
||||
* @param <A> 下游操作在进行中间操作时对应类型
|
||||
* @param <M> 最后返回结果Map类型
|
||||
* @return map
|
||||
* @see CollectorUtil#groupingBy(Function, Supplier, Collector)
|
||||
*/
|
||||
default <K, D, A, M extends Map<K, D>> M group(
|
||||
final Function<? super T, ? extends K> classifier,
|
||||
final Supplier<M> mapFactory,
|
||||
final Collector<? super T, A, D> downstream) {
|
||||
Objects.requireNonNull(classifier);
|
||||
Objects.requireNonNull(mapFactory);
|
||||
Objects.requireNonNull(downstream);
|
||||
return unwrap().collect(CollectorUtil.groupingBy(classifier, mapFactory, downstream));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定判断条件分组
|
||||
*
|
||||
* @param predicate 判断条件
|
||||
* @return map
|
||||
* @see #partition(Predicate, Collector)
|
||||
*/
|
||||
default Map<Boolean, List<T>> partition(final Predicate<T> predicate) {
|
||||
return this.partition(predicate, ArrayList::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定判断条件分组
|
||||
*
|
||||
* @param predicate 判断条件
|
||||
* @param collFactory 提供的集合
|
||||
* @return map
|
||||
* @see #partition(Predicate, Collector)
|
||||
*/
|
||||
default <C extends Collection<T>> Map<Boolean, C> partition(final Predicate<T> predicate, final Supplier<C> collFactory) {
|
||||
return this.partition(predicate, Collectors.toCollection(collFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据给定判断条件分组
|
||||
*
|
||||
* @param predicate 判断条件
|
||||
* @param downstream 下游操作
|
||||
* @param <R> 返回值类型
|
||||
* @return map
|
||||
*/
|
||||
default <R> Map<Boolean, R> partition(final Predicate<T> predicate, final Collector<T, ?, R> downstream) {
|
||||
Objects.requireNonNull(predicate);
|
||||
Objects.requireNonNull(downstream);
|
||||
return unwrap().collect(Collectors.partitioningBy(predicate, downstream));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ foreach ============
|
||||
|
||||
/**
|
||||
* 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个终端操作
|
||||
*
|
||||
* @param action 操作
|
||||
*/
|
||||
default void forEachIdx(final BiConsumer<? super T, Integer> action) {
|
||||
Objects.requireNonNull(action);
|
||||
if (isParallel()) {
|
||||
unwrap().forEach(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX));
|
||||
} else {
|
||||
final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX);
|
||||
unwrap().forEach(e -> action.accept(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个终端操作
|
||||
*
|
||||
* @param action 操作
|
||||
*/
|
||||
default void forEachOrderedIdx(final BiConsumer<? super T, Integer> action) {
|
||||
Objects.requireNonNull(action);
|
||||
if (isParallel()) {
|
||||
unwrap().forEachOrdered(e -> action.accept(e, NOT_FOUND_ELEMENT_INDEX));
|
||||
} else {
|
||||
final MutableInt index = new MutableInt(NOT_FOUND_ELEMENT_INDEX);
|
||||
unwrap().forEachOrdered(e -> action.accept(e, index.incrementAndGet()));
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
}
|
@ -0,0 +1,571 @@
|
||||
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 <T> 流中的元素类型
|
||||
* @param <S> {@link TransformableWrappedStream}的实现类类型
|
||||
* @author huangchengxing
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface TransformableWrappedStream<T, S extends TransformableWrappedStream<T, S>> extends WrappedStream<T, S> {
|
||||
|
||||
/**
|
||||
* 将 现有元素 与 给定迭代器中对应位置的元素 使用 zipper 转换为新的元素,并返回新元素组成的流<br>
|
||||
* 新流的数量为两个集合中较小的数量, 即, 只合并下标位置相同的部分<br>
|
||||
*
|
||||
* @param other 给定的迭代器
|
||||
* @param zipper 两个元素的合并器
|
||||
* @param <U> 给定的迭代对象类型
|
||||
* @param <R> 合并后的结果对象类型
|
||||
* @return 合并后的结果对象的流
|
||||
*/
|
||||
default <U, R> EasyStream<R> zip(
|
||||
final Iterable<U> other,
|
||||
final BiFunction<? super T, ? super U, ? extends R> zipper) {
|
||||
Objects.requireNonNull(zipper);
|
||||
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的值
|
||||
final MutableObj<T> key = new MutableObj<>();
|
||||
// 保存第二个Spliterator的值
|
||||
final MutableObj<U> 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(unwrap()::close);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定长度切分为双层流
|
||||
* <p>
|
||||
* 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]]
|
||||
* </p>
|
||||
*
|
||||
* @param batchSize 指定长度, 正整数
|
||||
* @return 切好的流
|
||||
*/
|
||||
default EasyStream<EasyStream<T>> split(final int batchSize) {
|
||||
final List<T> list = this.collect(Collectors.toList());
|
||||
final int size = list.size();
|
||||
// 指定长度 大于等于 列表长度
|
||||
if (size <= batchSize) {
|
||||
// 返回第一层只有单个元素的双层流,形如:[[1,2,3,4,5]]
|
||||
return EasyStream.<EasyStream<T>>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(unwrap()::close);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定长度切分为元素为list的流
|
||||
* <p>
|
||||
* 形如:[1,2,3,4,5] -> [[1,2], [3,4], [5,6]]
|
||||
* </p>
|
||||
*
|
||||
* @param batchSize 指定长度, 正整数
|
||||
* @return 切好的流
|
||||
*/
|
||||
default EasyStream<List<T>> splitList(final int batchSize) {
|
||||
return split(batchSize).map(EasyStream::toList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前流转为键值对流
|
||||
*
|
||||
* @param keyMapper 键的映射方法
|
||||
* @param valueMapper 值的映射方法
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
default <K, V> EntryStream<K, V> toEntries(final Function<T, K> keyMapper, final Function<T, V> valueMapper) {
|
||||
Objects.requireNonNull(keyMapper);
|
||||
Objects.requireNonNull(valueMapper);
|
||||
return new EntryStream<>(map(t -> EntryStream.ofEntry(keyMapper.apply(t), valueMapper.apply(t))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前流转为键值对流
|
||||
*
|
||||
* @param keyMapper 键的映射方法
|
||||
* @param <K> 键类型
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
default <K> EntryStream<K, T> toEntries(final Function<T, K> keyMapper) {
|
||||
return toEntries(keyMapper, Function.identity());
|
||||
}
|
||||
|
||||
// region ============ generic ============
|
||||
|
||||
/**
|
||||
* 反转顺序
|
||||
*
|
||||
* @return 反转元素顺序
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default S reverse() {
|
||||
final T[] array = (T[]) toArray();
|
||||
ArrayUtil.reverse(array);
|
||||
return wrap(Stream.of(array)).parallel(isParallel());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改流的并行状态
|
||||
*
|
||||
* @param parallel 是否并行
|
||||
* @return 流
|
||||
*/
|
||||
default S parallel(final boolean parallel) {
|
||||
return parallel ? parallel() : sequential();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过删除或替换现有元素或者原地添加新的元素来修改列表,并以列表形式返回被修改的内容。此方法不会改变原列表。
|
||||
* 类似js的<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/splice">splice</a>函数
|
||||
*
|
||||
* @param start 起始下标
|
||||
* @param deleteCount 删除个数,正整数
|
||||
* @param items 放入值
|
||||
* @return 操作后的流
|
||||
*/
|
||||
default S splice(final int start, final int deleteCount, final T... items) {
|
||||
final List<T> elements = unwrap().collect(Collectors.toList());
|
||||
return wrap(ListUtil.splice(elements, start, deleteCount, items).stream())
|
||||
.parallel(isParallel());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>遍历流中与断言匹配的元素,当遇到第一个不匹配的元素时终止,返回由匹配的元素组成的流。<br>
|
||||
* eg:
|
||||
* <pre>{@code
|
||||
* EasyStream.of(1, 2, 3, 4, 5)
|
||||
* .takeWhile(i -> Objects.equals(3, i)) // 获取元素,一直到遇到第一个3为止
|
||||
* .toList(); // = [1, 2]
|
||||
* }</pre>
|
||||
*
|
||||
* <p>与{@code JDK9}中的{@code takeWhile}方法不太一样,此操作为顺序且有状态的中间操作。
|
||||
* 即使在并行流中,该操作仍然是顺序执行的,并且不影响后续的并行操作:
|
||||
* <pre>{@code
|
||||
* EasyStream.iterate(1, i -> i + 1)
|
||||
* .parallel()
|
||||
* .takeWhile(e -> e < 50) // 顺序执行
|
||||
* .map(e -> e + 1) // 并发
|
||||
* .map(String::valueOf) // 并发
|
||||
* .toList();
|
||||
* }</pre>
|
||||
* 若非必要,不推荐在并行流中进行该操作。
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 与指定断言匹配的元素组成的流
|
||||
*/
|
||||
default S takeWhile(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return wrap(StreamUtil.takeWhile(unwrap(), predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* <<p>删除流中与断言匹配的元素,当遇到第一个不匹配的元素时终止,返回由剩余不匹配的元素组成的流。<br>
|
||||
* eg:
|
||||
* <pre>{@code
|
||||
* EasyStream.of(1, 2, 3, 4, 5)
|
||||
* .dropWhile(i -> !Objects.equals(3, i)) // 删除不为3的元素,一直到遇到第一个3为止
|
||||
* .toList(); // = [3, 4, 5]
|
||||
* }</pre>
|
||||
*
|
||||
* <p>与{@code JDK9}中的{@code dropWhile}方法不太一样,此操作为顺序且有状态的中间操作。
|
||||
* 即使在并行流中,该操作仍然是顺序执行的,并且不影响后续的并行操作:
|
||||
* <pre>{@code
|
||||
* EasyStream.iterate(1, i -> i + 1)
|
||||
* .parallel()
|
||||
* .dropWhile(e -> e < 50) // 顺序执行
|
||||
* .map(e -> e + 1) // 并发
|
||||
* .map(String::valueOf) // 并发
|
||||
* .toList();
|
||||
* }</pre>
|
||||
* 若非必要,不推荐在并行流中进行该操作。
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 剩余元素组成的流
|
||||
*/
|
||||
default S dropWhile(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return wrap(StreamUtil.dropWhile(unwrap(), predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个
|
||||
* 这是一个有状态中间操作
|
||||
*
|
||||
* @param <F> 参数类型
|
||||
* @param keyExtractor 去重依据
|
||||
* @return 一个具有去重特征的流
|
||||
*/
|
||||
default <F> EasyStream<T> distinct(final Function<? super T, F> keyExtractor) {
|
||||
Objects.requireNonNull(keyExtractor);
|
||||
if (isParallel()) {
|
||||
final ConcurrentHashMap<F, Boolean> exists = MapUtil.newConcurrentHashMap();
|
||||
// 标记是否出现过null值,用于保留第一个出现的null
|
||||
// 由于ConcurrentHashMap的key不能为null,所以用此变量来标记
|
||||
final AtomicBoolean hasNull = new AtomicBoolean(false);
|
||||
return EasyStream.of(unwrap().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<F> exists = new HashSet<>();
|
||||
return EasyStream.of(unwrap().filter(e -> exists.add(keyExtractor.apply(e))));
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ peek ============
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
* @param action 指定的函数
|
||||
* @return 返回叠加操作后的FastStream
|
||||
* @apiNote 该方法存在的意义主要是用来调试
|
||||
* 当你需要查看经过操作管道某处的元素和下标,可以执行以下操作:
|
||||
* <pre>{@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());
|
||||
* }</pre>
|
||||
*/
|
||||
default S peekIdx(final BiConsumer<? super T, Integer> 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<T> result = unwrap();
|
||||
if (ArrayUtil.isNotEmpty(obj)) {
|
||||
result = Stream.concat(unwrap(), Stream.of(obj));
|
||||
}
|
||||
return wrap(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给定元素组成的流与当前流合并,成为新的流
|
||||
*
|
||||
* @param obj 元素
|
||||
* @return 流
|
||||
*/
|
||||
default S unshift(final T... obj) {
|
||||
Stream<T> result = unwrap();
|
||||
if (ArrayUtil.isNotEmpty(obj)) {
|
||||
result = Stream.concat(Stream.of(obj), unwrap());
|
||||
}
|
||||
return wrap(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入元素转为流,返回一个前半段为当前流,后半段为新流的新实例
|
||||
*
|
||||
* @param iterable 集合
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
default S append(final Iterable<? extends T> iterable) {
|
||||
if (IterUtil.isEmpty(iterable)) {
|
||||
return wrap(this);
|
||||
}
|
||||
final Stream<? extends T> contacted = StreamSupport.stream(iterable.spliterator(), isParallel());
|
||||
return wrap(Stream.concat(this, contacted));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将输入元素转为流,返回一个前半段为新流,后半段为当前流的新实例
|
||||
*
|
||||
* @param iterable 集合
|
||||
* @return {@link EntryStream}实例
|
||||
*/
|
||||
default S prepend(final Iterable<? extends T> iterable) {
|
||||
if (IterUtil.isEmpty(iterable)) {
|
||||
return wrap(this);
|
||||
}
|
||||
final Stream<? extends T> contacted = StreamSupport.stream(iterable.spliterator(), isParallel());
|
||||
return wrap(Stream.concat(contacted, this));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ filter ============
|
||||
|
||||
/**
|
||||
* 过滤掉空元素
|
||||
*
|
||||
* @return 过滤后的流
|
||||
*/
|
||||
default S nonNull() {
|
||||
return filter(Objects::nonNull);
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤元素,返回与指定断言匹配的元素组成的流,断言带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param predicate 断言
|
||||
* @return 返回叠加过滤操作后的流
|
||||
*/
|
||||
default S filterIdx(final BiPredicate<? super T, Integer> 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 <R> 返回类型
|
||||
* @param mapper 操作
|
||||
* @param value 用来匹配的值
|
||||
* @return 与 指定操作结果 匹配 指定值 的元素组成的流
|
||||
*/
|
||||
default <R> S filter(final Function<? super T, ? extends R> mapper, final R value) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return filter(e -> Objects.equals(mapper.apply(e), value));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ flat ============
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流:
|
||||
* <pre>{@code
|
||||
* EasyStream<Long> ids = EasyStream.of(users).flatMap(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
@Override
|
||||
default <R> EasyStream<R> flatMap(final Function<? super T, ? extends Stream<? extends R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return new EasyStream<>(unwrap().flatMap(mapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
default <R> EasyStream<R> flatMapIdx(final BiFunction<? super T, Integer, ? extends Stream<? extends R>> 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操作, 转换为迭代器元素,
|
||||
* 最后返回所有迭代器的所有元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* 例如,将users里所有user的id和parentId组合在一起,形成一个新的流:
|
||||
* <pre>{@code
|
||||
* EasyStream<Long> ids = EasyStream.of(users).flat(user -> FastStream.of(user.getId(), user.getParentId()));
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 操作,返回可迭代对象
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
default <R> EasyStream<R> flat(final Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMap(w -> EasyStream.of(mapper.apply(w)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩散流操作,可能影响流元素个数,对过滤后的非{@code null}元素执行mapper操作,转换为迭代器,
|
||||
* 并过滤迭代器中为{@code null}的元素, 返回所有迭代器的所有非空元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
*
|
||||
* @param mapper 操作,返回流
|
||||
* @param <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
* @see #flat(Function)
|
||||
* @see #nonNull()
|
||||
*/
|
||||
default <R> EasyStream<R> flatNonNull(final Function<? super T, ? extends Iterable<? extends R>> mapper) {
|
||||
return nonNull().flat(mapper).nonNull();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将树递归扁平化为集合,内置一个小递归
|
||||
* 这是一个无状态中间操作 <br>
|
||||
* eg:
|
||||
* <pre>{@code
|
||||
* List<Student> students = EasyStream.of(studentTree)
|
||||
* .flatTree(Student::getChildren, Student::setChildren)
|
||||
* .toList();
|
||||
* }</pre>
|
||||
*
|
||||
* @param childrenGetter 获取子节点的lambda,可以写作 {@code Student::getChildren}
|
||||
* @param childrenSetter 设置子节点的lambda,可以写作 {@code Student::setChildren}
|
||||
* @return EasyStream 一个流
|
||||
*/
|
||||
default S flatTree(final Function<T, List<T>> childrenGetter, final BiConsumer<T, List<T>> childrenSetter) {
|
||||
Objects.requireNonNull(childrenGetter);
|
||||
Objects.requireNonNull(childrenSetter);
|
||||
final MutableObj<Function<T, EasyStream<T>>> recursiveRef = new MutableObj<>();
|
||||
final Function<T, EasyStream<T>> recursive = e -> EasyStream.of(childrenGetter.apply(e))
|
||||
.flat(recursiveRef.get())
|
||||
.unshift(e);
|
||||
recursiveRef.set(recursive);
|
||||
return wrap(flatMap(recursive).peek(e -> childrenSetter.accept(e, null)));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region ============ map ============
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行的结果组成的流
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param mapper 指定的函数
|
||||
* @param <R> 函数执行后返回的类型
|
||||
* @return 返回叠加操作后的流
|
||||
*/
|
||||
@Override
|
||||
default <R> EasyStream<R> map(final Function<? super T, ? extends R> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return new EasyStream<>(unwrap().map(mapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 元素 转换后 并且不为 {@code null} 的 新元素组成的流<br>
|
||||
* 这是一个无状态中间操作<br>
|
||||
* <pre>{@code
|
||||
* // 等价于先调用map再调用nonNull
|
||||
* .nonNull().map(...).nonNull()...
|
||||
* }</pre>
|
||||
*
|
||||
* @param mapper 指定的函数
|
||||
* @param <R> 函数执行后返回的类型
|
||||
* @return 新元素组成的流
|
||||
*/
|
||||
default <R> EasyStream<R> mapNonNull(final Function<? super T, ? extends R> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return new EasyStream<>(nonNull().<R>map(mapper).nonNull());
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回与指定函数将元素作为参数执行的结果组成的流,操作带下标,并行流时下标永远为-1
|
||||
* 这是一个无状态中间操作
|
||||
*
|
||||
* @param mapper 指定的函数
|
||||
* @param <R> 函数执行后返回的类型
|
||||
* @return 返回叠加操作后的流
|
||||
*/
|
||||
default <R> EasyStream<R> mapIdx(final BiFunction<? super T, Integer, ? extends R> 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 <R> 拆分后流的元素类型
|
||||
* @return 返回叠加拆分操作后的流
|
||||
*/
|
||||
default <R> EasyStream<R> mapMulti(final BiConsumer<? super T, ? super Consumer<R>> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return flatMap(e -> {
|
||||
final EasyStream.Builder<R> buffer = EasyStream.builder();
|
||||
mapper.accept(e, buffer);
|
||||
return buffer.build();
|
||||
});
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
}
|
@ -5,27 +5,45 @@ import java.util.function.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
/**
|
||||
* {@link Stream}的包装类,用于基于一个已有的流实例进行扩展
|
||||
* <p>{@link Stream}实例的包装器,用于增强原始的{@link Stream},提供一些额外的中间与终端操作。 <br>
|
||||
* 默认提供两个可用实现:
|
||||
* <ul>
|
||||
* <li>{@link EasyStream}:针对单元素的通用增强流实现;</li>
|
||||
* <li>{@link EntryStream}:针对键值对类型元素的增强流实现;</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param <T> 流中的元素类型
|
||||
* @param <S> {@link WrappedStream}的实现类类型
|
||||
* @author huangchengxing
|
||||
* @see TerminableWrappedStream
|
||||
* @see TransformableWrappedStream
|
||||
* @see AbstractEnhancedWrappedStream
|
||||
* @see EasyStream
|
||||
* @see EntryStream
|
||||
* @since 6.0.0
|
||||
*/
|
||||
abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Iterable<T> {
|
||||
public interface WrappedStream<T, S extends WrappedStream<T, S>> extends Stream<T>, Iterable<T> {
|
||||
|
||||
/**
|
||||
* 原始的流实例
|
||||
* 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标
|
||||
*/
|
||||
protected final Stream<T> stream;
|
||||
int NOT_FOUND_ELEMENT_INDEX = -1;
|
||||
|
||||
/**
|
||||
* 创建一个流包装器
|
||||
* 获取被当前实例包装的流对象
|
||||
*
|
||||
* @param stream 包装的流对象
|
||||
* @return 被当前实例包装的流对象
|
||||
*/
|
||||
protected StreamWrapper(final Stream<T> stream) {
|
||||
Objects.requireNonNull(stream, "stream must not null");
|
||||
this.stream = stream;
|
||||
}
|
||||
Stream<T> unwrap();
|
||||
|
||||
/**
|
||||
* 将一个原始流包装为指定类型的增强流 <br>
|
||||
* 若{@code source}于当前实例包装的流并不相同,则该增强流与当前实例无关联关系
|
||||
*
|
||||
* @param source 被包装的流
|
||||
* @return 包装后的流
|
||||
*/
|
||||
S wrap(final Stream<T> source);
|
||||
|
||||
/**
|
||||
* 过滤元素,返回与指定断言匹配的元素组成的流
|
||||
@ -35,8 +53,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 返回叠加过滤操作后的流
|
||||
*/
|
||||
@Override
|
||||
public I filter(final Predicate<? super T> predicate) {
|
||||
return convertToStreamImpl(stream.filter(predicate));
|
||||
default S filter(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return wrap(unwrap().filter(predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,8 +66,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 叠加操作后元素类型全为int的流
|
||||
*/
|
||||
@Override
|
||||
public IntStream mapToInt(final ToIntFunction<? super T> mapper) {
|
||||
return stream.mapToInt(mapper);
|
||||
default IntStream mapToInt(final ToIntFunction<? super T> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return unwrap().mapToInt(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,8 +79,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 叠加操作后元素类型全为long的流
|
||||
*/
|
||||
@Override
|
||||
public LongStream mapToLong(final ToLongFunction<? super T> mapper) {
|
||||
return stream.mapToLong(mapper);
|
||||
default LongStream mapToLong(final ToLongFunction<? super T> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return unwrap().mapToLong(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,8 +92,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 叠加操作后元素类型全为double的流
|
||||
*/
|
||||
@Override
|
||||
public DoubleStream mapToDouble(final ToDoubleFunction<? super T> mapper) {
|
||||
return stream.mapToDouble(mapper);
|
||||
default DoubleStream mapToDouble(final ToDoubleFunction<? super T> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return unwrap().mapToDouble(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,8 +105,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 返回叠加拆分操作后的IntStream
|
||||
*/
|
||||
@Override
|
||||
public IntStream flatMapToInt(final Function<? super T, ? extends IntStream> mapper) {
|
||||
return stream.flatMapToInt(mapper);
|
||||
default IntStream flatMapToInt(final Function<? super T, ? extends IntStream> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return unwrap().flatMapToInt(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,8 +118,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 返回叠加拆分操作后的LongStream
|
||||
*/
|
||||
@Override
|
||||
public LongStream flatMapToLong(final Function<? super T, ? extends LongStream> mapper) {
|
||||
return stream.flatMapToLong(mapper);
|
||||
default LongStream flatMapToLong(final Function<? super T, ? extends LongStream> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return unwrap().flatMapToLong(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,8 +131,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 返回叠加拆分操作后的DoubleStream
|
||||
*/
|
||||
@Override
|
||||
public DoubleStream flatMapToDouble(final Function<? super T, ? extends DoubleStream> mapper) {
|
||||
return stream.flatMapToDouble(mapper);
|
||||
default DoubleStream flatMapToDouble(final Function<? super T, ? extends DoubleStream> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return unwrap().flatMapToDouble(mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,8 +143,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 一个具有去重特征的流
|
||||
*/
|
||||
@Override
|
||||
public I distinct() {
|
||||
return convertToStreamImpl(stream.distinct());
|
||||
default S distinct() {
|
||||
return wrap(unwrap().distinct());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,8 +156,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 一个元素按自然顺序排序的流
|
||||
*/
|
||||
@Override
|
||||
public I sorted() {
|
||||
return convertToStreamImpl(stream.sorted());
|
||||
default S sorted() {
|
||||
return wrap(unwrap().sorted());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,8 +170,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 一个元素按指定的Comparator排序的流
|
||||
*/
|
||||
@Override
|
||||
public I sorted(final Comparator<? super T> comparator) {
|
||||
return convertToStreamImpl(stream.sorted(comparator));
|
||||
default S sorted(final Comparator<? super T> comparator) {
|
||||
Objects.requireNonNull(comparator);
|
||||
return wrap(unwrap().sorted(comparator));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,8 +193,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* }</pre>
|
||||
*/
|
||||
@Override
|
||||
public I peek(final Consumer<? super T> action) {
|
||||
return convertToStreamImpl(stream.peek(action));
|
||||
default S peek(final Consumer<? super T> action) {
|
||||
Objects.requireNonNull(action);
|
||||
return wrap(unwrap().peek(action));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,8 +206,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 截取后的流
|
||||
*/
|
||||
@Override
|
||||
public I limit(final long maxSize) {
|
||||
return convertToStreamImpl(stream.limit(maxSize));
|
||||
default S limit(final long maxSize) {
|
||||
return wrap(unwrap().limit(maxSize));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,8 +218,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 丢弃前面n个元素后的剩余元素组成的流
|
||||
*/
|
||||
@Override
|
||||
public I skip(final long n) {
|
||||
return convertToStreamImpl(stream.skip(n));
|
||||
default S skip(final long n) {
|
||||
return wrap(unwrap().skip(n));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,8 +229,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @param action 操作
|
||||
*/
|
||||
@Override
|
||||
public void forEach(final Consumer<? super T> action) {
|
||||
stream.forEach(action);
|
||||
default void forEach(final Consumer<? super T> action) {
|
||||
Objects.requireNonNull(action);
|
||||
unwrap().forEach(action);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -213,8 +241,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @param action 操作
|
||||
*/
|
||||
@Override
|
||||
public void forEachOrdered(final Consumer<? super T> action) {
|
||||
stream.forEachOrdered(action);
|
||||
default void forEachOrdered(final Consumer<? super T> action) {
|
||||
Objects.requireNonNull(action);
|
||||
unwrap().forEachOrdered(action);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,8 +253,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 包含此流元素的数组
|
||||
*/
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return stream.toArray();
|
||||
default Object[] toArray() {
|
||||
return unwrap().toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -238,9 +267,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常
|
||||
*/
|
||||
@Override
|
||||
public <A> A[] toArray(final IntFunction<A[]> generator) {
|
||||
//noinspection SuspiciousToArrayCall
|
||||
return stream.toArray(generator);
|
||||
default <A> A[] toArray(final IntFunction<A[]> generator) {
|
||||
Objects.requireNonNull(generator);
|
||||
return unwrap().toArray(generator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -264,8 +293,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 聚合计算后的值
|
||||
*/
|
||||
@Override
|
||||
public T reduce(final T identity, final BinaryOperator<T> accumulator) {
|
||||
return stream.reduce(identity, accumulator);
|
||||
default T reduce(final T identity, final BinaryOperator<T> accumulator) {
|
||||
Objects.requireNonNull(accumulator);
|
||||
return unwrap().reduce(identity, accumulator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -274,7 +304,7 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* <pre>{@code
|
||||
* boolean foundAny = false;
|
||||
* T result = null;
|
||||
* for (T element : this stream) {
|
||||
* for (T element : this unwrap) {
|
||||
* if (!foundAny) {
|
||||
* foundAny = true;
|
||||
* result = element;
|
||||
@ -299,8 +329,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @see #max(Comparator)
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> reduce(final BinaryOperator<T> accumulator) {
|
||||
return stream.reduce(accumulator);
|
||||
default Optional<T> reduce(final BinaryOperator<T> accumulator) {
|
||||
Objects.requireNonNull(accumulator);
|
||||
return unwrap().reduce(accumulator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -316,8 +347,10 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @see #reduce(Object, BinaryOperator)
|
||||
*/
|
||||
@Override
|
||||
public <U> U reduce(final U identity, final BiFunction<U, ? super T, U> accumulator, final BinaryOperator<U> combiner) {
|
||||
return stream.reduce(identity, accumulator, combiner);
|
||||
default <U> U reduce(final U identity, final BiFunction<U, ? super T, U> accumulator, final BinaryOperator<U> combiner) {
|
||||
Objects.requireNonNull(accumulator);
|
||||
Objects.requireNonNull(combiner);
|
||||
return unwrap().reduce(identity, accumulator, combiner);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -334,8 +367,11 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* }</pre>
|
||||
*/
|
||||
@Override
|
||||
public <R> R collect(final Supplier<R> supplier, final BiConsumer<R, ? super T> accumulator, final BiConsumer<R, R> combiner) {
|
||||
return stream.collect(supplier, accumulator, combiner);
|
||||
default <R> R collect(final Supplier<R> supplier, final BiConsumer<R, ? super T> accumulator, final BiConsumer<R, R> combiner) {
|
||||
Objects.requireNonNull(supplier);
|
||||
Objects.requireNonNull(accumulator);
|
||||
Objects.requireNonNull(combiner);
|
||||
return unwrap().collect(supplier, accumulator, combiner);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -348,8 +384,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 收集后的容器
|
||||
*/
|
||||
@Override
|
||||
public <R, A> R collect(final Collector<? super T, A, R> collector) {
|
||||
return stream.collect(collector);
|
||||
default <R, A> R collect(final Collector<? super T, A, R> collector) {
|
||||
Objects.requireNonNull(collector);
|
||||
return unwrap().collect(collector);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -359,8 +396,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 最小值
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> min(final Comparator<? super T> comparator) {
|
||||
return stream.min(comparator);
|
||||
default Optional<T> min(final Comparator<? super T> comparator) {
|
||||
Objects.requireNonNull(comparator);
|
||||
return unwrap().min(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,8 +408,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 最大值
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> max(final Comparator<? super T> comparator) {
|
||||
return stream.max(comparator);
|
||||
default Optional<T> max(final Comparator<? super T> comparator) {
|
||||
Objects.requireNonNull(comparator);
|
||||
return unwrap().max(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,8 +419,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 流元素个数
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
return stream.count();
|
||||
default long count() {
|
||||
return unwrap().count();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -391,8 +430,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 是否有任何一个元素满足给定断言
|
||||
*/
|
||||
@Override
|
||||
public boolean anyMatch(final Predicate<? super T> predicate) {
|
||||
return stream.anyMatch(predicate);
|
||||
default boolean anyMatch(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return unwrap().anyMatch(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -402,8 +442,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 是否所有元素满足给定断言
|
||||
*/
|
||||
@Override
|
||||
public boolean allMatch(final Predicate<? super T> predicate) {
|
||||
return stream.allMatch(predicate);
|
||||
default boolean allMatch(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return unwrap().allMatch(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -413,8 +454,9 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 是否没有元素满足给定断言
|
||||
*/
|
||||
@Override
|
||||
public boolean noneMatch(final Predicate<? super T> predicate) {
|
||||
return stream.noneMatch(predicate);
|
||||
default boolean noneMatch(final Predicate<? super T> predicate) {
|
||||
Objects.requireNonNull(predicate);
|
||||
return unwrap().noneMatch(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -423,8 +465,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 第一个元素
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> findFirst() {
|
||||
return stream.findFirst();
|
||||
default Optional<T> findFirst() {
|
||||
return unwrap().findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -433,8 +475,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 随便取一个
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> findAny() {
|
||||
return stream.findAny();
|
||||
default Optional<T> findAny() {
|
||||
return unwrap().findAny();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -443,8 +485,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 流的迭代器
|
||||
*/
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return stream.iterator();
|
||||
default Iterator<T> iterator() {
|
||||
return unwrap().iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -453,8 +495,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 流的拆分器
|
||||
*/
|
||||
@Override
|
||||
public Spliterator<T> spliterator() {
|
||||
return stream.spliterator();
|
||||
default Spliterator<T> spliterator() {
|
||||
return unwrap().spliterator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -463,8 +505,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 流的并行状态
|
||||
*/
|
||||
@Override
|
||||
public boolean isParallel() {
|
||||
return stream.isParallel();
|
||||
default boolean isParallel() {
|
||||
return unwrap().isParallel();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -473,8 +515,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 串行流
|
||||
*/
|
||||
@Override
|
||||
public I sequential() {
|
||||
return convertToStreamImpl(stream.sequential());
|
||||
default S sequential() {
|
||||
return wrap(unwrap().sequential());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -483,8 +525,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 并行流
|
||||
*/
|
||||
@Override
|
||||
public I parallel() {
|
||||
return convertToStreamImpl(stream.parallel());
|
||||
default S parallel() {
|
||||
return wrap(unwrap().parallel());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -494,8 +536,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 无序流
|
||||
*/
|
||||
@Override
|
||||
public I unordered() {
|
||||
return convertToStreamImpl(stream.unordered());
|
||||
default S unordered() {
|
||||
return wrap(unwrap().unordered());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -505,8 +547,8 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @return 流
|
||||
*/
|
||||
@Override
|
||||
public I onClose(final Runnable closeHandler) {
|
||||
return convertToStreamImpl(stream.onClose(closeHandler));
|
||||
default S onClose(Runnable closeHandler) {
|
||||
return wrap(unwrap().onClose(closeHandler));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -515,50 +557,33 @@ abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Itera
|
||||
* @see AutoCloseable#close()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
stream.close();
|
||||
default void close() {
|
||||
unwrap().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<T> stream);
|
||||
String toString();
|
||||
|
||||
}
|
@ -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() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
final List<Integer> toList = wrap(list).toList();
|
||||
Assert.assertEquals(list, toList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToUnmodifiableList() {
|
||||
final List<Integer> list = wrap(1, 2, 3)
|
||||
.toUnmodifiableList();
|
||||
Assert.assertThrows(UnsupportedOperationException.class, () -> list.remove(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToSet() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
Set<String> toSet = wrap(list).map(String::valueOf).toSet();
|
||||
Assert.assertEquals(new HashSet<>(asList("1", "2", "3")), toSet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToUnmodifiableSet() {
|
||||
final Set<Integer> set = wrap(1, 2, 3)
|
||||
.toUnmodifiableSet();
|
||||
Assert.assertThrows(UnsupportedOperationException.class, () -> set.remove(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCollection() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
final List<String> toCollection = wrap(list).map(String::valueOf).toColl(LinkedList::new);
|
||||
Assert.assertEquals(asList("1", "2", "3"), toCollection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToMap() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
final Map<String, Integer> identityMap = wrap(list).toMap(String::valueOf);
|
||||
Assert.assertEquals(new HashMap<String, Integer>() {{
|
||||
put("1", 1);
|
||||
put("2", 2);
|
||||
put("3", 3);
|
||||
}}, identityMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToUnmodifiableMap() {
|
||||
final Map<Integer, Integer> map1 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity());
|
||||
Assert.assertThrows(UnsupportedOperationException.class, () -> map1.remove(1));
|
||||
final Map<Integer, Integer> map2 = wrap(1, 2, 3).toUnmodifiableMap(Function.identity(), Function.identity(), (t1, t2) -> t1);
|
||||
Assert.assertThrows(UnsupportedOperationException.class, () -> map2.remove(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToZip() {
|
||||
final List<Integer> orders = asList(1, 2, 3);
|
||||
final List<String> list = asList("dromara", "hutool", "sweet");
|
||||
final Map<Integer, String> toZip = wrap(orders).toZip(list);
|
||||
Assert.assertEquals(new HashMap<Integer, String>() {{
|
||||
put(1, "dromara");
|
||||
put(2, "hutool");
|
||||
put(3, "sweet");
|
||||
}}, toZip);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransform() {
|
||||
final List<Integer> list = wrap(1, 2, 3).transform(Wrapper::toList).orElse(null);
|
||||
Assert.assertEquals(asList(1, 2, 3), list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindFirst() {
|
||||
final List<Integer> 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() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
Assert.assertEquals(1, wrap(list).findFirstIdx(t -> (t & 1) == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindLast() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
Assert.assertEquals((Integer)3, wrap(list).findLast(t -> (t & 1) == 1).orElse(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindLastIdx() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
Assert.assertEquals(1, wrap(list).findLastIdx(t -> (t & 1) == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAt() {
|
||||
final List<Integer> 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() {
|
||||
final List<Integer> 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(",", "(", ")"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrouping() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
final Map<String, List<Integer>> map = new HashMap<String, List<Integer>>() {{
|
||||
put("1", singletonList(1));
|
||||
put("2", singletonList(2));
|
||||
put("3", singletonList(3));
|
||||
}};
|
||||
|
||||
Map<String, List<Integer>> 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() {
|
||||
final List<Integer> list = asList(1, 2, 3);
|
||||
final Map<Boolean, List<Integer>> map = new HashMap<Boolean, List<Integer>>() {{
|
||||
put(Boolean.TRUE, singletonList(2));
|
||||
put(Boolean.FALSE, asList(1, 3));
|
||||
}};
|
||||
|
||||
Map<Boolean, List<Integer>> partition = wrap(list).partition(t -> (t & 1) == 0, Collectors.toList());
|
||||
Assert.assertEquals(map, partition);
|
||||
partition = wrap(list).partition(t -> (t & 1) == 0);
|
||||
Assert.assertEquals(map, partition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForEachIdx() {
|
||||
final List<Integer> elements = new ArrayList<>();
|
||||
final List<Integer> 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() {
|
||||
final List<Integer> elements = new ArrayList<>();
|
||||
final List<Integer> 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() {
|
||||
final List<Integer> elements = new ArrayList<>();
|
||||
wrap(1, 2, 3).forEachOrdered(elements::add);
|
||||
Assert.assertEquals(asList(1, 2, 3), elements);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForEach() {
|
||||
final List<Integer> elements = new ArrayList<>();
|
||||
wrap(1, 2, 3).forEach(elements::add);
|
||||
Assert.assertEquals(asList(1, 2, 3), elements);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapToInt() {
|
||||
final int[] array = wrap(1, 2, 3).mapToInt(Integer::intValue).toArray();
|
||||
Assert.assertArrayEquals(new int[] {1, 2, 3}, array);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapToLong() {
|
||||
final long[] array = wrap(1L, 2L, 3L).mapToLong(Long::intValue).toArray();
|
||||
Assert.assertArrayEquals(new long[] {1L, 2L, 3L}, array);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMapToDouble() {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlatMapToInt() {
|
||||
final int[] array = wrap(1, 2, 3).flatMapToInt(IntStream::of).toArray();
|
||||
Assert.assertArrayEquals(new int[] {1, 2, 3}, array);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlatMapToLong() {
|
||||
final long[] array = wrap(1L, 2L, 3L).flatMapToLong(LongStream::of).toArray();
|
||||
Assert.assertArrayEquals(new long[] {1L, 2L, 3L}, array);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlatMapToDouble() {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSorted() {
|
||||
final List<Integer> list = wrap(3, 1, 2).sorted().toList();
|
||||
Assert.assertEquals(asList(1, 2, 3), list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeek() {
|
||||
final List<Integer> elements = new ArrayList<>();
|
||||
wrap(1, 2, 3).peek(elements::add).exec();
|
||||
Assert.assertEquals(asList(1, 2, 3), elements);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeekIdx() {
|
||||
final List<Integer> elements = new ArrayList<>();
|
||||
final List<Integer> 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);
|
||||
|
||||
final Set<Integer> elements2 = new HashSet<>();
|
||||
final Set<Integer> 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() {
|
||||
final List<Integer> list = wrap(1, 2, 3).limit(2L).toList();
|
||||
Assert.assertEquals(asList(1, 2), list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkip() {
|
||||
final List<Integer> 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() {
|
||||
final Iterator<Integer> iter1 = Stream.of(1, 2, 3).iterator();
|
||||
final Iterator<Integer> iter2 = wrap(1, 2, 3).iterator();
|
||||
while (iter1.hasNext() && iter2.hasNext()) {
|
||||
Assert.assertEquals(iter1.next(), iter2.next());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpliterator() {
|
||||
final Spliterator<Integer> iter1 = Stream.of(1, 2, 3).spliterator();
|
||||
final Spliterator<Integer> 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() {
|
||||
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() {
|
||||
final Wrapper<Integer> 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() {
|
||||
final List<Integer> 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() {
|
||||
final List<Integer> 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() {
|
||||
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());
|
||||
}
|
||||
|
||||
@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() {
|
||||
final List<Integer> 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() {
|
||||
final Stream<Integer> stream = Stream.of(1, 2, 3);
|
||||
Assert.assertEquals(stream.hashCode(), wrap(stream).hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
final Stream<Integer> stream = Stream.of(1, 2, 3);
|
||||
Assert.assertEquals(wrap(stream), stream);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() {
|
||||
final Stream<Integer> stream = Stream.of(1, 2, 3);
|
||||
Assert.assertEquals(stream.toString(), wrap(stream).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToEntries() {
|
||||
final Map<Integer, Integer> expect = new HashMap<Integer, Integer>(){{
|
||||
put(1, 1);
|
||||
put(2, 2);
|
||||
put(3, 3);
|
||||
}};
|
||||
Map<Integer, Integer> 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<Integer> orders = Arrays.asList(1, 2, 3);
|
||||
final List<String> list = Arrays.asList("dromara", "hutool", "sweet");
|
||||
List<String> zip = wrap(orders).zip(list, (e1, e2) -> e1 + "." + e2).toList();
|
||||
Assert.assertEquals(Arrays.asList("1.dromara", "2.hutool", "3.sweet"), zip);
|
||||
|
||||
zip = wrap((Stream<? extends Object>)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<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
|
||||
List<List<Integer>> 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<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
|
||||
List<List<Integer>> 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 <T> Wrapper<T> wrap(T... array) {
|
||||
return new Wrapper<>(Stream.of(array));
|
||||
}
|
||||
|
||||
private static <T> Wrapper<T> wrap(Iterable<T> iterable) {
|
||||
return new Wrapper<>(StreamSupport.stream(iterable.spliterator(), false));
|
||||
}
|
||||
|
||||
private static <T> Wrapper<T> wrap(Stream<T> stream) {
|
||||
return new Wrapper<>(stream);
|
||||
}
|
||||
|
||||
private static class Wrapper<T> extends AbstractEnhancedWrappedStream<T, Wrapper<T>> {
|
||||
|
||||
/**
|
||||
* 创建一个流包装器
|
||||
*
|
||||
* @param stream 包装的流对象
|
||||
* @throws NullPointerException 当{@code unwrap}为{@code null}时抛出
|
||||
*/
|
||||
protected Wrapper(Stream<T> stream) {
|
||||
super(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wrapper<T> wrap(Stream<T> source) {
|
||||
return new Wrapper<>(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
private static class Tree {
|
||||
private final Integer id;
|
||||
private List<Tree> children;
|
||||
}
|
||||
|
||||
}
|
@ -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() {
|
||||
final Stream<Integer> stream1 = Stream.of(1, 2);
|
||||
final Stream<Integer> stream2 = Stream.of(3, 4);
|
||||
Assert.assertEquals(4, EasyStream.concat(stream1, stream2).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilder() {
|
||||
final List<Integer> list = EasyStream.<Integer>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<Integer> list = Arrays.asList(1, 2, 3);
|
||||
final List<String> 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<Integer> collect1 = list.stream().distinct().collect(Collectors.toList());
|
||||
final List<Integer> collect2 = list.stream().parallel().distinct().collect(Collectors.toList());
|
||||
|
||||
// 使用FastStream去重
|
||||
// 使用EasyStream去重
|
||||
final List<Integer> distinctBy1 = EasyStream.of(list).distinct().toList();
|
||||
final List<Integer> 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()).sequential().count()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForeachIdx() {
|
||||
final List<String> list = Arrays.asList("dromara", "hutool", "sweet");
|
||||
final EasyStream.FastStreamBuilder<String> builder = EasyStream.builder();
|
||||
final EasyStream.Builder<String> 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<String> list = Arrays.asList("dromara", "hutool", "sweet");
|
||||
final EasyStream.FastStreamBuilder<String> builder = EasyStream.builder();
|
||||
final EasyStream.Builder<String> 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<String> streamBuilder = EasyStream.builder();
|
||||
final EasyStream.Builder<String> 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<Integer> orders = Arrays.asList(1, 2, 3);
|
||||
final List<String> list = Arrays.asList("dromara", "hutool", "sweet");
|
||||
List<String> 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<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
|
||||
List<List<Integer>> 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<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
|
||||
List<List<Integer>> 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;
|
||||
|
@ -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;
|
||||
|
||||
@ -30,22 +31,39 @@ 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
|
||||
public void testOf() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
map.put("1", "1");
|
||||
Assert.assertEquals(1, EntryStream.of(map).count());
|
||||
Assert.assertEquals(0, EntryStream.of((Map<String, String>)null).count());
|
||||
|
||||
Set<Map.Entry<Integer, Integer>> entries = new HashSet<>();
|
||||
final Set<Map.Entry<Integer, Integer>> 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<Map.Entry<Integer, Integer>>)null).count());
|
||||
Assert.assertEquals(2, EntryStream.of(entries.stream()).count());
|
||||
Assert.assertEquals(0, EntryStream.of((Stream<Map.Entry<Integer, Integer>>)null).count());
|
||||
Assert.assertEquals(2, new EntryStream<>(entries.stream()).count());
|
||||
Assert.assertThrows(NullPointerException.class, () -> new EntryStream<>(null));
|
||||
|
||||
Iterable<Integer> iterable = Arrays.asList(1, 2, null);
|
||||
final Iterable<Integer> 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
|
||||
@ -55,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);
|
||||
@ -63,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);
|
||||
@ -71,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);
|
||||
@ -79,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);
|
||||
@ -87,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);
|
||||
@ -95,7 +113,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testPeekKey() {
|
||||
List<Integer> keys = new ArrayList<>();
|
||||
final List<Integer> 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();
|
||||
@ -104,7 +122,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testPeekValue() {
|
||||
List<Integer> values = new ArrayList<>();
|
||||
final List<Integer> 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();
|
||||
@ -120,12 +138,66 @@ public class EntryStreamTest {
|
||||
.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() {
|
||||
final Map<Integer, Integer> map1 = new HashMap<Integer, Integer>(){{
|
||||
put(1, 1);
|
||||
put(2, 2);
|
||||
}};
|
||||
final Map<Integer, Integer> map2 = new HashMap<Integer, Integer>(){{
|
||||
put(3, 3);
|
||||
put(4, 4);
|
||||
}};
|
||||
Assert.assertEquals(
|
||||
new ArrayList<Map.Entry<Integer, Integer>>(){{
|
||||
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() {
|
||||
final Map<Integer, Integer> map1 = new HashMap<Integer, Integer>(){{
|
||||
put(1, 1);
|
||||
put(2, 2);
|
||||
}};
|
||||
final Map<Integer, Integer> map2 = new HashMap<Integer, Integer>(){{
|
||||
put(3, 3);
|
||||
put(4, 4);
|
||||
}};
|
||||
Assert.assertEquals(
|
||||
new ArrayList<Map.Entry<Integer, Integer>>(){{
|
||||
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
|
||||
public void testSortByKey() {
|
||||
List<Map.Entry<Integer, Integer>> entries = EntryStream.of(Arrays.asList(new Entry<>(3, 1), new Entry<>(2, 1), new Entry<>(4, 1), new Entry<>(1, 1)))
|
||||
final List<Map.Entry<Integer, Integer>> 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(
|
||||
@ -136,7 +208,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testSortByValue() {
|
||||
List<Map.Entry<Integer, Integer>> entries = EntryStream.of(Arrays.asList(new Entry<>(4, 4), new Entry<>(2, 2), new Entry<>(1, 1), new Entry<>(3, 3)))
|
||||
final List<Map.Entry<Integer, Integer>> 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(
|
||||
@ -147,7 +219,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testToValueStream() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -158,7 +230,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testToKeyStream() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -169,7 +241,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testCollectKey() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -179,7 +251,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testCollectValue() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -189,7 +261,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testMapKeys() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -204,7 +276,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testMapValues() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -219,7 +291,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testMap() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -229,11 +301,17 @@ 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
|
||||
public void testFlatMap() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -245,11 +323,11 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testFlatMapValue() {
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
final Map<String, Integer> map = new HashMap<>();
|
||||
map.put("class1", 1);
|
||||
map.put("class2", 2);
|
||||
map.put("class3", 3);
|
||||
List<String> values = EntryStream.of(map)
|
||||
final List<String> values = EntryStream.of(map)
|
||||
.flatMapKey(k -> Stream.of(k + "'s student1", k + "'s student2"))
|
||||
.map((k, v) -> v + "=" + k)
|
||||
.sorted()
|
||||
@ -266,11 +344,11 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testInverse() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
map.put("key1", "value1");
|
||||
map.put("key2", "value2");
|
||||
map.put("key3", "value3");
|
||||
List<String> results = EntryStream.of(map)
|
||||
final List<String> results = EntryStream.of(map)
|
||||
.inverse()
|
||||
.map((k, v) -> k + "=" + v)
|
||||
.collect(Collectors.toList());
|
||||
@ -282,11 +360,11 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testFlatMapKey() {
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
final Map<Integer, String> map = new HashMap<>();
|
||||
map.put(1, "class1");
|
||||
map.put(2, "class2");
|
||||
map.put(3, "class3");
|
||||
List<String> values = EntryStream.of(map)
|
||||
final List<String> values = EntryStream.of(map)
|
||||
.flatMapValue(v -> Stream.of(v + "'s student1", v + "'s student2"))
|
||||
.map((k, v) -> k + "=" + v)
|
||||
.collect(Collectors.toList());
|
||||
@ -302,13 +380,13 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testForEach() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
|
||||
List<Integer> keys = new ArrayList<>();
|
||||
List<Integer> values = new ArrayList<>();
|
||||
final List<Integer> keys = new ArrayList<>();
|
||||
final List<Integer> values = new ArrayList<>();
|
||||
EntryStream.of(map).forEach((k ,v) -> {
|
||||
keys.add(k);
|
||||
values.add(v);
|
||||
@ -319,7 +397,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testToMap() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -327,7 +405,7 @@ public class EntryStreamTest {
|
||||
Map<Integer, Integer> result = EntryStream.of(map).toMap();
|
||||
Assert.assertEquals(map, result);
|
||||
|
||||
result = EntryStream.of(map).toMap(LinkedHashMap::new);
|
||||
result = EntryStream.of(map).toMap((Supplier<Map<Integer, Integer>>)LinkedHashMap::new);
|
||||
Assert.assertEquals(new LinkedHashMap<>(map), result);
|
||||
|
||||
result = EntryStream.of(map).toMap(LinkedHashMap::new, (t1, t2) -> t1);
|
||||
@ -336,7 +414,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testToTable() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -360,7 +438,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testToTableByKey() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -384,7 +462,7 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testToTableByValue() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(1, 1);
|
||||
map.put(2, 2);
|
||||
map.put(3, 3);
|
||||
@ -408,19 +486,19 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testGroupByKey() {
|
||||
Map<Integer, List<Integer>> map1 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity())
|
||||
final Map<Integer, List<Integer>> 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<Integer, Set<Integer>> map2 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity())
|
||||
final Map<Integer, Set<Integer>> 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<Integer, Set<Integer>> map3 = EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity())
|
||||
final Map<Integer, Set<Integer>> 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));
|
||||
@ -453,26 +531,26 @@ public class EntryStreamTest {
|
||||
|
||||
@Test
|
||||
public void testNonNull() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> 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
|
||||
public void testKeyNonNull() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> 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
|
||||
public void testValueNonNull() {
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
final Map<Integer, Integer> 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<K, V> implements Map.Entry<K, V> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user