Merge pull request #1065 from promise2mm/v5-dev

添加stream操作工具类
This commit is contained in:
Golden Looly 2020-09-04 15:13:54 +08:00 committed by GitHub
commit 5dfd34de3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 354 additions and 0 deletions

View File

@ -0,0 +1,175 @@
package cn.hutool.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* java Stream工具类
* <pre>
* 在实际项目中我们经常需要处理集合对象, 提取集合元素的某个字段集合
* 此工具类可以简化一些代码,
* 原来需要提取User对象的id集合
* 1 - 写一个for循环
* {@code
* List<Long> userIds = new ArrayList<>();
* for(User user : userList) {
* userIds.add(user.getId());
* }
* }
* 2 - 使用java8 引入的stream方式
* {@code List<Long> userIds = userList.stream().map(User::getId).collect(Collectors.toList());}
*
* 3 - 此工具类提供了更简洁的stream写法(无需每次都写{@code collect(Collectors.toList())})
* {@code List<Long> userId = StreamUtil.list(userList, User::getId);}
* </pre>
*
* @author yiming
*/
public class StreamUtil {
/**
* 将指定List元素的某个field提取成新的List(过滤null元素)
*
* @param sourceList 原list
* @param mapperFunction 映射方法
* @return 转化后的list
*/
public static <T, R> List<R> list(List<T> sourceList, Function<T, R> mapperFunction) {
if (CollUtil.isEmpty(sourceList)) {
return Collections.emptyList();
}
Assert.notNull(mapperFunction);
return sourceList.stream().map(mapperFunction).filter(Objects::nonNull).distinct().collect(Collectors.toList());
}
/**
* 获取对象列表的某个字段list
*
* @param sourceList 源数据
* @param mapperFunction 映射方法
* @param filter 过滤器
* @param <T> 源数据类型
* @param <R> 结果数据类型
* @return 转化后的列表数据
*/
public static <T, R> List<R> list(List<T> sourceList, Function<T, R> mapperFunction, Predicate<? super T> filter) {
if (CollUtil.isEmpty(sourceList)) {
return Collections.emptyList();
}
Assert.notNull(mapperFunction);
Assert.notNull(filter);
return sourceList.stream().filter(filter).map(mapperFunction).distinct().collect(Collectors.toList());
}
/**
* 将指定List元素的某个field提取成新的Set(过滤null元素)
*
* @param sourceList 原list
* @param mapperFunction 映射方法
* @return 转化后的set
*/
public static <T, R> Set<R> toSet(List<T> sourceList, Function<T, R> mapperFunction) {
return new HashSet<>(list(sourceList, mapperFunction));
}
/**
* 将指定List元素的某个field提取成新的Set(过滤null元素)
*
* @param sourceList 原list
* @param mapperFunction 映射方法
* @param filter 过滤器
* @return 转化后的set
*/
public static <T, R> Set<R> toSet(List<T> sourceList, Function<T, R> mapperFunction, Predicate<T> filter) {
return new HashSet<>(list(sourceList, mapperFunction, filter));
}
/**
* list map
*
* @param sourceList 源数据
* @param keyMapper key映射
* @param valueMapper value映射
* @param mergeFunction value合并策略
* @param <T> 对象集合类型
* @param <K> map键类型
* @param <R> map值类型
* @return 由list转化而的map
*/
public static <T, K, R> Map<K, R> toMap(List<T> sourceList, Function<T, K> keyMapper, Function<T, R> valueMapper,
BinaryOperator<R> mergeFunction) {
if (CollUtil.isEmpty(sourceList) || keyMapper == null || valueMapper == null) {
return Collections.emptyMap();
}
return sourceList.stream().collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction));
}
/**
* 提取list指定字段转成map
*
* @param sourceList 原list
* @param keyMapper key映射方法
* @param valueMapper value映射方法
* @return 指定map
*/
public static <T, K, R> Map<K, R> toMap(List<T> sourceList, Function<T, K> keyMapper, Function<T, R> valueMapper) {
if (CollUtil.isEmpty(sourceList) || keyMapper == null || valueMapper == null) {
return Collections.emptyMap();
}
return sourceList.stream().collect(Collectors.toMap(keyMapper, valueMapper, (v1, v2) -> v1));
}
/**
* 提取list指定字段转成map
*
* @param sourceList 原list
* @param keyMapper key映射方法
* @param valueMapper value映射方法
* @param filter 过滤器
* @return 指定map
*/
public static <T, K, R> Map<K, R> toMap(List<T> sourceList, Function<T, K> keyMapper, Function<T, R> valueMapper,
Predicate<? super T> filter) {
if (CollUtil.isEmpty(sourceList) || keyMapper == null || valueMapper == null) {
return Collections.emptyMap();
}
return sourceList.stream().filter(filter).collect(Collectors.toMap(keyMapper, valueMapper, (v1, v2) -> v1));
}
/**
* list -> map (map值类型同list元素类型)
*
* @param sourceList 源数据
* @param keyMapper key映射
* @param <T> 对象集合类型
* @param <K> map键类型
* @return 由list转化而来的map
*/
public static <T, K> Map<K, T> toMap(List<T> sourceList, Function<T, K> keyMapper) {
return toMap(sourceList, keyMapper, Function.identity(), (v1, v2) -> v1);
}
/**
* 按指定字段分组
*
* @param sourceList 原list
* @param keyMapper 字段映射
* @return 分组
*/
public static <T, K> Map<K, List<T>> groupingBy(List<T> sourceList, Function<T, K> keyMapper) {
if (CollUtil.isEmpty(sourceList) || keyMapper == null) {
return Collections.emptyMap();
}
return sourceList.stream().collect(Collectors.groupingBy(keyMapper));
}
}

