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 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 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 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 对象集合类型
+ * @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