【Java】Stream 流工具类
·
愿景:简化 Stream 操作,代码优雅++
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
/**
* @desc Stream流处理工具(完善版:加强null安全、一致性,并添加平均/排序/分区功能)
* 所有方法默认过滤源集合中的null元素,返回空集合而非null,避免NPE。
*/
public class StreamUtils {
/**
* 转换map,获取本身,去重,对象、key忽略null。
*
* @param source 源集合(Collection<T>),输入数据源
* @param keyFunction 获取key的函数(Function<T, K>),用于提取map的key
* @return Map<K, T> 转换后的map,key重复时保留最后value;若源空,返回空HashMap
*/
public static <T, K> Map<K, T> toMap(Collection<T> source, Function<T, K> keyFunction) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(obj -> Objects.nonNull(keyFunction.apply(obj)))
.collect(Collectors.toMap(keyFunction, Function.identity(), (v1, v2) -> v2));
}
/**
* 转换map,获取本身,去重,对象、key忽略null,并应用额外过滤。
*
* @param source 源集合(Collection<T>),输入数据源
* @param keyFunction 获取key的函数(Function<T, K>),用于提取map的key
* @param predicate 过滤条件(Predicate<? super T>),额外过滤源元素
* @return Map<K, T> 转换后的map,key重复时保留最后value;若源空,返回空HashMap
*/
public static <T, K> Map<K, T> toMap(Collection<T> source, Function<T, K> keyFunction, Predicate<? super T> predicate) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(obj -> Objects.nonNull(keyFunction.apply(obj)))
.filter(predicate)
.collect(Collectors.toMap(keyFunction, Function.identity(), (v1, v2) -> v2));
}
/**
* 转换map,去重,对象、key、value忽略null。
*
* @param source 源集合(Collection<T>),输入数据源
* @param keyFunction 获取key的函数(Function<T, K>),用于提取map的key
* @param valueFunction 获取value的函数(Function<T, V>),用于提取map的value
* @return Map<K, V> 转换后的map,key重复时保留最后value;若源空,返回空HashMap
*/
public static <T, K, V> Map<K, V> toMap(Collection<T> source, Function<T, K> keyFunction, Function<T, V> valueFunction) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(obj -> Objects.nonNull(keyFunction.apply(obj)))
.filter(obj -> Objects.nonNull(valueFunction.apply(obj)))
.collect(Collectors.toMap(keyFunction, valueFunction, (v1, v2) -> v2));
}
/**
* 转换map,去重,对象、key、value忽略null,支持自定义合并。
*
* @param source 源集合(Collection<T>),输入数据源
* @param keyFunction 获取key的函数(Function<T, K>),用于提取map的key
* @param valueFunction 获取value的函数(Function<T, V>),用于提取map的value
* @param mergeFunction 合并函数(BinaryOperator<V>),key重复时的value合并逻辑
* @return Map<K, V> 转换后的map,使用mergeFunction处理重复;若源空,返回空HashMap
*/
public static <T, K, V> Map<K, V> toMap(Collection<T> source, Function<T, K> keyFunction, Function<T, V> valueFunction, BinaryOperator<V> mergeFunction) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(obj -> Objects.nonNull(keyFunction.apply(obj)))
.filter(obj -> Objects.nonNull(valueFunction.apply(obj)))
.collect(Collectors.toMap(keyFunction, valueFunction, mergeFunction));
}
/**
* 转换map,去重,使用map重新put方式(优化:统一用toMap,过滤value null)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param keyFunction 获取key的函数(Function<T, K>),用于提取map的key
* @param valueFunction 获取value的函数(Function<T, V>),用于提取map的value
* @return HashMap<K, V> 转换后的HashMap,key重复时保留最后value;若源空,返回空HashMap
*/
public static <T, K, V> HashMap<K, V> toMap2(Collection<T> source, Function<T, K> keyFunction, Function<T, V> valueFunction) {
return (HashMap<K, V>) toMap(source, keyFunction, valueFunction, (v1, v2) -> v2); // 复用toMap,确保一致性
}
/**
* 转换为List,应用映射函数。
*
* @param source 源集合(Collection<T>),输入数据源
* @param function 映射函数(Function<T, R>),转换每个元素
* @return List<R> 转换后的List,可能包含null;若源空,返回空ArrayList
*/
public static <T, R> List<R> toList(Collection<T> source, Function<T, R> function) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newArrayList();
}
return source.stream()
.filter(Objects::nonNull)
.map(function)
.collect(Collectors.toList());
}
/**
* 转换为List,应用映射函数,忽略null结果。
*
* @param source 源集合(Collection<T>),输入数据源
* @param function 映射函数(Function<T, R>),转换每个元素
* @return List<R> 转换后的List,不含null;若源空,返回空ArrayList
*/
public static <T, R> List<R> toListIgnoreNull(Collection<T> source, Function<T, R> function) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newArrayList();
}
return source.stream()
.filter(Objects::nonNull)
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 转换为Set,应用映射函数。
*
* @param source 源集合(Collection<T>),输入数据源
* @param function 映射函数(Function<T, R>),转换每个元素
* @return Set<R> 转换后的HashSet,可能包含null;若源空,返回空HashSet
*/
public static <T, R> Set<R> toSet(Collection<T> source, Function<T, R> function) {
if (CollectionUtils.isEmpty(source)) {
return Sets.newHashSet();
}
return source.stream()
.filter(Objects::nonNull)
.map(function)
.collect(Collectors.toSet());
}
/**
* 转换为Set,应用映射函数,忽略null结果。
*
* @param source 源集合(Collection<T>),输入数据源
* @param function 映射函数(Function<T, R>),转换每个元素
* @return Set<R> 转换后的HashSet,不含null;若源空,返回空HashSet
*/
public static <T, R> Set<R> toSetIgnoreNull(Collection<T> source, Function<T, R> function) {
if (CollectionUtils.isEmpty(source)) {
return Sets.newHashSet();
}
return source.stream()
.filter(Objects::nonNull)
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
/**
* 转换为LinkedHashSet,应用映射函数,忽略null结果。
*
* @param source 源集合(Collection<T>),输入数据源
* @param function 映射函数(Function<T, R>),转换每个元素
* @return LinkedHashSet<R> 转换后的LinkedHashSet,不含null,保持插入顺序;若源空,返回空LinkedHashSet
*/
public static <T, R> LinkedHashSet<R> toLinkedHashSet(Collection<T> source, Function<T, R> function) {
if (CollectionUtils.isEmpty(source)) {
return Sets.newLinkedHashSet();
}
return source.stream()
.filter(Objects::nonNull)
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
* 转换为LinkedList,应用映射函数,忽略null结果。
*
* @param source 源集合(Collection<T>),输入数据源
* @param function 映射函数(Function<T, R>),转换每个元素
* @return LinkedList<R> 转换后的LinkedList,不含null;若源空,返回空LinkedList
*/
public static <T, R> LinkedList<R> toLinkedList(Collection<T> source, Function<T, R> function) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newLinkedList();
}
return source.stream()
.filter(Objects::nonNull)
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedList::new));
}
/**
* 将集合分组,遇到重复的key,value放入集合中。
*
* @param source 源集合(Collection<V>),输入数据源
* @param keyFunction 获取key的函数(Function<? super V, ? extends K>),用于分组key
* @return Map<K, List<V>> 分组后的map,value为List;若源空,返回空HashMap
*/
public static <K, V> Map<K, List<V>> groupToMap(Collection<V> source, Function<? super V, ? extends K> keyFunction) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(s -> Objects.nonNull(keyFunction.apply(s)))
.collect(Collectors.groupingBy(keyFunction));
}
/**
* 将集合分组,遇到重复的key,value放入集合中(LinkedHashMap 保持顺序)。
*
* @param source 源集合(Collection<V>),输入数据源
* @param keyFunction 获取key的函数(Function<? super V, ? extends K>),用于分组key
* @return LinkedHashMap<K, List<V>> 分组后的LinkedHashMap,value为List,保持插入顺序;若源空,返回空LinkedHashMap
*/
public static <K, V> LinkedHashMap<K, List<V>> groupToLinkedHashMap(Collection<V> source, Function<? super V, ? extends K> keyFunction) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newLinkedHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(s -> Objects.nonNull(keyFunction.apply(s)))
.collect(Collectors.groupingBy(keyFunction, LinkedHashMap::new, Collectors.toList()));
}
/**
* 将集合分组,遇到重复的key,并统计重复key的次数。
*
* @param source 源集合(Collection<V>),输入数据源
* @param keyFunction 获取key的函数(Function<? super V, ? extends K>),用于分组key
* @return Map<K, Long> 分组后的map,value为计数;若源空,返回空HashMap
*/
public static <K, V> Map<K, Long> groupToMapCount(Collection<V> source, Function<? super V, ? extends K> keyFunction) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.filter(s -> Objects.nonNull(keyFunction.apply(s)))
.collect(Collectors.groupingBy(keyFunction, Collectors.counting()));
}
/**
* 过滤集合,返回List。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate 过滤条件(Predicate<? super T>),保留满足条件的元素
* @return List<T> 过滤后的List;若源空,返回空ArrayList
*/
public static <T> List<T> filter(Collection<T> source, Predicate<? super T> predicate) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newArrayList();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate)
.collect(Collectors.toList());
}
/**
* 过滤Set,返回Set。
*
* @param set 源Set(Set<T>),输入数据源
* @param predicate 过滤条件(Predicate<? super T>),保留满足条件的元素
* @return Set<T> 过滤后的HashSet;若源空,返回空HashSet
*/
public static <T> Set<T> filter(Set<T> set, Predicate<? super T> predicate) {
if (CollectionUtils.isEmpty(set)) {
return Sets.newHashSet();
}
return set.stream()
.filter(Objects::nonNull)
.filter(predicate)
.collect(Collectors.toCollection(HashSet::new));
}
/**
* 过滤集合,返回LinkedHashSet。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate 过滤条件(Predicate<? super T>),保留满足条件的元素
* @return LinkedHashSet<T> 过滤后的LinkedHashSet,保持顺序;若源空,返回空LinkedHashSet
*/
public static <T> LinkedHashSet<T> filterToSet(Collection<T> source, Predicate<? super T> predicate) {
if (CollectionUtils.isEmpty(source)) {
return Sets.newLinkedHashSet();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
* 过滤集合,返回Stream。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate 过滤条件(Predicate<? super T>),保留满足条件的元素
* @return Stream<T> 过滤后的Stream;若源空,返回空Stream
*/
public static <T> Stream<T> filterStream(Collection<T> source, Predicate<? super T> predicate) {
if (CollectionUtils.isEmpty(source)) {
return Stream.empty();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate);
}
/**
* 过滤集合,返回Stream(双过滤条件)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate1 第一过滤条件(Predicate<? super T>),第一个过滤
* @param predicate2 第二过滤条件(Predicate<? super T>),第二个过滤
* @return Stream<T> 过滤后的Stream;若源空,返回空Stream
*/
public static <T> Stream<T> filterStream(Collection<T> source, Predicate<? super T> predicate1, Predicate<? super T> predicate2) {
if (CollectionUtils.isEmpty(source)) {
return Stream.empty();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate1)
.filter(predicate2);
}
/**
* 过滤并映射,返回List。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate 过滤条件(Predicate<? super T>),保留满足条件的元素
* @param mapFunction 映射函数(Function<T, R>),转换每个元素
* @return List<R> 过滤映射后的List,可能包含null;若源空,返回空ArrayList
*/
public static <T, R> List<R> filterMapList(Collection<T> source, Predicate<? super T> predicate, Function<T, R> mapFunction) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newArrayList();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate)
.map(mapFunction)
.collect(Collectors.toList());
}
/**
* 过滤并映射,返回List(双过滤条件)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate1 第一过滤条件(Predicate<? super T>),第一个过滤
* @param predicate2 第二过滤条件(Predicate<? super T>),第二个过滤
* @param mapFunction 映射函数(Function<T, R>),转换每个元素
* @return List<R> 过滤映射后的List,可能包含null;若源空,返回空ArrayList
*/
public static <T, R> List<R> filterMapList(Collection<T> source, Predicate<? super T> predicate1, Predicate<? super T> predicate2, Function<T, R> mapFunction) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newArrayList();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate1)
.filter(predicate2)
.map(mapFunction)
.collect(Collectors.toList());
}
/**
* 过滤并映射,返回Set。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate 过滤条件(Predicate<? super T>),保留满足条件的元素
* @param mapFunction 映射函数(Function<T, R>),转换每个元素
* @return Set<R> 过滤映射后的HashSet,可能包含null;若源空,返回空HashSet
*/
public static <T, R> Set<R> filterMapSet(Collection<T> source, Predicate<? super T> predicate, Function<T, R> mapFunction) {
if (CollectionUtils.isEmpty(source)) {
return Sets.newHashSet();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate)
.map(mapFunction)
.collect(Collectors.toSet());
}
/**
* 过滤并映射,返回Set(双过滤条件)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate1 第一过滤条件(Predicate<? super T>),第一个过滤
* @param predicate2 第二过滤条件(Predicate<? super T>),第二个过滤
* @param mapFunction 映射函数(Function<T, R>),转换每个元素
* @return Set<R> 过滤映射后的HashSet,可能包含null;若源空,返回空HashSet
*/
public static <T, R> Set<R> filterMapSet(Collection<T> source, Predicate<? super T> predicate1, Predicate<? super T> predicate2, Function<T, R> mapFunction) {
if (CollectionUtils.isEmpty(source)) {
return Sets.newHashSet();
}
return source.stream()
.filter(Objects::nonNull)
.filter(predicate1)
.filter(predicate2)
.map(mapFunction)
.collect(Collectors.toSet());
}
/**
* 从eList中设置字段值到vList中(优化:检查v key null)。
*
* @param vList v对象集合(Collection<V>),目标集合,需要设置值的对象
* @param eList e对象集合(Collection<E>),源集合,提供值的对象
* @param keyFunctionE e对象获取key方法(Function<E, R>),从e提取key
* @param keyFunctionV v对象获取key方法(Function<V, R>),从v提取key
* @param consumer 赋值转换方法(BiConsumer<V, E>),设置v字段的逻辑(v, e)
*/
public static <V, E, R> void setVFromE(Collection<V> vList, Collection<E> eList, Function<E, R> keyFunctionE,
Function<V, R> keyFunctionV,
BiConsumer<V, E> consumer) {
if (CollectionUtils.isNotEmpty(eList)) {
Map<R, E> map = StreamUtils.toMap(eList, keyFunctionE);
vList.stream()
.filter(Objects::nonNull)
.filter(v -> Objects.nonNull(keyFunctionV.apply(v))) // 新增:防v key null
.forEach(v -> {
E e = map.get(keyFunctionV.apply(v));
if (Objects.nonNull(e)) {
consumer.accept(v, e);
}
});
}
}
/**
* 从eList中设置集合项字段值到vList中的集合项属性(优化:检查v key null)。
*
* @param vList v对象集合(Collection<V>),目标集合,需要设置值的对象
* @param eList e对象集合(Collection<E>),源集合,提供值的对象
* @param keyFunctionE e对象获取key方法(Function<E, R>),从e提取key
* @param keyFunctionV v对象获取key方法(Function<V, R>),从v提取key
* @param consumer 赋值转换方法(BiConsumer<V, List<E>>),设置v集合字段的逻辑(v, List<e>)
*/
public static <V, E, R> void setVListFromE(Collection<V> vList, Collection<E> eList, Function<E, R> keyFunctionE,
Function<V, R> keyFunctionV,
BiConsumer<V, List<E>> consumer) {
if (CollectionUtils.isNotEmpty(eList)) {
Map<R, List<E>> map = StreamUtils.groupToMap(eList, keyFunctionE);
vList.stream()
.filter(Objects::nonNull)
.filter(v -> Objects.nonNull(keyFunctionV.apply(v))) // 新增:防v key null
.forEach(v -> {
List<E> e = map.get(keyFunctionV.apply(v));
if (Objects.nonNull(e)) {
consumer.accept(v, e);
}
});
}
}
/**
* 累加(优化:过滤value null,用Optional防NPE)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param valueFunction 获取BigDecimal字段(Function<T, BigDecimal>),提取数值
* @return BigDecimal 所有值的累加和;若源空或无有效值,返回BigDecimal.ZERO
* @param <T> 源元素类型
*/
public static <T> BigDecimal add(Collection<T> source, Function<T, BigDecimal> valueFunction) {
if (CollectionUtils.isEmpty(source)) {
return BigDecimal.ZERO;
}
return source.stream()
.filter(Objects::nonNull)
.map(valueFunction)
.filter(Objects::nonNull) // 新增:防value null
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
/**
* 累加并设置四舍五入(保留newScale位小数)(优化:过滤value null)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param valueFunction 获取BigDecimal字段(Function<T, BigDecimal>),提取数值
* @param newScale 小数位数(int),保留位数
* @return BigDecimal 累加和,四舍五入后结果;若源空或无有效值,返回BigDecimal.ZERO
* @param <T> 源元素类型
*/
public static <T> BigDecimal addHalfUp(Collection<T> source, Function<T, BigDecimal> valueFunction, int newScale) {
if (CollectionUtils.isEmpty(source)) {
return BigDecimal.ZERO;
}
return add(source, valueFunction).setScale(newScale, RoundingMode.HALF_UP);
}
/**
* 累加并设置四舍五入(保留2位小数)(优化:过滤value null)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param valueFunction 获取BigDecimal字段(Function<T, BigDecimal>),提取数值
* @return BigDecimal 累加和,四舍五入后结果(2位);若源空或无有效值,返回BigDecimal.ZERO
* @param <T> 源元素类型
*/
public static <T> BigDecimal addHalfUp(Collection<T> source, Function<T, BigDecimal> valueFunction) {
return addHalfUp(source, valueFunction, 2);
}
/**
* 计算平均值(优化:过滤value null,四舍五入)。
*
* @param source 源集合(Collection<T>),输入数据源
* @param valueFunction 获取BigDecimal字段(Function<T, BigDecimal>),提取数值
* @param scale 小数位数(int),保留位数
* @return BigDecimal 平均值,四舍五入后结果;若源空或无有效值,返回BigDecimal.ZERO
* @param <T> 源元素类型
*/
public static <T> BigDecimal average(Collection<T> source, Function<T, BigDecimal> valueFunction, int scale) {
if (CollectionUtils.isEmpty(source)) {
return BigDecimal.ZERO;
}
BigDecimal sum = add(source, valueFunction);
long count = source.stream()
.filter(Objects::nonNull)
.filter(t -> Objects.nonNull(valueFunction.apply(t))) // 一致过滤
.count();
return count == 0 ? BigDecimal.ZERO : sum.divide(BigDecimal.valueOf(count), scale, RoundingMode.HALF_UP);
}
/**
* 转换为排序列表。
*
* @param source 源集合(Collection<T>),输入数据源
* @param comparator 比较器(Comparator<? super T>),排序规则
* @return List<T> 排序后的List;若源空,返回空ArrayList
* @param <T> 元素类型
*/
public static <T> List<T> toSortedList(Collection<T> source, Comparator<? super T> comparator) {
if (CollectionUtils.isEmpty(source)) {
return Lists.newArrayList();
}
return source.stream()
.filter(Objects::nonNull)
.sorted(comparator)
.collect(Collectors.toList());
}
/**
* 根据predicate分区:true/false -> List。
*
* @param source 源集合(Collection<T>),输入数据源
* @param predicate 分区条件(Predicate<? super T>),true/false分组
* @return Map<Boolean, List<T>> 分区map,key为Boolean,value为对应List;若源空,返回空HashMap
* @param <T> 元素类型
*/
public static <T> Map<Boolean, List<T>> partitionToMap(Collection<T> source, Predicate<? super T> predicate) {
if (CollectionUtils.isEmpty(source)) {
return Maps.newHashMap();
}
return source.stream()
.filter(Objects::nonNull)
.collect(Collectors.partitioningBy(predicate));
}
/**
* 根据key去重(返回Predicate)。
*
* @param keyExtractor 提取key函数(Function<? super T, K>),用于去重判断
* @return Predicate<T> 去重过滤器
* @param <T> 元素类型
* @param <K> key类型
*/
public static <T, K> Predicate<T> distinctByKey(Function<? super T, K> keyExtractor) {
Set<K> seen = new HashSet<>(); // 优化:单线程用HashSet,减少开销
return t -> {
K key = keyExtractor.apply(t);
if (Objects.isNull(key)) {
return false; // 新增:忽略key null
}
return seen.add(key);
};
}
/**
* 根据key去重,返回List。
*
* @param list 源List(List<T>),输入数据源
* @param keyExtractor 提取key函数(Function<? super T, K>),用于去重判断
* @return List<T> 去重后的List,保留首次出现的元素;若源空,返回空List
* @param <T> 元素类型
* @param <K> key类型
*/
public static <T, K> List<T> distinctByKey(List<T> list, Function<? super T, K> keyExtractor) {
return list.stream()
.filter(distinctByKey(keyExtractor))
.collect(Collectors.toList());
}
}
更多推荐
所有评论(0)