View File

@ -0,0 +1,179 @@
package cn.hutool.core.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.Test;
/**
* @author yiming
* @date 2020/9/4 13:40
*/
public class StreamUtilTest {
static List<User> userList = Arrays.asList(
new User(1L, "A", 3, true, true, true),
new User(2L, "B", 4, true, true, false),
new User(3L, "C", 5, true, false, true),
new User(4L, "E", 6, true, false, false),
new User(5L, "D", 7, false, true, true),
new User(6L, "F", 7, false, false, true),
new User(7L, "G", 8, false, true, false),
new User(8L, "H", 8, false, false, false)
);
@Test
public void testList() {
List<Long> userIds = StreamUtil.list(userList, User::getId);
Assert.assertEquals(userIds.size(), userList.size());
}
@Test
public void testListFilter() {
// 提取管理员用户名称
List<String> names = StreamUtil.list(userList, User::getName, x -> Boolean.TRUE.equals(x.isAdmin));
Assert.assertEquals(names.size(), 4);
}
@Test
public void testToSet() {
Set<Integer> ages = StreamUtil.toSet(userList, User::getAge);
Assert.assertEquals(ages.size(), 6);
}
@Test
public void testToSetFilter() {
// 提取大于6的元素
Set<Integer> ages = StreamUtil.toSet(userList, User::getAge, x -> x.getAge() > 6);
Assert.assertEquals(ages.size(), 2);
}
@Test
public void testToMap() {
Map<Long, User> userMap = StreamUtil.toMap(userList, User::getId);
Assert.assertEquals(userMap.size(), userList.size());
}
@Test
public void testToMapValue() {
Map<Long, String> userNameMap = StreamUtil.toMap(userList, User::getId, User::getName);
Assert.assertEquals(userNameMap.size(), userList.size());
}
@Test
public void testToMapFilter() {
// 提取年龄>5的用户姓名map
Map<Long, String> userNameMap = StreamUtil.toMap(userList, User::getId, User::getName, x -> x.getAge() > 5);
Assert.assertEquals(userNameMap.size(), 5);
}
@Test
public void testToMapMerge() {
// 提取用户姓名map - 自定义map的value覆盖策略
User tempUser1 = new User(10L, "X", 10, true, false, false);
User tempUser2 = new User(10L, "Y", 10, true, false, false);
List<User> users = new ArrayList<>();
users.add(tempUser1);
users.add(tempUser2);
// 若新的value同原value取原value
Map<Long, String> userNameMap = StreamUtil.toMap(users, User::getId, User::getName, (v1, v2) -> v1);
Assert.assertEquals(userNameMap.get(10L), "X");
// 若新的value同原value取新value
userNameMap = StreamUtil.toMap(users, User::getId, User::getName, (v1, v2) -> v2);
Assert.assertEquals(userNameMap.get(10L), "Y");
}
@Test
public void groupingBy() {
// 按年龄分组
Map<Integer, List<User>> ageGroupMap = StreamUtil.groupingBy(userList, User::getAge);
Assert.assertEquals(ageGroupMap.size(), 6);
}
/**
* 测试bean
*/
public static class User {
private Long id;
private String name;
private int age;
private boolean isAdmin;
private boolean isSuper;
private boolean gender;
public User() {
}
public User(Long id, String name, int age, boolean isAdmin, boolean isSuper, boolean gender) {
this.id = id;
this.name = name;
this.age = age;
this.isAdmin = isAdmin;
this.isSuper = isSuper;
this.gender = gender;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public User setAge(int age) {
this.age = age;
return this;
}
public String testMethod() {
return "test for " + this.name;
}
public boolean isAdmin() {
return isAdmin;
}
public void setAdmin(boolean isAdmin) {
this.isAdmin = isAdmin;
}
public boolean isIsSuper() {
return isSuper;
}
public void setIsSuper(boolean isSuper) {
this.isSuper = isSuper;
}
public boolean isGender() {
return gender;
}
public void setGender(boolean gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", isAdmin=" + isAdmin + ", gender=" + gender + "]";
}
}
}