mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
!781 【6.x】新增基于键值对类型元素的Stream增强流实现
Merge pull request !781 from Createsequence/feat-stream-ext
This commit is contained in:
@@ -4,15 +4,7 @@ import cn.hutool.core.lang.Opt;
|
|||||||
import cn.hutool.core.text.StrUtil;
|
import cn.hutool.core.text.StrUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.BinaryOperator;
|
import java.util.function.BinaryOperator;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -32,7 +24,7 @@ public class CollectorUtil {
|
|||||||
* 说明已包含IDENTITY_FINISH特征 为 Characteristics.IDENTITY_FINISH 的缩写
|
* 说明已包含IDENTITY_FINISH特征 为 Characteristics.IDENTITY_FINISH 的缩写
|
||||||
*/
|
*/
|
||||||
public static final Set<Collector.Characteristics> CH_ID
|
public static final Set<Collector.Characteristics> CH_ID
|
||||||
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
|
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
|
||||||
/**
|
/**
|
||||||
* 说明不包含IDENTITY_FINISH特征
|
* 说明不包含IDENTITY_FINISH特征
|
||||||
*/
|
*/
|
||||||
@@ -58,7 +50,7 @@ public class CollectorUtil {
|
|||||||
* @return {@link Collector}
|
* @return {@link Collector}
|
||||||
*/
|
*/
|
||||||
public static <T> Collector<T, ?, String> joining(final CharSequence delimiter,
|
public static <T> Collector<T, ?, String> joining(final CharSequence delimiter,
|
||||||
final Function<T, ? extends CharSequence> toStringFunc) {
|
final Function<T, ? extends CharSequence> toStringFunc) {
|
||||||
return joining(delimiter, StrUtil.EMPTY, StrUtil.EMPTY, toStringFunc);
|
return joining(delimiter, StrUtil.EMPTY, StrUtil.EMPTY, toStringFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,15 +65,15 @@ public class CollectorUtil {
|
|||||||
* @return {@link Collector}
|
* @return {@link Collector}
|
||||||
*/
|
*/
|
||||||
public static <T> Collector<T, ?, String> joining(final CharSequence delimiter,
|
public static <T> Collector<T, ?, String> joining(final CharSequence delimiter,
|
||||||
final CharSequence prefix,
|
final CharSequence prefix,
|
||||||
final CharSequence suffix,
|
final CharSequence suffix,
|
||||||
final Function<T, ? extends CharSequence> toStringFunc) {
|
final Function<T, ? extends CharSequence> toStringFunc) {
|
||||||
return new SimpleCollector<>(
|
return new SimpleCollector<>(
|
||||||
() -> new StringJoiner(delimiter, prefix, suffix),
|
() -> new StringJoiner(delimiter, prefix, suffix),
|
||||||
(joiner, ele) -> joiner.add(toStringFunc.apply(ele)),
|
(joiner, ele) -> joiner.add(toStringFunc.apply(ele)),
|
||||||
StringJoiner::merge,
|
StringJoiner::merge,
|
||||||
StringJoiner::toString,
|
StringJoiner::toString,
|
||||||
Collections.emptySet()
|
Collections.emptySet()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,8 +92,8 @@ public class CollectorUtil {
|
|||||||
* @return {@link Collector}
|
* @return {@link Collector}
|
||||||
*/
|
*/
|
||||||
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(final Function<? super T, ? extends K> classifier,
|
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(final Function<? super T, ? extends K> classifier,
|
||||||
final Supplier<M> mapFactory,
|
final Supplier<M> mapFactory,
|
||||||
final Collector<? super T, A, D> downstream) {
|
final Collector<? super T, A, D> downstream) {
|
||||||
final Supplier<A> downstreamSupplier = downstream.supplier();
|
final Supplier<A> downstreamSupplier = downstream.supplier();
|
||||||
final BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
|
final BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
|
||||||
final BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
|
final BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
|
||||||
@@ -141,7 +133,7 @@ public class CollectorUtil {
|
|||||||
*/
|
*/
|
||||||
public static <T, K, A, D>
|
public static <T, K, A, D>
|
||||||
Collector<T, ?, Map<K, D>> groupingBy(final Function<? super T, ? extends K> classifier,
|
Collector<T, ?, Map<K, D>> groupingBy(final Function<? super T, ? extends K> classifier,
|
||||||
final Collector<? super T, A, D> downstream) {
|
final Collector<? super T, A, D> downstream) {
|
||||||
return groupingBy(classifier, HashMap::new, downstream);
|
return groupingBy(classifier, HashMap::new, downstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,8 +163,8 @@ public class CollectorUtil {
|
|||||||
*/
|
*/
|
||||||
public static <T, K, U>
|
public static <T, K, U>
|
||||||
Collector<T, ?, Map<K, U>> toMap(final Function<? super T, ? extends K> keyMapper,
|
Collector<T, ?, Map<K, U>> toMap(final Function<? super T, ? extends K> keyMapper,
|
||||||
final Function<? super T, ? extends U> valueMapper,
|
final Function<? super T, ? extends U> valueMapper,
|
||||||
final BinaryOperator<U> mergeFunction) {
|
final BinaryOperator<U> mergeFunction) {
|
||||||
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
|
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,11 +183,11 @@ public class CollectorUtil {
|
|||||||
*/
|
*/
|
||||||
public static <T, K, U, M extends Map<K, U>>
|
public static <T, K, U, M extends Map<K, U>>
|
||||||
Collector<T, ?, M> toMap(final Function<? super T, ? extends K> keyMapper,
|
Collector<T, ?, M> toMap(final Function<? super T, ? extends K> keyMapper,
|
||||||
final Function<? super T, ? extends U> valueMapper,
|
final Function<? super T, ? extends U> valueMapper,
|
||||||
final BinaryOperator<U> mergeFunction,
|
final BinaryOperator<U> mergeFunction,
|
||||||
final Supplier<M> mapSupplier) {
|
final Supplier<M> mapSupplier) {
|
||||||
final BiConsumer<M, T> accumulator
|
final BiConsumer<M, T> accumulator
|
||||||
= (map, element) -> map.put(Opt.ofNullable(element).map(keyMapper).get(), Opt.ofNullable(element).map(valueMapper).get());
|
= (map, element) -> map.put(Opt.ofNullable(element).map(keyMapper).get(), Opt.ofNullable(element).map(valueMapper).get());
|
||||||
return new SimpleCollector<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
|
return new SimpleCollector<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,15 +233,84 @@ public class CollectorUtil {
|
|||||||
*/
|
*/
|
||||||
public static <K, V, R extends Map<K, List<V>>> Collector<Map<K, V>, ?, R> reduceListMap(final Supplier<R> mapSupplier) {
|
public static <K, V, R extends Map<K, List<V>>> Collector<Map<K, V>, ?, R> reduceListMap(final Supplier<R> mapSupplier) {
|
||||||
return Collectors.reducing(mapSupplier.get(), value -> {
|
return Collectors.reducing(mapSupplier.get(), value -> {
|
||||||
R result = mapSupplier.get();
|
R result = mapSupplier.get();
|
||||||
value.forEach((k, v) -> result.computeIfAbsent(k, i -> new ArrayList<>()).add(v));
|
value.forEach((k, v) -> result.computeIfAbsent(k, i -> new ArrayList<>()).add(v));
|
||||||
return result;
|
return result;
|
||||||
}, (l, r) -> {
|
}, (l, r) -> {
|
||||||
r.forEach((k, v) -> l.computeIfAbsent(k, i -> new ArrayList<>()).addAll(v));
|
r.forEach((k, v) -> l.computeIfAbsent(k, i -> new ArrayList<>()).addAll(v));
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将流转为{@link EntryStream}
|
||||||
|
*
|
||||||
|
* @param keyMapper 键的映射方法
|
||||||
|
* @param valueMapper 值的映射方法
|
||||||
|
* @param <T> 输入元素类型
|
||||||
|
* @param <K> 元素的键类型
|
||||||
|
* @param <V> 元素的值类型
|
||||||
|
* @return 收集器
|
||||||
|
*/
|
||||||
|
public static <T, K, V> Collector<T, List<T>, EntryStream<K, V>> toEntryStream(
|
||||||
|
Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
|
||||||
|
Objects.requireNonNull(keyMapper);
|
||||||
|
Objects.requireNonNull(valueMapper);
|
||||||
|
return transform(ArrayList::new, list -> EntryStream.of(list, keyMapper, valueMapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将流转为{@link EasyStream}
|
||||||
|
*
|
||||||
|
* @param <T> 输入元素类型
|
||||||
|
* @return 收集器
|
||||||
|
*/
|
||||||
|
public static <T> Collector<T, ?, EasyStream<T>> toEasyStream() {
|
||||||
|
return transform(ArrayList::new, EasyStream::of);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集元素,将其转为指定{@link Collection}集合后,再对该集合进行转换,并最终返回转换后的结果。
|
||||||
|
* 返回的收集器的效果等同于:
|
||||||
|
* <pre>{@code
|
||||||
|
* Collection<T> coll = Stream.of(a, b, c, d)
|
||||||
|
* .collect(Collectors.toCollection(collFactory));
|
||||||
|
* R result = mapper.apply(coll);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param collFactory 中间收集输入元素的集合的创建方法
|
||||||
|
* @param mapper 最终将元素集合映射为返回值的方法
|
||||||
|
* @param <R> 返回值类型
|
||||||
|
* @param <T> 输入元素类型
|
||||||
|
* @param <C> 中间收集输入元素的集合类型
|
||||||
|
* @return 收集器
|
||||||
|
*/
|
||||||
|
public static <T, R, C extends Collection<T>> Collector<T, C, R> transform(
|
||||||
|
Supplier<C> collFactory, Function<C, R> mapper) {
|
||||||
|
Objects.requireNonNull(collFactory);
|
||||||
|
Objects.requireNonNull(mapper);
|
||||||
|
return new SimpleCollector<>(
|
||||||
|
collFactory, C::add, (l1, l2) -> { l1.addAll(l2); return l1; }, mapper, CH_NOID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集元素,将其转为{@link ArrayList}集合后,再对该集合进行转换,并最终返回转换后的结果。
|
||||||
|
* 返回的收集器的效果等同于:
|
||||||
|
* <pre>{@code
|
||||||
|
* List<T> coll = Stream.of(a, b, c, d)
|
||||||
|
* .collect(Collectors.toList());
|
||||||
|
* R result = mapper.apply(coll);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param mapper 最终将元素集合映射为返回值的方法
|
||||||
|
* @param <R> 返回值类型
|
||||||
|
* @param <T> 输入元素类型
|
||||||
|
* @return 收集器
|
||||||
|
*/
|
||||||
|
public static <T, R> Collector<T, List<T>, R> transform(Function<List<T>, R> mapper) {
|
||||||
|
return transform(ArrayList::new, mapper);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,41 +9,15 @@ import cn.hutool.core.lang.mutable.MutableObj;
|
|||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.text.StrUtil;
|
import cn.hutool.core.text.StrUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Spliterator;
|
|
||||||
import java.util.Spliterators;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.*;
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.function.BiPredicate;
|
|
||||||
import java.util.function.BinaryOperator;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.IntFunction;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.function.ToDoubleFunction;
|
|
||||||
import java.util.function.ToIntFunction;
|
|
||||||
import java.util.function.ToLongFunction;
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
import java.util.stream.Collector;
|
import java.util.stream.Collector;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.DoubleStream;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import java.util.stream.LongStream;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
@@ -82,21 +56,19 @@ import java.util.stream.StreamSupport;
|
|||||||
* @see java.util.stream.Stream
|
* @see java.util.stream.Stream
|
||||||
* @since 6.0.0
|
* @since 6.0.0
|
||||||
*/
|
*/
|
||||||
public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
public class EasyStream<T> extends StreamWrapper<T, EasyStream<T>> implements Stream<T>, Iterable<T> {
|
||||||
/**
|
/**
|
||||||
* 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标
|
* 代表不存在的下标, 一般用于并行流的下标, 或者未找到元素时的下标
|
||||||
*/
|
*/
|
||||||
private static final int NOT_FOUND_INDEX = -1;
|
private static final int NOT_FOUND_INDEX = -1;
|
||||||
|
|
||||||
protected final Stream<T> stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
* @param stream {@link Stream}
|
* @param stream {@link Stream}
|
||||||
*/
|
*/
|
||||||
EasyStream(final Stream<T> stream) {
|
EasyStream(final Stream<T> stream) {
|
||||||
this.stream = null == stream ? Stream.empty() : stream;
|
super(ObjUtil.isNull(stream) ? Stream.empty() : stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Static method
|
// region Static method
|
||||||
@@ -280,18 +252,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
// --------------------------------------------------------------- Static method end
|
// --------------------------------------------------------------- Static method end
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
/**
|
|
||||||
* 过滤元素,返回与指定断言匹配的元素组成的流
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param predicate 断言
|
|
||||||
* @return 返回叠加过滤操作后的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> filter(final Predicate<? super T> predicate) {
|
|
||||||
return new EasyStream<>(stream.filter(predicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 过滤元素,返回与 指定操作结果 匹配 指定值 的元素组成的流
|
* 过滤元素,返回与 指定操作结果 匹配 指定值 的元素组成的流
|
||||||
* 这是一个无状态中间操作
|
* 这是一个无状态中间操作
|
||||||
@@ -414,42 +374,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 和{@link EasyStream#map(Function)}一样,只不过函数的返回值必须为int类型
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param mapper 返回值为int类型的函数
|
|
||||||
* @return 叠加操作后元素类型全为int的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public IntStream mapToInt(final ToIntFunction<? super T> mapper) {
|
|
||||||
return stream.mapToInt(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 和{@link EasyStream#map(Function)}一样,只不过函数的返回值必须为long类型
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param mapper 返回值为long类型的函数
|
|
||||||
* @return 叠加操作后元素类型全为long的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public LongStream mapToLong(final ToLongFunction<? super T> mapper) {
|
|
||||||
return stream.mapToLong(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 和{@link EasyStream#map(Function)}一样,只不过函数的返回值必须为double类型
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param mapper 返回值为double类型的函数
|
|
||||||
* @return 叠加操作后元素类型全为double的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public DoubleStream mapToDouble(final ToDoubleFunction<? super T> mapper) {
|
|
||||||
return stream.mapToDouble(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素,
|
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作, 转换为迭代器元素,
|
||||||
* 最后返回所有迭代器的所有元素组成的流<br>
|
* 最后返回所有迭代器的所有元素组成的流<br>
|
||||||
@@ -483,42 +407,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
return nonNull().flat(mapper).nonNull();
|
return nonNull().flat(mapper).nonNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param mapper 操作,返回IntStream
|
|
||||||
* @return 返回叠加拆分操作后的IntStream
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public IntStream flatMapToInt(final Function<? super T, ? extends IntStream> mapper) {
|
|
||||||
return stream.flatMapToInt(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param mapper 操作,返回LongStream
|
|
||||||
* @return 返回叠加拆分操作后的LongStream
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public LongStream flatMapToLong(final Function<? super T, ? extends LongStream> mapper) {
|
|
||||||
return stream.flatMapToLong(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param mapper 操作,返回DoubleStream
|
|
||||||
* @return 返回叠加拆分操作后的DoubleStream
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public DoubleStream flatMapToDouble(final Function<? super T, ? extends DoubleStream> mapper) {
|
|
||||||
return stream.flatMapToDouble(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带一个方法,调用该方法可增加元素
|
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流,操作带一个方法,调用该方法可增加元素
|
||||||
* 这是一个无状态中间操作
|
* 这是一个无状态中间操作
|
||||||
@@ -536,17 +424,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个
|
|
||||||
* 这是一个有状态中间操作
|
|
||||||
*
|
|
||||||
* @return 一个具有去重特征的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> distinct() {
|
|
||||||
return new EasyStream<>(stream.distinct());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个
|
* 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个
|
||||||
* 这是一个有状态中间操作
|
* 这是一个有状态中间操作
|
||||||
@@ -582,55 +459,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个元素按自然顺序排序的流
|
|
||||||
* 如果此流的元素不是{@code Comparable} ,则在执行终端操作时可能会抛出 {@code java.lang.ClassCastException}
|
|
||||||
* 对于顺序流,排序是稳定的。 对于无序流,没有稳定性保证。
|
|
||||||
* 这是一个有状态中间操作
|
|
||||||
*
|
|
||||||
* @return 一个元素按自然顺序排序的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> sorted() {
|
|
||||||
return new EasyStream<>(stream.sorted());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个元素按指定的{@link Comparator}排序的流
|
|
||||||
* 如果此流的元素不是{@code Comparable} ,则在执行终端操作时可能会抛出{@code java.lang.ClassCastException}
|
|
||||||
* 对于顺序流,排序是稳定的。 对于无序流,没有稳定性保证。
|
|
||||||
* 这是一个有状态中间操作
|
|
||||||
*
|
|
||||||
* @param comparator 排序规则
|
|
||||||
* @return 一个元素按指定的Comparator排序的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> sorted(final Comparator<? super T> comparator) {
|
|
||||||
return new EasyStream<>(stream.sorted(comparator));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回与指定函数将元素作为参数执行后组成的流。
|
|
||||||
* 这是一个无状态中间操作
|
|
||||||
*
|
|
||||||
* @param action 指定的函数
|
|
||||||
* @return 返回叠加操作后的FastStream
|
|
||||||
* @apiNote 该方法存在的意义主要是用来调试
|
|
||||||
* 当你需要查看经过操作管道某处的元素,可以执行以下操作:
|
|
||||||
* <pre>{@code
|
|
||||||
* .of("one", "two", "three", "four")
|
|
||||||
* .filter(e -> e.length() > 3)
|
|
||||||
* .peek(e -> System.out.println("Filtered value: " + e))
|
|
||||||
* .map(String::toUpperCase)
|
|
||||||
* .peek(e -> System.out.println("Mapped value: " + e))
|
|
||||||
* .collect(Collectors.toList());
|
|
||||||
* }</pre>
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> peek(final Consumer<? super T> action) {
|
|
||||||
return new EasyStream<>(stream.peek(action));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1
|
* 返回与指定函数将元素作为参数执行后组成的流。操作带下标,并行流时下标永远为-1
|
||||||
* 这是一个无状态中间操作
|
* 这是一个无状态中间操作
|
||||||
@@ -656,6 +484,7 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
return peek(e -> action.accept(e, index.incrementAndGet()));
|
return peek(e -> action.accept(e, index.incrementAndGet()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回叠加调用{@link Console#log(Object)}打印出结果的流
|
* 返回叠加调用{@link Console#log(Object)}打印出结果的流
|
||||||
*
|
*
|
||||||
@@ -665,53 +494,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
return peek(Console::log);
|
return peek(Console::log);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回截取后面一些元素的流
|
|
||||||
* 这是一个短路状态中间操作
|
|
||||||
*
|
|
||||||
* @param maxSize 元素截取后的个数
|
|
||||||
* @return 截取后的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> limit(final long maxSize) {
|
|
||||||
return new EasyStream<>(stream.limit(maxSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回丢弃前面n个元素后的剩余元素组成的流,如果当前元素个数小于n,则返回一个元素为空的流
|
|
||||||
* 这是一个有状态中间操作
|
|
||||||
*
|
|
||||||
* @param n 需要丢弃的元素个数
|
|
||||||
* @return 丢弃前面n个元素后的剩余元素组成的流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> skip(final long n) {
|
|
||||||
return new EasyStream<>(stream.skip(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个串行流,该方法可以将并行流转换为串行流
|
|
||||||
*
|
|
||||||
* @return 串行流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> sequential() {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
stream.sequential();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对流里面的每一个元素执行一个操作
|
|
||||||
* 这是一个终端操作
|
|
||||||
*
|
|
||||||
* @param action 操作
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void forEach(final Consumer<? super T> action) {
|
|
||||||
stream.forEach(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1
|
* 对流里面的每一个元素执行一个操作,操作带下标,并行流时下标永远为-1
|
||||||
* 这是一个终端操作
|
* 这是一个终端操作
|
||||||
@@ -728,17 +510,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 对流里面的每一个元素按照顺序执行一个操作
|
|
||||||
* 这是一个终端操作
|
|
||||||
*
|
|
||||||
* @param action 操作
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void forEachOrdered(final Consumer<? super T> action) {
|
|
||||||
stream.forEachOrdered(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1
|
* 对流里面的每一个元素按照顺序执行一个操作,操作带下标,并行流时下标永远为-1
|
||||||
* 这是一个终端操作
|
* 这是一个终端操作
|
||||||
@@ -755,216 +526,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个包含此流元素的数组
|
|
||||||
* 这是一个终端操作
|
|
||||||
*
|
|
||||||
* @return 包含此流元素的数组
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object[] toArray() {
|
|
||||||
return stream.toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个包含此流元素的指定的数组,例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException}
|
|
||||||
* <pre>{@code String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new); }</pre>
|
|
||||||
*
|
|
||||||
* @param generator 这里的IntFunction的参数是元素的个数,返回值为数组类型
|
|
||||||
* @param <A> 给定的数组类型
|
|
||||||
* @return 包含此流元素的指定的数组
|
|
||||||
* @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <A> A[] toArray(final IntFunction<A[]> generator) {
|
|
||||||
//noinspection SuspiciousToArrayCall
|
|
||||||
return stream.toArray(generator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对元素进行聚合,并返回聚合后的值,相当于在for循环里写sum=sum+ints[i]
|
|
||||||
* 这是一个终端操作<br>
|
|
||||||
* 求和、最小值、最大值、平均值和转换成一个String字符串均为聚合操作
|
|
||||||
* 例如这里对int进行求和可以写成:
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* Integer sum = integers.reduce(0, (a, b) -> a+b);
|
|
||||||
* }</pre>
|
|
||||||
* <p>
|
|
||||||
* 或者写成:
|
|
||||||
*
|
|
||||||
* <pre>{@code
|
|
||||||
* Integer sum = integers.reduce(0, Integer::sum);
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* @param identity 初始值,还用于限定泛型
|
|
||||||
* @param accumulator 你想要的聚合操作
|
|
||||||
* @return 聚合计算后的值
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public T reduce(final T identity, final BinaryOperator<T> accumulator) {
|
|
||||||
return stream.reduce(identity, accumulator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对元素进行聚合,并返回聚合后用 {@link Optional}包裹的值,相当于在for循环里写sum=sum+ints[i]
|
|
||||||
* 该操作相当于:
|
|
||||||
* <pre>{@code
|
|
||||||
* boolean foundAny = false;
|
|
||||||
* T result = null;
|
|
||||||
* for (T element : this stream) {
|
|
||||||
* if (!foundAny) {
|
|
||||||
* foundAny = true;
|
|
||||||
* result = element;
|
|
||||||
* }
|
|
||||||
* else
|
|
||||||
* result = accumulator.apply(result, element);
|
|
||||||
* }
|
|
||||||
* return foundAny ? Optional.of(result) : Optional.empty();
|
|
||||||
* }</pre>
|
|
||||||
* 但它不局限于顺序执行,例如并行流等情况下
|
|
||||||
* 这是一个终端操作<br>
|
|
||||||
* 例如以下场景抛出 NPE :
|
|
||||||
* <pre>{@code
|
|
||||||
* Optional<Integer> reduce = Stream.<Integer>builder().add(1).add(1).build().reduce((a, b) -> null);
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* @param accumulator 你想要的聚合操作
|
|
||||||
* @return 聚合后用 {@link Optional}包裹的值
|
|
||||||
* @throws NullPointerException 如果给定的聚合操作中执行后结果为空,并用于下一次执行,则抛出该异常
|
|
||||||
* @see #reduce(Object, BinaryOperator)
|
|
||||||
* @see #min(Comparator)
|
|
||||||
* @see #max(Comparator)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Optional<T> reduce(final BinaryOperator<T> accumulator) {
|
|
||||||
return stream.reduce(accumulator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对元素进行聚合,并返回聚合后的值,并行流时聚合拿到的初始值不稳定
|
|
||||||
* 这是一个终端操作
|
|
||||||
*
|
|
||||||
* @param identity 初始值
|
|
||||||
* @param accumulator 累加器,具体为你要的聚合操作
|
|
||||||
* @param combiner 用于并行流时组合多个结果
|
|
||||||
* @param <U> 初始值
|
|
||||||
* @return 聚合操作的结果
|
|
||||||
* @see #reduce(BinaryOperator)
|
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对元素进行收集,并返回收集后的容器
|
|
||||||
* 这是一个终端操作
|
|
||||||
*
|
|
||||||
* @param supplier 提供初始值的函数式接口,一般可以传入构造参数
|
|
||||||
* @param accumulator 具体收集操作
|
|
||||||
* @param combiner 用于并行流时组合多个结果
|
|
||||||
* @param <R> 用于收集元素的容器,大多是集合
|
|
||||||
* @return 收集后的容器
|
|
||||||
* <pre>{@code
|
|
||||||
* List<Integer> collect = Stream.iterate(1, i -> ++i).limit(10).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
|
||||||
* }</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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 对元素进行收集,并返回收集后的元素
|
|
||||||
* 这是一个终端操作
|
|
||||||
*
|
|
||||||
* @param collector 收集器
|
|
||||||
* @param <R> 容器类型
|
|
||||||
* @param <A> 具体操作时容器类型,例如 {@code List::add} 时它为 {@code List}
|
|
||||||
* @return 收集后的容器
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public <R, A> R collect(final Collector<? super T, A, R> collector) {
|
|
||||||
return stream.collect(collector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取最小值
|
|
||||||
*
|
|
||||||
* @param comparator 一个用来比较大小的比较器{@link Comparator}
|
|
||||||
* @return 最小值
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Optional<T> min(final Comparator<? super T> comparator) {
|
|
||||||
return stream.min(comparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取最大值
|
|
||||||
*
|
|
||||||
* @param comparator 一个用来比较大小的比较器{@link Comparator}
|
|
||||||
* @return 最大值
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Optional<T> max(final Comparator<? super T> comparator) {
|
|
||||||
return stream.max(comparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回流元素个数
|
|
||||||
*
|
|
||||||
* @return 流元素个数
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public long count() {
|
|
||||||
return stream.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否有任何一个元素满足给定断言
|
|
||||||
*
|
|
||||||
* @param predicate 断言
|
|
||||||
* @return 是否有任何一个元素满足给定断言
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean anyMatch(final Predicate<? super T> predicate) {
|
|
||||||
return stream.anyMatch(predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否所有元素满足给定断言
|
|
||||||
*
|
|
||||||
* @param predicate 断言
|
|
||||||
* @return 是否所有元素满足给定断言
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean allMatch(final Predicate<? super T> predicate) {
|
|
||||||
return stream.allMatch(predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断是否没有元素满足给定断言
|
|
||||||
*
|
|
||||||
* @param predicate 断言
|
|
||||||
* @return 是否没有元素满足给定断言
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean noneMatch(final Predicate<? super T> predicate) {
|
|
||||||
return stream.noneMatch(predicate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取第一个元素
|
|
||||||
*
|
|
||||||
* @return 第一个元素
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Optional<T> findFirst() {
|
|
||||||
return stream.findFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取与给定断言匹配的第一个元素
|
* 获取与给定断言匹配的第一个元素
|
||||||
*
|
*
|
||||||
@@ -1057,48 +618,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
return of(array).parallel(isParallel()).onClose(stream::close);
|
return of(array).parallel(isParallel()).onClose(stream::close);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 考虑性能,随便取一个,这里不是随机取一个,是随便取一个
|
|
||||||
*
|
|
||||||
* @return 随便取一个
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Optional<T> findAny() {
|
|
||||||
return stream.findAny();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回流的迭代器
|
|
||||||
*
|
|
||||||
* @return 流的迭代器
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
|
||||||
return stream.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回流的拆分器
|
|
||||||
*
|
|
||||||
* @return 流的拆分器
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Spliterator<T> spliterator() {
|
|
||||||
return stream.spliterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将流转换为并行
|
|
||||||
*
|
|
||||||
* @return 并行流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> parallel() {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
stream.parallel();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更改流的并行状态
|
* 更改流的并行状态
|
||||||
*
|
*
|
||||||
@@ -1109,30 +628,6 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
return parallel ? parallel() : sequential();
|
return parallel ? parallel() : sequential();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 返回一个无序流(无手动排序)
|
|
||||||
* <p>标记一个流是不在意元素顺序的, 在并行流的某些情况下可以提高性能</p>
|
|
||||||
*
|
|
||||||
* @return 无序流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> unordered() {
|
|
||||||
return new EasyStream<>(stream.unordered());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在流关闭时执行操作
|
|
||||||
*
|
|
||||||
* @param closeHandler 在流关闭时执行的操作
|
|
||||||
* @return 流
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EasyStream<T> onClose(final Runnable closeHandler) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
stream.onClose(closeHandler);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 与给定元素组成的流合并,成为新的流
|
* 与给定元素组成的流合并,成为新的流
|
||||||
*
|
*
|
||||||
@@ -1187,57 +682,14 @@ public class EasyStream<T> implements Stream<T>, Iterable<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 返回流的并行状态
|
* 根据一个原始的流,返回一个新包装类实例
|
||||||
*
|
*
|
||||||
* @return 流的并行状态
|
* @param stream 流
|
||||||
|
* @return 实现类
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isParallel() {
|
protected EasyStream<T> convertToStreamImpl(Stream<T> stream) {
|
||||||
return stream.isParallel();
|
return new EasyStream<>(stream);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭流
|
|
||||||
*
|
|
||||||
* @see AutoCloseable#close()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hashcode
|
|
||||||
*
|
|
||||||
* @return hashcode
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return stream.hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* equals
|
|
||||||
*
|
|
||||||
* @param obj 对象
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean equals(final Object obj) {
|
|
||||||
if (obj instanceof Stream) {
|
|
||||||
return stream.equals(obj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* toString
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return stream.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
774
hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java
Normal file
774
hutool-core/src/main/java/cn/hutool/core/stream/EntryStream.java
Normal file
@@ -0,0 +1,774 @@
|
|||||||
|
package cn.hutool.core.stream;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.ConcurrentHashSet;
|
||||||
|
import cn.hutool.core.map.multi.RowKeyTable;
|
||||||
|
import cn.hutool.core.map.multi.Table;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
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 Map.Entry}特化的增强流,
|
||||||
|
* 本身可视为一个元素类型为{@link Map.Entry}的{@link Stream}。<br>
|
||||||
|
* 用于支持流式处理{@link Map}集合中的、或具有潜在可能转为{@link Map}集合的数据。
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author huangchengxing
|
||||||
|
*/
|
||||||
|
public class EntryStream<K, V> extends StreamWrapper<Map.Entry<K, V>, EntryStream<K, V>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据键与值的集合创建键值对流,若两集合在相同下标的位置找不到对应的键或值,则使用{@code null}填充。<br>
|
||||||
|
* 比如: {@code [1, 2, 3]}与{@code [1, 2]}合并,则得到{@code [{1=1}, {2=2}, {3=null}]}。
|
||||||
|
*
|
||||||
|
* @param keys 键集合
|
||||||
|
* @param values 值集合
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public static <A, B> EntryStream<A, B> merge(Iterable<A> keys, Iterable<B> values) {
|
||||||
|
final boolean hasKeys = ObjUtil.isNotNull(keys);
|
||||||
|
final boolean hasValues = ObjUtil.isNotNull(values);
|
||||||
|
// 皆为空
|
||||||
|
if (!hasKeys && !hasValues) {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
// 值为空
|
||||||
|
if (hasKeys && !hasValues) {
|
||||||
|
return of(keys, Function.identity(), k -> null);
|
||||||
|
}
|
||||||
|
// 键为空
|
||||||
|
if (!hasKeys) {
|
||||||
|
return of(values, v -> null, Function.identity());
|
||||||
|
}
|
||||||
|
// 皆不为空
|
||||||
|
final List<Map.Entry<A, B>> entries = new ArrayList<>();
|
||||||
|
final Iterator<A> keyItr = keys.iterator();
|
||||||
|
final Iterator<B> valueItr = values.iterator();
|
||||||
|
while (keyItr.hasNext() || valueItr.hasNext()) {
|
||||||
|
entries.add(new Entry<>(
|
||||||
|
keyItr.hasNext() ? keyItr.next() : null,
|
||||||
|
valueItr.hasNext() ? valueItr.next() : null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return of(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据一个{@link Map}集合中的键值对创建一个串行流,
|
||||||
|
* 对流的操作不会影响到入参的{@code map}实例本身
|
||||||
|
*
|
||||||
|
* @param map 集合
|
||||||
|
* @param <A> 键类型
|
||||||
|
* @param <B> 值类型
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public static <A, B> EntryStream<A, B> of(Map<A, B> map) {
|
||||||
|
return ObjUtil.isNull(map) ?
|
||||||
|
empty() : of(map.entrySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据一个{@link Map.Entry}类型的{@link Iterable}创建一个串行流,
|
||||||
|
* 对流的操作不会影响到入参的{@code entries}实例本身。<br>
|
||||||
|
* 若输入流中存在元素为{@code null},则会映射为一个键值皆为{@code null}的键值对。
|
||||||
|
*
|
||||||
|
* @param entries {@link Iterable}实例
|
||||||
|
* @param <A> 键类型
|
||||||
|
* @param <B> 值类型
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public static <A, B> EntryStream<A, B> of(Iterable<? extends Map.Entry<A, B>> entries) {
|
||||||
|
return ObjUtil.isNull(entries) ?
|
||||||
|
empty() : of(StreamSupport.stream(entries.spliterator(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据一个{@link Collection}集合中创建一个串行流
|
||||||
|
*
|
||||||
|
* @param source 原始集合
|
||||||
|
* @param keyMapper 键的映射方法
|
||||||
|
* @param valueMapper 值的映射方法
|
||||||
|
* @param <A> 键类型
|
||||||
|
* @param <B> 值类型
|
||||||
|
* @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) {
|
||||||
|
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)));
|
||||||
|
return new EntryStream<>(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包装一个已有的流,若入参为空则返回一个空的串行流。<br>
|
||||||
|
* 若输入流中存在元素为{@code null},则会映射为一个键值皆为{@code null}的键值对。
|
||||||
|
*
|
||||||
|
* @param stream 流
|
||||||
|
* @param <A> 键类型
|
||||||
|
* @param <B> 值类型
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public static <A, B> EntryStream<A, B> of(Stream<? extends Map.Entry<A, B>> stream) {
|
||||||
|
return ObjUtil.isNull(stream) ?
|
||||||
|
empty() : new EntryStream<>(stream.map(Entry::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个空的串行流
|
||||||
|
*
|
||||||
|
* @param <A> 键类型
|
||||||
|
* @param <B> 值类型
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public static <A, B> EntryStream<A, B> empty() {
|
||||||
|
return new EntryStream<>(Stream.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
EntryStream(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ 中间操作 ================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据键去重,默认丢弃靠后的
|
||||||
|
*
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> distinctByKey() {
|
||||||
|
Set<K> accessed = new ConcurrentHashSet<>(16);
|
||||||
|
return new EntryStream<>(stream.filter(e -> {
|
||||||
|
K key = e.getKey();
|
||||||
|
if (accessed.contains(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
accessed.add(key);
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据值去重,默认丢弃靠后的
|
||||||
|
*
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> distinctByValue() {
|
||||||
|
Set<V> accessed = new ConcurrentHashSet<>(16);
|
||||||
|
return new EntryStream<>(stream.filter(e -> {
|
||||||
|
V val = e.getValue();
|
||||||
|
if (accessed.contains(val)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
accessed.add(val);
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据键和值过滤键值对
|
||||||
|
*
|
||||||
|
* @param filter 判断条件
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> filter(BiPredicate<? super K, ? super V> filter) {
|
||||||
|
Objects.requireNonNull(filter);
|
||||||
|
return super.filter(e -> filter.test(e.getKey(), e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据键过滤键值对
|
||||||
|
*
|
||||||
|
* @param filter 判断条件
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> filterByKey(Predicate<? super K> filter) {
|
||||||
|
Objects.requireNonNull(filter);
|
||||||
|
return super.filter(e -> filter.test(e.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据值过滤键值对
|
||||||
|
*
|
||||||
|
* @param filter 判断条件
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> filterByValue(Predicate<? super V> filter) {
|
||||||
|
Objects.requireNonNull(filter);
|
||||||
|
return super.filter(e -> filter.test(e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤流中键值对本身、键值对中的值或键为{@code null}的元素
|
||||||
|
*
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> nonNull() {
|
||||||
|
return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey()) && ObjUtil.isNotNull(e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤流中键值对本身,或键值对的键为{@code null}的元素
|
||||||
|
*
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> keyNonNull() {
|
||||||
|
return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤流中键值对本身,或键值对的值为{@code null}的元素
|
||||||
|
*
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> valueNonNull() {
|
||||||
|
return super.filter(e -> ObjUtil.isNotNull(e) && ObjUtil.isNotNull(e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查键
|
||||||
|
*
|
||||||
|
* @param consumer 操作
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> peekKey(Consumer<? super K> consumer) {
|
||||||
|
Objects.requireNonNull(consumer);
|
||||||
|
return super.peek(e -> consumer.accept(e.getKey()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查值
|
||||||
|
*
|
||||||
|
* @param consumer 操作
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> peekValue(Consumer<? super V> consumer) {
|
||||||
|
Objects.requireNonNull(consumer);
|
||||||
|
return super.peek(e -> consumer.accept(e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据键排序
|
||||||
|
*
|
||||||
|
* @param comparator 排序器
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> sortByKey(Comparator<? super K> comparator) {
|
||||||
|
Objects.requireNonNull(comparator);
|
||||||
|
return sorted(Map.Entry.comparingByKey(comparator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据值排序
|
||||||
|
*
|
||||||
|
* @param comparator 排序器
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<K, V> sortByValue(Comparator<? super V> comparator) {
|
||||||
|
Objects.requireNonNull(comparator);
|
||||||
|
return sorted(Map.Entry.comparingByValue(comparator));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ 转换操作 ================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向当前流末尾追加元素
|
||||||
|
*
|
||||||
|
* @param key 键
|
||||||
|
* @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))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为值的流
|
||||||
|
*
|
||||||
|
* @return 值的流
|
||||||
|
*/
|
||||||
|
public EasyStream<V> toValueStream() {
|
||||||
|
return EasyStream.of(stream.map(Map.Entry::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为键的流
|
||||||
|
*
|
||||||
|
* @return 值的流
|
||||||
|
*/
|
||||||
|
public EasyStream<K> toKeyStream() {
|
||||||
|
return EasyStream.of(stream.map(Map.Entry::getKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键映射为另一类型
|
||||||
|
*
|
||||||
|
* @param mapper 映射方法
|
||||||
|
* @param <N> 新的键类型
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public <N> EntryStream<N, V> mapKeys(Function<? super K, ? extends N> mapper) {
|
||||||
|
Objects.requireNonNull(mapper);
|
||||||
|
return new EntryStream<>(
|
||||||
|
stream.map(e -> new Entry<>(mapper.apply(e.getKey()), e.getValue()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将值映射为另一类型
|
||||||
|
*
|
||||||
|
* @param mapper 映射方法
|
||||||
|
* @param <N> 新的值类型
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public <N> EntryStream<K, N> mapValues(Function<? super V, ? extends N> mapper) {
|
||||||
|
Objects.requireNonNull(mapper);
|
||||||
|
return new EntryStream<>(
|
||||||
|
stream.map(e -> new Entry<>(e.getKey(), mapper.apply(e.getValue())))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回与指定函数将元素作为参数执行的结果组成的流
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 指定的函数
|
||||||
|
* @param <R> 函数执行后返回流中元素的类型
|
||||||
|
* @return 返回叠加操作后的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> EasyStream<R> map(Function<? super Map.Entry<K, V>, ? extends R> mapper) {
|
||||||
|
Objects.requireNonNull(mapper);
|
||||||
|
return EasyStream.of(stream.map(mapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将实例转为根据键值对生成的单对象{@link Stream}实例
|
||||||
|
*
|
||||||
|
* @param mapper 映射方法
|
||||||
|
* @param <N> 函数执行后返回流中元素的类型
|
||||||
|
* @return 映射后的单对象组成的流
|
||||||
|
*/
|
||||||
|
public <N> EasyStream<N> map(BiFunction<? super K, ? super V, ? extends N> mapper) {
|
||||||
|
Objects.requireNonNull(mapper);
|
||||||
|
return EasyStream.of(stream.map(e -> mapper.apply(e.getKey(), e.getValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩散流操作,可能影响流元素个数,将原有流元素执行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(Function<? super Map.Entry<K, V>, ? extends Stream<? extends R>> mapper) {
|
||||||
|
Objects.requireNonNull(mapper);
|
||||||
|
return EasyStream.of(stream.flatMap(mapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>将原有流的键执行mapper操作映射为流,流中的所有所有元素仍然对应原本的值,
|
||||||
|
* 然后再返回由这些流中所有元素组成的流新{@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}]
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param keyMapper 值转映射方法
|
||||||
|
* @param <N> 新的键类型
|
||||||
|
* @return 返回叠加拆分操作后的流
|
||||||
|
*/
|
||||||
|
public <N> EntryStream<N, V> flatMapKey(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()))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>将原有流的值执行mapper操作映射为流,流中的所有所有元素仍然对应原本的键,
|
||||||
|
* 然后再返回由这些流中所有元素组成的流新{@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}]
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param valueMapper 值转映射方法
|
||||||
|
* @param <N> 新的值类型
|
||||||
|
* @return 返回叠加拆分操作后的流
|
||||||
|
*/
|
||||||
|
public <N> EntryStream<K, N> flatMapValue(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))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================ 结束操作 ================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为{@link Map}集合
|
||||||
|
*
|
||||||
|
* @param mapFactory 获取集合的工厂方法
|
||||||
|
* @param operator 当存在重复键时的处理
|
||||||
|
* @return 集合
|
||||||
|
* @see Collectors#toMap(Function, Function, BinaryOperator, Supplier)
|
||||||
|
*/
|
||||||
|
public Map<K, V> toMap(Supplier<Map<K, V>> mapFactory, BinaryOperator<V> operator) {
|
||||||
|
Objects.requireNonNull(mapFactory);
|
||||||
|
Objects.requireNonNull(operator);
|
||||||
|
return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, operator, mapFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为{@link Map}集合
|
||||||
|
*
|
||||||
|
* @param mapFactory 获取集合的工厂方法
|
||||||
|
* @return 集合
|
||||||
|
* @see Collectors#toMap(Function, Function, BinaryOperator)
|
||||||
|
*/
|
||||||
|
public Map<K, V> toMap(Supplier<Map<K, V>> mapFactory) {
|
||||||
|
Objects.requireNonNull(mapFactory);
|
||||||
|
return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (t1, t2) -> t2, mapFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转为{@link HashMap}集合
|
||||||
|
*
|
||||||
|
* @return 集合
|
||||||
|
* @see Collectors#toMap(Function, Function)
|
||||||
|
* @throws IllegalArgumentException 当键重复时抛出
|
||||||
|
*/
|
||||||
|
public Map<K, V> toMap() {
|
||||||
|
return super.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对分组后再转为二维{@link Map}集合,最终返回一个{@link Table}集合
|
||||||
|
*
|
||||||
|
* @param rowKeyMapper 将键映射为父集合中键的方法
|
||||||
|
* @param colMapFactory 创建子集合的工厂方法
|
||||||
|
* @param operator 当存在重复键时的处理
|
||||||
|
* @param <N> 父集合的键类型
|
||||||
|
* @return 集合
|
||||||
|
* @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) {
|
||||||
|
Objects.requireNonNull(rowKeyMapper);
|
||||||
|
Objects.requireNonNull(colMapFactory);
|
||||||
|
Objects.requireNonNull(operator);
|
||||||
|
final Map<N, Map<K, V>> rawMap = collect(Collectors.groupingBy(
|
||||||
|
e -> rowKeyMapper.apply(e.getKey(), e.getValue()),
|
||||||
|
HashMap::new,
|
||||||
|
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, operator, colMapFactory)
|
||||||
|
));
|
||||||
|
return new RowKeyTable<>(rawMap, colMapFactory::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对分组后再转为二维{@link HashMap}集合,最终返回一个{@link Table}集合
|
||||||
|
*
|
||||||
|
* @param rowKeyMapper 创建父集合的工厂方法
|
||||||
|
* @param <N> 父集合的键类型
|
||||||
|
* @return 集合
|
||||||
|
* @throws IllegalArgumentException 当父集合或子集合中的键重复时抛出
|
||||||
|
*/
|
||||||
|
public <N> Table<N, K, V> toTable(BiFunction<? super K, ? super V, ? extends N> rowKeyMapper) {
|
||||||
|
return toTable(rowKeyMapper, HashMap::new, throwingMerger());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按值分组后再转为二维{@link Map}集合,最终返回一个{@link Table}集合
|
||||||
|
*
|
||||||
|
* @param rowKeyMapper 将键映射为父集合中键的方法
|
||||||
|
* @param colMapFactory 创建子集合的工厂方法
|
||||||
|
* @param operator 当存在重复键时的处理
|
||||||
|
* @param <N> 父集合的键类型
|
||||||
|
* @return 集合
|
||||||
|
*/
|
||||||
|
public <N> Table<N, K, V> toTableByKey(
|
||||||
|
Function<? super K, ? extends N> rowKeyMapper, Supplier<Map<K, V>> colMapFactory, BinaryOperator<V> operator) {
|
||||||
|
return toTable((k, v) -> rowKeyMapper.apply(k), colMapFactory, operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按键分组后再转为二维{@link HashMap}集合,最终返回一个{@link Table}集合
|
||||||
|
*
|
||||||
|
* @param rowKeyMapper 创建父集合的工厂方法
|
||||||
|
* @param <N> 父集合的键类型
|
||||||
|
* @return 集合
|
||||||
|
* @throws IllegalArgumentException 当父集合或子集合中的键重复时抛出
|
||||||
|
*/
|
||||||
|
public <N> Table<N, K, V> toTableByKey(Function<? super K, ? extends N> rowKeyMapper) {
|
||||||
|
return toTable((k, v) -> rowKeyMapper.apply(k));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按值分组后再转为二维{@link Map}集合,最终返回一个{@link Table}集合
|
||||||
|
*
|
||||||
|
* @param rowKeyMapper 将键映射为父集合中键的方法
|
||||||
|
* @param colMapFactory 创建子集合的工厂方法
|
||||||
|
* @param operator 当存在重复键时的处理
|
||||||
|
* @param <N> 父集合的键类型
|
||||||
|
* @return 集合
|
||||||
|
*/
|
||||||
|
public <N> Table<N, K, V> toTableByValue(
|
||||||
|
Function<? super V, ? extends N> rowKeyMapper, Supplier<Map<K, V>> colMapFactory, BinaryOperator<V> operator) {
|
||||||
|
return toTable((k, v) -> rowKeyMapper.apply(v), colMapFactory, operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按键分组后再转为二维{@link HashMap}集合,最终返回一个{@link Table}集合
|
||||||
|
*
|
||||||
|
* @param rowKeyMapper 创建父集合的工厂方法
|
||||||
|
* @param <N> 父集合的键类型
|
||||||
|
* @return 集合
|
||||||
|
* @throws IllegalArgumentException 当父集合或子集合中的键重复时抛出
|
||||||
|
*/
|
||||||
|
public <N> Table<N, K, V> toTableByValue(Function<? super V, ? extends N> rowKeyMapper) {
|
||||||
|
return toTable((k, v) -> rowKeyMapper.apply(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按键分组
|
||||||
|
*
|
||||||
|
* @return 集合
|
||||||
|
*/
|
||||||
|
public Map<K, List<V>> groupByKey() {
|
||||||
|
return groupByKey(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按键分组
|
||||||
|
*
|
||||||
|
* @param collector 对具有相同键的值的收集器
|
||||||
|
* @param <C> 值集合的类型
|
||||||
|
* @return 集合
|
||||||
|
*/
|
||||||
|
public <C extends Collection<V>> Map<K, C> groupByKey(Collector<V, ?, C> collector) {
|
||||||
|
return groupByKey((Supplier<Map<K,C>>)HashMap::new, collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对按键分组
|
||||||
|
*
|
||||||
|
* @param mapFactory 创建map集合的工厂方法
|
||||||
|
* @param collector 对具有相同键的值的收集器
|
||||||
|
* @param <C> 值集合的类型
|
||||||
|
* @param <M> 返回的map集合类型
|
||||||
|
* @return 集合
|
||||||
|
*/
|
||||||
|
public <C extends Collection<V>, M extends Map<K, C>> M groupByKey(Supplier<M> mapFactory, 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))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 遍历键值对
|
||||||
|
*
|
||||||
|
* @param consumer 操作
|
||||||
|
*/
|
||||||
|
public void forEach(BiConsumer<K, V> consumer) {
|
||||||
|
Objects.requireNonNull(consumer);
|
||||||
|
super.forEach(e -> consumer.accept(e.getKey(), e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将键值对翻转
|
||||||
|
*
|
||||||
|
* @return {@link EntryStream}实例
|
||||||
|
*/
|
||||||
|
public EntryStream<V, K> inverse() {
|
||||||
|
return new EntryStream<>(
|
||||||
|
stream.map(e -> new Entry<>(e.getValue(), e.getKey()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集键
|
||||||
|
*
|
||||||
|
* @param collector 收集器
|
||||||
|
* @param <R> 返回值类型
|
||||||
|
* @return 收集容器
|
||||||
|
*/
|
||||||
|
public <R> R collectKeys(Collector<K, ?, R> collector) {
|
||||||
|
return toKeyStream().collect(collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收集值
|
||||||
|
*
|
||||||
|
* @param collector 收集器
|
||||||
|
* @param <R> 返回值类型
|
||||||
|
* @return 收集容器
|
||||||
|
*/
|
||||||
|
public <R> R collectValues(Collector<V, ?, R> collector) {
|
||||||
|
return toValueStream().collect(collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否存在任意符合条件的键值对
|
||||||
|
*
|
||||||
|
* @param predicate 判断条件
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public boolean anyMatch(BiPredicate<? super K, ? super V> predicate) {
|
||||||
|
return super.anyMatch(e -> predicate.test(e.getKey(), e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有键值对是否都符合条件
|
||||||
|
*
|
||||||
|
* @param predicate 判断条件
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public boolean allMatch(BiPredicate<? super K, ? super V> predicate) {
|
||||||
|
Objects.requireNonNull(predicate);
|
||||||
|
return super.allMatch(e -> predicate.test(e.getKey(), e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 所有键值对是否都不符合条件
|
||||||
|
*
|
||||||
|
* @param predicate 判断条件
|
||||||
|
* @return 是否
|
||||||
|
*/
|
||||||
|
public boolean noneMatch(BiPredicate<? super K, ? super V> predicate) {
|
||||||
|
Objects.requireNonNull(predicate);
|
||||||
|
return super.noneMatch(e -> predicate.test(e.getKey(), e.getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Map.Entry}的基本实现
|
||||||
|
*/
|
||||||
|
static class Entry<K, V> implements Map.Entry<K, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 键
|
||||||
|
*/
|
||||||
|
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 + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key重复时直接抛出异常
|
||||||
|
*/
|
||||||
|
private static <T> BinaryOperator<T> throwingMerger() {
|
||||||
|
return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,563 @@
|
|||||||
|
package cn.hutool.core.stream;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.*;
|
||||||
|
import java.util.stream.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Stream}的包装类,用于基于一个已有的流实例进行扩展
|
||||||
|
*
|
||||||
|
* @author huangchengxing
|
||||||
|
* @see EasyStream
|
||||||
|
*/
|
||||||
|
abstract class StreamWrapper<T, I extends Stream<T>> implements Stream<T>, Iterable<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始的流实例
|
||||||
|
*/
|
||||||
|
protected final Stream<T> stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个流包装器
|
||||||
|
*
|
||||||
|
* @param stream 包装的流对象
|
||||||
|
*/
|
||||||
|
protected StreamWrapper(Stream<T> stream) {
|
||||||
|
Objects.requireNonNull(stream, "stream must not null");
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤元素,返回与指定断言匹配的元素组成的流
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param predicate 断言
|
||||||
|
* @return 返回叠加过滤操作后的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I filter(Predicate<? super T> predicate) {
|
||||||
|
return convertToStreamImpl(stream.filter(predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 和{@link EasyStream#map(Function)}一样,只不过函数的返回值必须为int类型
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 返回值为int类型的函数
|
||||||
|
* @return 叠加操作后元素类型全为int的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IntStream mapToInt(ToIntFunction<? super T> mapper) {
|
||||||
|
return stream.mapToInt(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 和{@link EasyStream#map(Function)}一样,只不过函数的返回值必须为long类型
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 返回值为long类型的函数
|
||||||
|
* @return 叠加操作后元素类型全为long的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LongStream mapToLong(ToLongFunction<? super T> mapper) {
|
||||||
|
return stream.mapToLong(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 和{@link EasyStream#map(Function)}一样,只不过函数的返回值必须为double类型
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 返回值为double类型的函数
|
||||||
|
* @return 叠加操作后元素类型全为double的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
|
||||||
|
return stream.mapToDouble(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 操作,返回IntStream
|
||||||
|
* @return 返回叠加拆分操作后的IntStream
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
|
||||||
|
return stream.flatMapToInt(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 操作,返回LongStream
|
||||||
|
* @return 返回叠加拆分操作后的LongStream
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
|
||||||
|
return stream.flatMapToLong(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩散流操作,可能影响流元素个数,将原有流元素执行mapper操作,返回多个流所有元素组成的流
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param mapper 操作,返回DoubleStream
|
||||||
|
* @return 返回叠加拆分操作后的DoubleStream
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
|
||||||
|
return stream.flatMapToDouble(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个具有去重特征的流 非并行流(顺序流)下对于重复元素,保留遇到顺序中最先出现的元素,并行流情况下不能保证具体保留哪一个
|
||||||
|
* 这是一个有状态中间操作
|
||||||
|
*
|
||||||
|
* @return 一个具有去重特征的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I distinct() {
|
||||||
|
return convertToStreamImpl(stream.distinct());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个元素按自然顺序排序的流
|
||||||
|
* 如果此流的元素不是{@code Comparable} ,则在执行终端操作时可能会抛出 {@code java.lang.ClassCastException}
|
||||||
|
* 对于顺序流,排序是稳定的。 对于无序流,没有稳定性保证。
|
||||||
|
* 这是一个有状态中间操作
|
||||||
|
*
|
||||||
|
* @return 一个元素按自然顺序排序的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I sorted() {
|
||||||
|
return convertToStreamImpl(stream.sorted());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个元素按指定的{@link Comparator}排序的流
|
||||||
|
* 如果此流的元素不是{@code Comparable} ,则在执行终端操作时可能会抛出{@code java.lang.ClassCastException}
|
||||||
|
* 对于顺序流,排序是稳定的。 对于无序流,没有稳定性保证。
|
||||||
|
* 这是一个有状态中间操作
|
||||||
|
*
|
||||||
|
* @param comparator 排序规则
|
||||||
|
* @return 一个元素按指定的Comparator排序的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I sorted(Comparator<? super T> comparator) {
|
||||||
|
return convertToStreamImpl(stream.sorted(comparator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回与指定函数将元素作为参数执行后组成的流。
|
||||||
|
* 这是一个无状态中间操作
|
||||||
|
*
|
||||||
|
* @param action 指定的函数
|
||||||
|
* @return 返回叠加操作后的FastStream
|
||||||
|
* @apiNote 该方法存在的意义主要是用来调试
|
||||||
|
* 当你需要查看经过操作管道某处的元素,可以执行以下操作:
|
||||||
|
* <pre>{@code
|
||||||
|
* .of("one", "two", "three", "four")
|
||||||
|
* .filter(e -> e.length() > 3)
|
||||||
|
* .peek(e -> System.out.println("Filtered value: " + e))
|
||||||
|
* .map(String::toUpperCase)
|
||||||
|
* .peek(e -> System.out.println("Mapped value: " + e))
|
||||||
|
* .collect(Collectors.toList());
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I peek(Consumer<? super T> action) {
|
||||||
|
return convertToStreamImpl(stream.peek(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回截取后面一些元素的流
|
||||||
|
* 这是一个短路状态中间操作
|
||||||
|
*
|
||||||
|
* @param maxSize 元素截取后的个数
|
||||||
|
* @return 截取后的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I limit(long maxSize) {
|
||||||
|
return convertToStreamImpl(stream.limit(maxSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回丢弃前面n个元素后的剩余元素组成的流,如果当前元素个数小于n,则返回一个元素为空的流
|
||||||
|
* 这是一个有状态中间操作
|
||||||
|
*
|
||||||
|
* @param n 需要丢弃的元素个数
|
||||||
|
* @return 丢弃前面n个元素后的剩余元素组成的流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I skip(long n) {
|
||||||
|
return convertToStreamImpl(stream.skip(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对流里面的每一个元素执行一个操作
|
||||||
|
* 这是一个终端操作
|
||||||
|
*
|
||||||
|
* @param action 操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void forEach(Consumer<? super T> action) {
|
||||||
|
stream.forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对流里面的每一个元素按照顺序执行一个操作
|
||||||
|
* 这是一个终端操作
|
||||||
|
*
|
||||||
|
* @param action 操作
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void forEachOrdered(Consumer<? super T> action) {
|
||||||
|
stream.forEachOrdered(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个包含此流元素的数组
|
||||||
|
* 这是一个终端操作
|
||||||
|
*
|
||||||
|
* @return 包含此流元素的数组
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return stream.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个包含此流元素的指定的数组,例如以下代码编译正常,但运行时会抛出 {@link ArrayStoreException}
|
||||||
|
* <pre>{@code String[] strings = Stream.<Integer>builder().add(1).build().toArray(String[]::new); }</pre>
|
||||||
|
*
|
||||||
|
* @param generator 这里的IntFunction的参数是元素的个数,返回值为数组类型
|
||||||
|
* @param <A> 给定的数组类型
|
||||||
|
* @return 包含此流元素的指定的数组
|
||||||
|
* @throws ArrayStoreException 如果元素转换失败,例如不是该元素类型及其父类,则抛出该异常
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <A> A[] toArray(IntFunction<A[]> generator) {
|
||||||
|
return stream.toArray(generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对元素进行聚合,并返回聚合后的值,相当于在for循环里写sum=sum+ints[i]
|
||||||
|
* 这是一个终端操作<br>
|
||||||
|
* 求和、最小值、最大值、平均值和转换成一个String字符串均为聚合操作
|
||||||
|
* 例如这里对int进行求和可以写成:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Integer sum = integers.reduce(0, (a, b) -> a+b);
|
||||||
|
* }</pre>
|
||||||
|
* <p>
|
||||||
|
* 或者写成:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* Integer sum = integers.reduce(0, Integer::sum);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param identity 初始值,还用于限定泛型
|
||||||
|
* @param accumulator 你想要的聚合操作
|
||||||
|
* @return 聚合计算后的值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T reduce(T identity, BinaryOperator<T> accumulator) {
|
||||||
|
return stream.reduce(identity, accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对元素进行聚合,并返回聚合后用 {@link Optional}包裹的值,相当于在for循环里写sum=sum+ints[i]
|
||||||
|
* 该操作相当于:
|
||||||
|
* <pre>{@code
|
||||||
|
* boolean foundAny = false;
|
||||||
|
* T result = null;
|
||||||
|
* for (T element : this stream) {
|
||||||
|
* if (!foundAny) {
|
||||||
|
* foundAny = true;
|
||||||
|
* result = element;
|
||||||
|
* }
|
||||||
|
* else
|
||||||
|
* result = accumulator.apply(result, element);
|
||||||
|
* }
|
||||||
|
* return foundAny ? Optional.of(result) : Optional.empty();
|
||||||
|
* }</pre>
|
||||||
|
* 但它不局限于顺序执行,例如并行流等情况下
|
||||||
|
* 这是一个终端操作<br>
|
||||||
|
* 例如以下场景抛出 NPE :
|
||||||
|
* <pre>{@code
|
||||||
|
* Optional<Integer> reduce = Stream.<Integer>builder().add(1).add(1).build().reduce((a, b) -> null);
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param accumulator 你想要的聚合操作
|
||||||
|
* @return 聚合后用 {@link Optional}包裹的值
|
||||||
|
* @throws NullPointerException 如果给定的聚合操作中执行后结果为空,并用于下一次执行,则抛出该异常
|
||||||
|
* @see #reduce(Object, BinaryOperator)
|
||||||
|
* @see #min(Comparator)
|
||||||
|
* @see #max(Comparator)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<T> reduce(BinaryOperator<T> accumulator) {
|
||||||
|
return stream.reduce(accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对元素进行聚合,并返回聚合后的值,并行流时聚合拿到的初始值不稳定
|
||||||
|
* 这是一个终端操作
|
||||||
|
*
|
||||||
|
* @param identity 初始值
|
||||||
|
* @param accumulator 累加器,具体为你要的聚合操作
|
||||||
|
* @param combiner 用于并行流时组合多个结果
|
||||||
|
* @param <U> 初始值
|
||||||
|
* @return 聚合操作的结果
|
||||||
|
* @see #reduce(BinaryOperator)
|
||||||
|
* @see #reduce(Object, BinaryOperator)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
|
||||||
|
return stream.reduce(identity, accumulator, combiner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对元素进行收集,并返回收集后的容器
|
||||||
|
* 这是一个终端操作
|
||||||
|
*
|
||||||
|
* @param supplier 提供初始值的函数式接口,一般可以传入构造参数
|
||||||
|
* @param accumulator 具体收集操作
|
||||||
|
* @param combiner 用于并行流时组合多个结果
|
||||||
|
* @param <R> 用于收集元素的容器,大多是集合
|
||||||
|
* @return 收集后的容器
|
||||||
|
* <pre>{@code
|
||||||
|
* List<Integer> collect = Stream.iterate(1, i -> ++i).limit(10).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
|
||||||
|
return stream.collect(supplier, accumulator, combiner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对元素进行收集,并返回收集后的元素
|
||||||
|
* 这是一个终端操作
|
||||||
|
*
|
||||||
|
* @param collector 收集器
|
||||||
|
* @param <R> 容器类型
|
||||||
|
* @param <A> 具体操作时容器类型,例如 {@code List::add} 时它为 {@code List}
|
||||||
|
* @return 收集后的容器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <R, A> R collect(Collector<? super T, A, R> collector) {
|
||||||
|
return stream.collect(collector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最小值
|
||||||
|
*
|
||||||
|
* @param comparator 一个用来比较大小的比较器{@link Comparator}
|
||||||
|
* @return 最小值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<T> min(Comparator<? super T> comparator) {
|
||||||
|
return stream.min(comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最大值
|
||||||
|
*
|
||||||
|
* @param comparator 一个用来比较大小的比较器{@link Comparator}
|
||||||
|
* @return 最大值
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<T> max(Comparator<? super T> comparator) {
|
||||||
|
return stream.max(comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回流元素个数
|
||||||
|
*
|
||||||
|
* @return 流元素个数
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long count() {
|
||||||
|
return stream.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有任何一个元素满足给定断言
|
||||||
|
*
|
||||||
|
* @param predicate 断言
|
||||||
|
* @return 是否有任何一个元素满足给定断言
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean anyMatch(Predicate<? super T> predicate) {
|
||||||
|
return stream.anyMatch(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否所有元素满足给定断言
|
||||||
|
*
|
||||||
|
* @param predicate 断言
|
||||||
|
* @return 是否所有元素满足给定断言
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean allMatch(Predicate<? super T> predicate) {
|
||||||
|
return stream.allMatch(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否没有元素满足给定断言
|
||||||
|
*
|
||||||
|
* @param predicate 断言
|
||||||
|
* @return 是否没有元素满足给定断言
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean noneMatch(Predicate<? super T> predicate) {
|
||||||
|
return stream.noneMatch(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取第一个元素
|
||||||
|
*
|
||||||
|
* @return 第一个元素
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<T> findFirst() {
|
||||||
|
return stream.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 考虑性能,随便取一个,这里不是随机取一个,是随便取一个
|
||||||
|
*
|
||||||
|
* @return 随便取一个
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<T> findAny() {
|
||||||
|
return stream.findAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回流的迭代器
|
||||||
|
*
|
||||||
|
* @return 流的迭代器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return stream.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回流的拆分器
|
||||||
|
*
|
||||||
|
* @return 流的拆分器
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Spliterator<T> spliterator() {
|
||||||
|
return stream.spliterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回流的并行状态
|
||||||
|
*
|
||||||
|
* @return 流的并行状态
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isParallel() {
|
||||||
|
return stream.isParallel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个串行流,该方法可以将并行流转换为串行流
|
||||||
|
*
|
||||||
|
* @return 串行流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I sequential() {
|
||||||
|
return convertToStreamImpl(stream.sequential());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将流转换为并行
|
||||||
|
*
|
||||||
|
* @return 并行流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I parallel() {
|
||||||
|
return convertToStreamImpl(stream.parallel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一个无序流(无手动排序)
|
||||||
|
* <p>标记一个流是不在意元素顺序的, 在并行流的某些情况下可以提高性能</p>
|
||||||
|
*
|
||||||
|
* @return 无序流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I unordered() {
|
||||||
|
return convertToStreamImpl(stream.unordered());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在流关闭时执行操作
|
||||||
|
*
|
||||||
|
* @param closeHandler 在流关闭时执行的操作
|
||||||
|
* @return 流
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public I onClose(Runnable closeHandler) {
|
||||||
|
return convertToStreamImpl(stream.onClose(closeHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭流
|
||||||
|
*
|
||||||
|
* @see AutoCloseable#close()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashcode
|
||||||
|
*
|
||||||
|
* @return hashcode
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return stream.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* equals
|
||||||
|
*
|
||||||
|
* @param obj 对象
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (obj instanceof Stream) {
|
||||||
|
return stream.equals(obj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toString
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return stream.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据一个原始的流,返回一个新包装类实例
|
||||||
|
*
|
||||||
|
* @param stream 流
|
||||||
|
* @return 实现类
|
||||||
|
*/
|
||||||
|
protected abstract I convertToStreamImpl(Stream<T> stream);
|
||||||
|
|
||||||
|
}
|
@@ -4,11 +4,10 @@ import cn.hutool.core.map.MapUtil;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.function.Function;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CollectorUtilTest
|
* CollectorUtilTest
|
||||||
@@ -21,17 +20,50 @@ public class CollectorUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void reduceListMapTest() {
|
public void reduceListMapTest() {
|
||||||
final Set<Map<String, Integer>> nameScoreMapList = StreamUtil.of(
|
final Set<Map<String, Integer>> nameScoreMapList = StreamUtil.of(
|
||||||
// 集合内的第一个map,包含两个key value
|
// 集合内的第一个map,包含两个key value
|
||||||
MapUtil.builder("苏格拉底", 1).put("特拉叙马霍斯", 3).build(),
|
MapUtil.builder("苏格拉底", 1).put("特拉叙马霍斯", 3).build(),
|
||||||
MapUtil.of("苏格拉底", 2),
|
MapUtil.of("苏格拉底", 2),
|
||||||
MapUtil.of("特拉叙马霍斯", 1),
|
MapUtil.of("特拉叙马霍斯", 1),
|
||||||
MapUtil.of("特拉叙马霍斯", 2)
|
MapUtil.of("特拉叙马霍斯", 2)
|
||||||
).collect(Collectors.toSet());
|
).collect(Collectors.toSet());
|
||||||
// 执行聚合
|
// 执行聚合
|
||||||
final Map<String, List<Integer>> nameScoresMap = nameScoreMapList.stream().collect(CollectorUtil.reduceListMap());
|
final Map<String, List<Integer>> nameScoresMap = nameScoreMapList.stream().collect(CollectorUtil.reduceListMap());
|
||||||
|
|
||||||
Assert.assertEquals(MapUtil.builder("苏格拉底", Arrays.asList(1, 2))
|
Assert.assertEquals(MapUtil.builder("苏格拉底", Arrays.asList(1, 2))
|
||||||
.put("特拉叙马霍斯", Arrays.asList(3, 1, 2)).build(),
|
.put("特拉叙马霍斯", Arrays.asList(3, 1, 2)).build(),
|
||||||
nameScoresMap);
|
nameScoresMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransform() {
|
||||||
|
Stream<Integer> stream = Stream.of(1, 2, 3, 4)
|
||||||
|
.collect(CollectorUtil.transform(EasyStream::of));
|
||||||
|
Assert.assertEquals(EasyStream.class, stream.getClass());
|
||||||
|
|
||||||
|
stream = Stream.of(1, 2, 3, 4)
|
||||||
|
.collect(CollectorUtil.transform(HashSet::new, EasyStream::of));
|
||||||
|
Assert.assertEquals(EasyStream.class, stream.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToEasyStream() {
|
||||||
|
Stream<Integer> stream =Stream.of(1, 2, 3, 4)
|
||||||
|
.collect(CollectorUtil.toEasyStream());
|
||||||
|
Assert.assertEquals(EasyStream.class, stream.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToEntryStream() {
|
||||||
|
Map<String, Integer> map = Stream.of(1, 2, 3, 4, 5)
|
||||||
|
// 转为EntryStream
|
||||||
|
.collect(CollectorUtil.toEntryStream(Function.identity(), String::valueOf))
|
||||||
|
// 过滤偶数
|
||||||
|
.filterByKey(k -> (k & 1) == 1)
|
||||||
|
.inverse()
|
||||||
|
.toMap();
|
||||||
|
Assert.assertEquals((Integer)1, map.get("1"));
|
||||||
|
Assert.assertEquals((Integer)3, map.get("3"));
|
||||||
|
Assert.assertEquals((Integer)5, map.get("5"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,505 @@
|
|||||||
|
package cn.hutool.core.stream;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.multi.Table;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class EntryStreamTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMerge() {
|
||||||
|
Assert.assertEquals(0, EntryStream.merge(null, null).count());
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(2, 4, 6),
|
||||||
|
EntryStream.merge(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3))
|
||||||
|
.map(Integer::sum)
|
||||||
|
.toList()
|
||||||
|
);
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(1, 2, null),
|
||||||
|
EntryStream.merge(Arrays.asList(1, 2, 3), Arrays.asList(1, 2))
|
||||||
|
.collectValues(Collectors.toList())
|
||||||
|
);
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(1, 2, null),
|
||||||
|
EntryStream.merge(Arrays.asList(1, 2), Arrays.asList(1, 2, 3))
|
||||||
|
.collectKeys(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOf() {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("1", "1");
|
||||||
|
Assert.assertEquals(1, EntryStream.of(map).count());
|
||||||
|
|
||||||
|
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(2, EntryStream.of(entries.stream()).count());
|
||||||
|
|
||||||
|
Iterable<Integer> iterable = Arrays.asList(1, 2, null);
|
||||||
|
Assert.assertEquals(3, EntryStream.of(iterable, Function.identity(), Function.identity()).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmpty() {
|
||||||
|
Assert.assertEquals(0, EntryStream.empty().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.distinctByKey()
|
||||||
|
.count();
|
||||||
|
Assert.assertEquals(2, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.distinctByValue()
|
||||||
|
.count();
|
||||||
|
Assert.assertEquals(2, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.filter((k, v) -> k == 1 && v == 1)
|
||||||
|
.count();
|
||||||
|
Assert.assertEquals(1, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.filterByKey(k -> k == 1)
|
||||||
|
.count();
|
||||||
|
Assert.assertEquals(2, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.filterByValue(v -> v == 1)
|
||||||
|
.count();
|
||||||
|
Assert.assertEquals(2, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekKey() {
|
||||||
|
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();
|
||||||
|
Assert.assertEquals(Arrays.asList(1, 1, 2, 2), keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPeekValue() {
|
||||||
|
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();
|
||||||
|
Assert.assertEquals(Arrays.asList(1, 2, 1, 2), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPush() {
|
||||||
|
Assert.assertEquals(
|
||||||
|
5,
|
||||||
|
EntryStream.of(Arrays.asList(1, 2, 3), Function.identity(), Function.identity())
|
||||||
|
.push(4, 4)
|
||||||
|
.push(5, 5)
|
||||||
|
.count()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.sortByKey(Comparator.comparingInt(Integer::intValue))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(1, 2, 3, 4),
|
||||||
|
entries.stream().map(Map.Entry::getKey).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)))
|
||||||
|
.sortByValue(Comparator.comparingInt(Integer::intValue))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(1, 2, 3, 4),
|
||||||
|
entries.stream().map(Map.Entry::getValue).collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToValueStream() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
Assert.assertEquals(
|
||||||
|
new ArrayList<>(map.values()), EntryStream.of(map).toValueStream().collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToKeyStream() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
Assert.assertEquals(
|
||||||
|
new ArrayList<>(map.keySet()), EntryStream.of(map).toKeyStream().collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectKey() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
List<Integer> keys = EntryStream.of(map).collectKeys(Collectors.toList());
|
||||||
|
Assert.assertEquals(new ArrayList<>(map.keySet()), keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCollectValue() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
List<Integer> keys = EntryStream.of(map).collectValues(Collectors.toList());
|
||||||
|
Assert.assertEquals(new ArrayList<>(map.keySet()), keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapKeys() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList("1", "2", "3"),
|
||||||
|
EntryStream.of(map)
|
||||||
|
.mapKeys(String::valueOf)
|
||||||
|
.toKeyStream()
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapValues() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList("1", "2", "3"),
|
||||||
|
EntryStream.of(map)
|
||||||
|
.mapValues(String::valueOf)
|
||||||
|
.toValueStream()
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMap() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList("11", "22", "33"),
|
||||||
|
EntryStream.of(map)
|
||||||
|
.map((k, v) -> k.toString() + v.toString())
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFlatMap() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
List<Integer> list = EntryStream.of(map)
|
||||||
|
.flatMap(e -> Stream.of(e.getKey(), e.getKey() + 1))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Assert.assertEquals(Arrays.asList(1, 2, 2, 3, 3, 4), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFlatMapValue() {
|
||||||
|
Map<String, Integer> map = new HashMap<>();
|
||||||
|
map.put("class1", 1);
|
||||||
|
map.put("class2", 2);
|
||||||
|
map.put("class3", 3);
|
||||||
|
List<String> values = EntryStream.of(map)
|
||||||
|
.flatMapKey(k -> Stream.of(k + "'s student1", k + "'s student2"))
|
||||||
|
.map((k, v) -> v + "=" + k)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(
|
||||||
|
"1=class1's student1", "1=class1's student2",
|
||||||
|
"2=class2's student1", "2=class2's student2",
|
||||||
|
"3=class3's student1", "3=class3's student2"
|
||||||
|
),
|
||||||
|
values
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInverse() {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("key1", "value1");
|
||||||
|
map.put("key2", "value2");
|
||||||
|
map.put("key3", "value3");
|
||||||
|
List<String> results = EntryStream.of(map)
|
||||||
|
.inverse()
|
||||||
|
.map((k, v) -> k + "=" + v)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList("value1=key1", "value2=key2", "value3=key3"),
|
||||||
|
results
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFlatMapKey() {
|
||||||
|
Map<Integer, String> map = new HashMap<>();
|
||||||
|
map.put(1, "class1");
|
||||||
|
map.put(2, "class2");
|
||||||
|
map.put(3, "class3");
|
||||||
|
List<String> values = EntryStream.of(map)
|
||||||
|
.flatMapValue(v -> Stream.of(v + "'s student1", v + "'s student2"))
|
||||||
|
.map((k, v) -> k + "=" + v)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Assert.assertEquals(
|
||||||
|
Arrays.asList(
|
||||||
|
"1=class1's student1", "1=class1's student2",
|
||||||
|
"2=class2's student1", "2=class2's student2",
|
||||||
|
"3=class3's student1", "3=class3's student2"
|
||||||
|
),
|
||||||
|
values
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testForEach() {
|
||||||
|
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<>();
|
||||||
|
EntryStream.of(map).forEach((k ,v) -> {
|
||||||
|
keys.add(k);
|
||||||
|
values.add(v);
|
||||||
|
});
|
||||||
|
Assert.assertEquals(Arrays.asList(1, 2, 3), keys);
|
||||||
|
Assert.assertEquals(Arrays.asList(1, 2, 3), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToMap() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
|
||||||
|
Map<Integer, Integer> result = EntryStream.of(map).toMap();
|
||||||
|
Assert.assertEquals(map, result);
|
||||||
|
|
||||||
|
result = EntryStream.of(map).toMap(LinkedHashMap::new);
|
||||||
|
Assert.assertEquals(new LinkedHashMap<>(map), result);
|
||||||
|
|
||||||
|
result = EntryStream.of(map).toMap(LinkedHashMap::new, (t1, t2) -> t1);
|
||||||
|
Assert.assertEquals(new LinkedHashMap<>(map), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToTable() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
map.put(4, 4);
|
||||||
|
|
||||||
|
// 按是否偶数分组
|
||||||
|
Table<Boolean, Integer, Integer> table = EntryStream.of(map).toTable(
|
||||||
|
(k ,v) -> (k & 1) == 0, HashMap::new, (t1, t2) -> t1
|
||||||
|
);
|
||||||
|
Assert.assertEquals((Integer)1, table.get(false, 1));
|
||||||
|
Assert.assertEquals((Integer)3, table.get(false, 3));
|
||||||
|
Assert.assertEquals((Integer)2, table.get(true, 2));
|
||||||
|
Assert.assertEquals((Integer)4, table.get(true, 4));
|
||||||
|
|
||||||
|
table = EntryStream.of(map).toTable((k ,v) -> (k & 1) == 0);
|
||||||
|
Assert.assertEquals((Integer)1, table.get(false, 1));
|
||||||
|
Assert.assertEquals((Integer)3, table.get(false, 3));
|
||||||
|
Assert.assertEquals((Integer)2, table.get(true, 2));
|
||||||
|
Assert.assertEquals((Integer)4, table.get(true, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToTableByKey() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
map.put(4, 4);
|
||||||
|
|
||||||
|
// 按是否偶数分组
|
||||||
|
Table<Boolean, Integer, Integer> table = EntryStream.of(map).toTableByKey(
|
||||||
|
k -> (k & 1) == 0, HashMap::new, (t1, t2) -> t1
|
||||||
|
);
|
||||||
|
Assert.assertEquals((Integer)1, table.get(false, 1));
|
||||||
|
Assert.assertEquals((Integer)3, table.get(false, 3));
|
||||||
|
Assert.assertEquals((Integer)2, table.get(true, 2));
|
||||||
|
Assert.assertEquals((Integer)4, table.get(true, 4));
|
||||||
|
|
||||||
|
table = EntryStream.of(map).toTableByKey(k -> (k & 1) == 0);
|
||||||
|
Assert.assertEquals((Integer)1, table.get(false, 1));
|
||||||
|
Assert.assertEquals((Integer)3, table.get(false, 3));
|
||||||
|
Assert.assertEquals((Integer)2, table.get(true, 2));
|
||||||
|
Assert.assertEquals((Integer)4, table.get(true, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToTableByValue() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, 1);
|
||||||
|
map.put(2, 2);
|
||||||
|
map.put(3, 3);
|
||||||
|
map.put(4, 4);
|
||||||
|
|
||||||
|
// 按是否偶数分组
|
||||||
|
Table<Boolean, Integer, Integer> table = EntryStream.of(map).toTableByValue(
|
||||||
|
v -> (v & 1) == 0, HashMap::new, (t1, t2) -> t1
|
||||||
|
);
|
||||||
|
Assert.assertEquals((Integer)1, table.get(false, 1));
|
||||||
|
Assert.assertEquals((Integer)3, table.get(false, 3));
|
||||||
|
Assert.assertEquals((Integer)2, table.get(true, 2));
|
||||||
|
Assert.assertEquals((Integer)4, table.get(true, 4));
|
||||||
|
|
||||||
|
table = EntryStream.of(map).toTableByValue(v -> (v & 1) == 0);
|
||||||
|
Assert.assertEquals((Integer)1, table.get(false, 1));
|
||||||
|
Assert.assertEquals((Integer)3, table.get(false, 3));
|
||||||
|
Assert.assertEquals((Integer)2, table.get(true, 2));
|
||||||
|
Assert.assertEquals((Integer)4, table.get(true, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupByKey() {
|
||||||
|
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())
|
||||||
|
.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())
|
||||||
|
.groupByKey(LinkedHashMap::new, Collectors.toSet());
|
||||||
|
Assert.assertEquals(2, map3.size());
|
||||||
|
Assert.assertEquals(Collections.singleton(1), map3.get(1));
|
||||||
|
Assert.assertEquals(Collections.singleton(2), map3.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnyMatch() {
|
||||||
|
Assert.assertTrue(EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity())
|
||||||
|
.anyMatch((k, v) -> (k & 1) == 1));
|
||||||
|
Assert.assertFalse(EntryStream.of(Arrays.asList(2, 2, 2, 2), Function.identity(), Function.identity())
|
||||||
|
.anyMatch((k, v) -> (k & 1) == 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllMatch() {
|
||||||
|
Assert.assertFalse(EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity())
|
||||||
|
.allMatch((k, v) -> (k & 1) == 1));
|
||||||
|
Assert.assertTrue(EntryStream.of(Arrays.asList(2, 2, 2, 2), Function.identity(), Function.identity())
|
||||||
|
.allMatch((k, v) -> (k & 1) == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoneMatch() {
|
||||||
|
Assert.assertFalse(EntryStream.of(Arrays.asList(1, 1, 2, 2), Function.identity(), Function.identity())
|
||||||
|
.noneMatch((k, v) -> (k & 1) == 1));
|
||||||
|
Assert.assertTrue(EntryStream.of(Arrays.asList(2, 2, 2, 2), Function.identity(), Function.identity())
|
||||||
|
.noneMatch((k, v) -> (k & 1) == 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonNull() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, null);
|
||||||
|
map.put(null, 1);
|
||||||
|
Assert.assertEquals(0, EntryStream.of(map).nonNull().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKeyNonNull() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, null);
|
||||||
|
map.put(null, 1);
|
||||||
|
Assert.assertEquals(1, EntryStream.of(map).keyNonNull().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValueNonNull() {
|
||||||
|
Map<Integer, Integer> map = new HashMap<>();
|
||||||
|
map.put(1, null);
|
||||||
|
map.put(null, 1);
|
||||||
|
Assert.assertEquals(1, EntryStream.of(map).valueNonNull().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Entry<K, V> implements Map.Entry<K, V> {
|
||||||
|
|
||||||
|
private final K key;
|
||||||
|
private final V value;
|
||||||
|
|
||||||
|
public Entry(K key, V value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user