From 330b23dfa8e45dc2f2c59cf7a8f5cb749248bb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=80=E9=B8=A3?= Date: Fri, 4 Sep 2020 14:23:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0stream=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/hutool/core/util/StreamUtil.java | 175 +++++++++++++++++ .../cn/hutool/core/util/StreamUtilTest.java | 179 ++++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 hutool-core/src/main/java/cn/hutool/core/util/StreamUtil.java create mode 100644 hutool-core/src/test/java/cn/hutool/core/util/StreamUtilTest.java diff --git a/hutool-core/src/main/java/cn/hutool/core/util/StreamUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/StreamUtil.java new file mode 100644 index 000000000..c97d2fa64 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/util/StreamUtil.java @@ -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工具类 + *
+ *  在实际项目中,我们经常需要处理集合对象, 提取集合元素的某个字段集合。
+ *  此工具类可以简化一些代码, 如
+ *  原来需要提取User对象的id集合:
+ *      1 - 写一个for循环
+ *          {@code
+ *          List userIds = new ArrayList<>();
+ *          for(User user : userList) {
+ *              userIds.add(user.getId());
+ *          }
+ *          }
+ *      2 - 使用java8 引入的stream方式
+ *          {@code List userIds = userList.stream().map(User::getId).collect(Collectors.toList());}
+ *
+ *      3 - 此工具类提供了更简洁的stream写法(无需每次都写{@code collect(Collectors.toList())})
+ *          {@code List userId = StreamUtil.list(userList, User::getId);}
+ * 
+ * + * @author yiming + */ +public class StreamUtil { + + /** + * 将指定List元素的某个field提取成新的List(过滤null元素) + * + * @param sourceList 原list + * @param mapperFunction 映射方法 + * @return 转化后的list + */ + public static List list(List sourceList, Function 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 源数据类型 + * @param 结果数据类型 + * @return 转化后的列表数据 + */ + public static List list(List sourceList, Function mapperFunction, Predicate 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 Set toSet(List sourceList, Function mapperFunction) { + return new HashSet<>(list(sourceList, mapperFunction)); + } + + /** + * 将指定List元素的某个field提取成新的Set(过滤null元素) + * + * @param sourceList 原list + * @param mapperFunction 映射方法 + * @param filter 过滤器 + * @return 转化后的set + */ + public static Set toSet(List sourceList, Function mapperFunction, Predicate filter) { + return new HashSet<>(list(sourceList, mapperFunction, filter)); + } + + /** + * list 转 map + * + * @param sourceList 源数据 + * @param keyMapper key映射 + * @param valueMapper value映射 + * @param mergeFunction value合并策略 + * @param 对象集合类型 + * @param map键类型 + * @param map值类型 + * @return 由list转化而的map + */ + public static Map toMap(List sourceList, Function keyMapper, Function valueMapper, + BinaryOperator 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 Map toMap(List sourceList, Function keyMapper, Function 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 Map toMap(List sourceList, Function keyMapper, Function valueMapper, + Predicate 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 对象集合类型 + * @param map键类型 + * @return 由list转化而来的map + */ + public static Map toMap(List sourceList, Function keyMapper) { + return toMap(sourceList, keyMapper, Function.identity(), (v1, v2) -> v1); + } + + /** + * 按指定字段分组 + * + * @param sourceList 原list + * @param keyMapper 字段映射 + * @return 分组 + */ + public static Map> groupingBy(List sourceList, Function keyMapper) { + if (CollUtil.isEmpty(sourceList) || keyMapper == null) { + return Collections.emptyMap(); + } + return sourceList.stream().collect(Collectors.groupingBy(keyMapper)); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/util/StreamUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/StreamUtilTest.java new file mode 100644 index 000000000..c29cded6b --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/util/StreamUtilTest.java @@ -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 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 userIds = StreamUtil.list(userList, User::getId); + Assert.assertEquals(userIds.size(), userList.size()); + } + + @Test + public void testListFilter() { + // 提取管理员用户名称 + List names = StreamUtil.list(userList, User::getName, x -> Boolean.TRUE.equals(x.isAdmin)); + Assert.assertEquals(names.size(), 4); + } + + @Test + public void testToSet() { + Set ages = StreamUtil.toSet(userList, User::getAge); + Assert.assertEquals(ages.size(), 6); + } + + @Test + public void testToSetFilter() { + // 提取大于6的元素 + Set ages = StreamUtil.toSet(userList, User::getAge, x -> x.getAge() > 6); + Assert.assertEquals(ages.size(), 2); + } + + @Test + public void testToMap() { + Map userMap = StreamUtil.toMap(userList, User::getId); + Assert.assertEquals(userMap.size(), userList.size()); + } + + @Test + public void testToMapValue() { + Map userNameMap = StreamUtil.toMap(userList, User::getId, User::getName); + Assert.assertEquals(userNameMap.size(), userList.size()); + } + + @Test + public void testToMapFilter() { + // 提取年龄>5的用户姓名map + Map 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 users = new ArrayList<>(); + users.add(tempUser1); + users.add(tempUser2); + // 若新的value同原value,取原value + Map 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> 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 + "]"; + } + } +} \ No newline at end of file