diff --git a/hutool-core/src/main/java/cn/hutool/core/collection/CollectionStream.java b/hutool-core/src/main/java/cn/hutool/core/collection/CollectionStream.java
new file mode 100644
index 000000000..a4dd0f567
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/collection/CollectionStream.java
@@ -0,0 +1,202 @@
+package cn.hutool.core.collection;
+
+
+import cn.hutool.core.map.MapUtil;
+
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 常用stream操作集合
+ * 做这个集合的封装的原因在于list.stream后续的写法虽然简便,但是别人看不易不懂,甚至自己写了两周后都看不懂,维护成本高
+ * 于是笔者采用见名知意的方法封装,降低阅读难度,减轻了维护成本,使用者只需要提供一些简单的lambda表达式
+ * 当然本人才疏学浅,名字未必能够做到信雅达,欢迎各位大佬指教
+ * 另外,CollectionStream封装的都是一些常用的stream方法,当然考虑到一些方法不常用(比如map->list),并未提交,如果有一些好的意见,欢迎联系本人528910437@QQ.COM
+ *
+ * @version 1.0
+ */
+public class CollectionStream {
+ /**
+ * 将collection转化为类型不变的map
+ *
+ * {@code Collection ----> Map}
+ *
+ * @param collection 需要转化的集合
+ * @param key E类型转化为T类型的lambda方法
+ * @param collection中的泛型
+ * @param map中的key类型
+ * @return 转化后的map
+ */
+ public static Map toIdentityMap(Collection collection, Function key) {
+ if (CollUtil.isEmpty(collection)) {
+ return Collections.EMPTY_MAP;
+ }
+ return collection.stream().collect(Collectors.toMap(key, Function.identity()));
+ }
+
+ /**
+ * E
+ * 将collection转化为map(value类型与collection的泛型不同)
+ *
+ * {@code Collection -----> Map }
+ *
+ * @param collection 需要转化的集合
+ * @param key E类型转化为T类型的lambda方法
+ * @param value E类型转化为U类型的lambda方法
+ * @param collection中的泛型
+ * @param map中的key类型
+ * @param map中的value类型
+ * @return 转化后的map
+ */
+ public static Map toMap(Collection collection, Function key, Function value) {
+ if (CollUtil.isEmpty(collection)) {
+ return Collections.EMPTY_MAP;
+ }
+ return collection.stream().collect(Collectors.toMap(key, value));
+ }
+
+ /**
+ * 将collection按照规则(比如有相同的班级id)分类成map
+ *
+ * {@code Collection -------> Map> }
+ *
+ * @param collection 需要分类的集合
+ * @param key 分类的规则
+ * @param collection中的泛型
+ * @param map中的key类型
+ * @return 分类后的map
+ */
+ public static Map> groupByKey(Collection collection, Function key) {
+ if (CollUtil.isEmpty(collection)) {
+ return Collections.EMPTY_MAP;
+ }
+ return collection
+ .stream()
+ .collect(Collectors.groupingBy(key, Collectors.toList()));
+ }
+
+ /**
+ * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ *
+ * {@code Collection ---> Map>> }
+ *
+ * @param collection 需要分类的集合
+ * @param key1 第一个分类的规则
+ * @param key2 第二个分类的规则
+ * @param 第一个map中的key类型
+ * @param 第二个map中的key类型
+ * @return 分类后的map
+ */
+ public static Map>> groupBy2Key(Collection collection, Function key1, Function key2) {
+ if (CollUtil.isEmpty(collection)) {
+ return Collections.EMPTY_MAP;
+ }
+ return collection
+ .stream()
+ .collect(Collectors.groupingBy(key1, Collectors.groupingBy(key2, Collectors.toList())));
+ }
+
+ /**
+ * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map
+ *
+ * {@code Collection ---> Map> }
+ *
+ * @param collection 需要分类的集合
+ * @param key1 第一个分类的规则
+ * @param key2 第二个分类的规则
+ * @param 第一个map中的key类型
+ * @param 第二个map中的key类型
+ * @param collection中的泛型
+ * @return 分类后的map
+ */
+ public static Map> group2Map(Collection collection, Function key1, Function key2) {
+ if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
+ return Collections.EMPTY_MAP;
+ }
+ return collection
+ .stream()
+ .collect(Collectors.groupingBy(key1, Collectors.toMap(key2, Function.identity())));
+ }
+
+ /**
+ * 将collection转化为List集合,但是两者的泛型不同
+ *
+ * {@code Collection ------> List }
+ *
+ * @param collection 需要转化的集合
+ * @param function collection中的泛型转化为list泛型的lambda表达式
+ * @param collection中的泛型
+ * @param List中的泛型
+ * @return 转化后的list
+ */
+ public static List translate2List(Collection collection, Function function) {
+ if (CollUtil.isEmpty(collection)) {
+ return Collections.EMPTY_LIST;
+ }
+ return collection
+ .stream()
+ .map(function)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 将collection转化为Set集合,但是两者的泛型不同
+ *
+ * {@code Collection ------> Set }
+ *
+ * @param collection 需要转化的集合
+ * @param function collection中的泛型转化为set泛型的lambda表达式
+ * @param collection中的泛型
+ * @param Set中的泛型
+ * @return 转化后的Set
+ */
+ public static Set translate2Set(Collection collection, Function function) {
+ if (CollUtil.isEmpty(collection) || function == null) {
+ return Collections.EMPTY_SET;
+ }
+ return collection
+ .stream()
+ .map(function)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ }
+
+
+ /**
+ * 合并两个相同key类型的map
+ *
+ * @param map1 第一个需要合并的 map
+ * @param map2 第二个需要合并的 map
+ * @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况
+ * @param map中的key类型
+ * @param 第一个 map的value类型
+ * @param 第二个 map的value类型
+ * @param 最终map的value类型
+ * @return 合并后的map
+ */
+ public static Map merge(Map map1, Map map2, BiFunction merge) {
+ if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
+ return Collections.EMPTY_MAP;
+ } else if (MapUtil.isEmpty(map1)) {
+ map1 = Collections.EMPTY_MAP;
+ } else if (MapUtil.isEmpty(map2)) {
+ map2 = Collections.EMPTY_MAP;
+ }
+ Set key = new HashSet<>();
+ key.addAll(map1.keySet());
+ key.addAll(map2.keySet());
+ Map map = new HashMap<>();
+ for (T t : key) {
+ X x = map1.get(t);
+ Y y = map2.get(t);
+ Z z = merge.apply(x, y);
+ if (z != null) {
+ map.put(t, z);
+ }
+ }
+ return map;
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/collection/CollectionStreamTest.java b/hutool-core/src/test/java/cn/hutool/core/collection/CollectionStreamTest.java
new file mode 100644
index 000000000..c3a72d053
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/collection/CollectionStreamTest.java
@@ -0,0 +1,276 @@
+package cn.hutool.core.collection;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.*;
+
+/**
+ * CollectionStream测试方法
+ */
+public class CollectionStreamTest {
+ @Test
+ public void testToIdentityMap() {
+ Map map = CollectionStream.toIdentityMap(null, Student::getStudentId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ List list = new ArrayList<>();
+ map = CollectionStream.toIdentityMap(list, Student::getStudentId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ list.add(new Student(1, 1, 1, "张三"));
+ list.add(new Student(1, 1, 2, "李四"));
+ list.add(new Student(1, 1, 3, "王五"));
+ map = CollectionStream.toIdentityMap(list, Student::getStudentId);
+ Assert.assertEquals(map.get(1L).getName(), "张三");
+ Assert.assertEquals(map.get(2L).getName(), "李四");
+ Assert.assertEquals(map.get(3L).getName(), "王五");
+ Assert.assertEquals(map.get(4L), null);
+ }
+
+ @Test
+ public void testToMap() {
+ Map map = CollectionStream.toMap(null, Student::getStudentId, Student::getName);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ List list = new ArrayList<>();
+ map = CollectionStream.toMap(list, Student::getStudentId, Student::getName);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ list.add(new Student(1, 1, 1, "张三"));
+ list.add(new Student(1, 1, 2, "李四"));
+ list.add(new Student(1, 1, 3, "王五"));
+ map = CollectionStream.toMap(list, Student::getStudentId, Student::getName);
+ Assert.assertEquals(map.get(1L), "张三");
+ Assert.assertEquals(map.get(2L), "李四");
+ Assert.assertEquals(map.get(3L), "王五");
+ Assert.assertEquals(map.get(4L), null);
+ }
+
+ @Test
+ public void testGroupByKey() {
+ Map> map = CollectionStream.groupByKey(null, Student::getClassId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ List list = new ArrayList<>();
+ map = CollectionStream.groupByKey(list, Student::getClassId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ list.add(new Student(1, 1, 1, "张三"));
+ list.add(new Student(1, 2, 2, "李四"));
+ list.add(new Student(2, 1, 1, "擎天柱"));
+ list.add(new Student(2, 2, 2, "威震天"));
+ list.add(new Student(2, 3, 2, "霸天虎"));
+ map = CollectionStream.groupByKey(list, Student::getClassId);
+ Map> compare = new HashMap<>();
+ List class1 = new ArrayList<>();
+ class1.add(new Student(1, 1, 1, "张三"));
+ class1.add(new Student(2, 1, 1, "擎天柱"));
+ compare.put(1L, class1);
+ List class2 = new ArrayList<>();
+ class2.add(new Student(1, 2, 2, "李四"));
+ class2.add(new Student(2, 2, 2, "威震天"));
+
+ compare.put(2L, class2);
+ List class3 = new ArrayList<>();
+ class3.add(new Student(2, 3, 2, "霸天虎"));
+ compare.put(3L, class3);
+ Assert.assertEquals(true, map.equals(compare));
+ }
+
+ public void testGroupBy2Key() {
+ Map>> map = CollectionStream.groupBy2Key(null, Student::getTermId, Student::getClassId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ List list = new ArrayList<>();
+ map = CollectionStream.groupBy2Key(list, Student::getTermId, Student::getClassId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ list.add(new Student(1, 1, 1, "张三"));
+ list.add(new Student(1, 2, 2, "李四"));
+ list.add(new Student(1, 2, 3, "王五"));
+ list.add(new Student(2, 1, 1, "擎天柱"));
+ list.add(new Student(2, 2, 2, "威震天"));
+ list.add(new Student(2, 2, 3, "霸天虎"));
+ map = CollectionStream.groupBy2Key(list, Student::getTermId, Student::getClassId);
+ Map>> compare = new HashMap<>();
+ Map> map1 = new HashMap<>();
+ List list11 = new ArrayList<>();
+ list11.add(new Student(1, 1, 1, "张三"));
+ map1.put(1L, list11);
+ compare.put(1L, map1);
+ List list12 = new ArrayList<>();
+ list12.add(new Student(1, 2, 2, "李四"));
+ list12.add(new Student(1, 2, 3, "王五"));
+ map1.put(2L, list12);
+ compare.put(2L, map1);
+ Map> map2 = new HashMap<>();
+ List list21 = new ArrayList<>();
+ list21.add(new Student(2, 1, 1, "擎天柱"));
+ map2.put(1L, list21);
+ compare.put(2L, map2);
+
+ List list22 = new ArrayList<>();
+ list22.add(new Student(2, 2, 2, "威震天"));
+ list22.add(new Student(2, 2, 3, "霸天虎"));
+ map2.put(2L, list22);
+ compare.put(2L, map2);
+ Assert.assertEquals(true, map.equals(compare));
+ }
+
+ @Test
+ public void testGroup2Map() {
+ List list = null;
+ Map> map = CollectionStream.group2Map(list, Student::getTermId, Student::getClassId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ list = new ArrayList<>();
+ map = CollectionStream.group2Map(list, Student::getTermId, Student::getClassId);
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ list.add(new Student(1, 1, 1, "张三"));
+ list.add(new Student(1, 2, 1, "李四"));
+ list.add(new Student(2, 2, 1, "王五"));
+ map = CollectionStream.group2Map(list, Student::getTermId, Student::getClassId);
+ Map> compare = new HashMap<>();
+ Map map1 = new HashMap<>();
+ map1.put(1L, new Student(1, 1, 1, "张三"));
+ map1.put(2L, new Student(1, 2, 1, "李四"));
+ compare.put(1L, map1);
+ Map map2 = new HashMap<>();
+ map2.put(2L, new Student(2, 2, 1, "王五"));
+ compare.put(2L, map2);
+ Assert.assertEquals(true,compare.equals(map));
+
+ }
+
+ @Test
+ public void testTranslate2List() {
+ List list = CollectionStream.translate2List(null, Student::getName);
+ Assert.assertEquals(list, Collections.EMPTY_LIST);
+ List students = new ArrayList<>();
+ list = CollectionStream.translate2List(students, Student::getName);
+ Assert.assertEquals(list, Collections.EMPTY_LIST);
+ students.add(new Student(1, 1, 1, "张三"));
+ students.add(new Student(1, 2, 2, "李四"));
+ students.add(new Student(2, 1, 1, "李四"));
+ students.add(new Student(2, 2, 2, "李四"));
+ students.add(new Student(2, 3, 2, "霸天虎"));
+ list = CollectionStream.translate2List(students, Student::getName);
+ List compare = new ArrayList<>();
+ compare.add("张三");
+ compare.add("李四");
+ compare.add("李四");
+ compare.add("李四");
+ compare.add("霸天虎");
+ Assert.assertEquals(true, list.equals(compare));
+ }
+
+ @Test
+ public void testTranslate2Set() {
+ Set set = CollectionStream.translate2Set(null, Student::getName);
+ Assert.assertEquals(set, Collections.EMPTY_SET);
+ List students = new ArrayList<>();
+ set = CollectionStream.translate2Set(students, Student::getName);
+ Assert.assertEquals(set, Collections.EMPTY_SET);
+ students.add(new Student(1, 1, 1, "张三"));
+ students.add(new Student(1, 2, 2, "李四"));
+ students.add(new Student(2, 1, 1, "李四"));
+ students.add(new Student(2, 2, 2, "李四"));
+ students.add(new Student(2, 3, 2, "霸天虎"));
+ set = CollectionStream.translate2Set(students, Student::getName);
+ Set compare = new HashSet<>();
+ compare.add("张三");
+ compare.add("李四");
+ compare.add("李四");
+ compare.add("李四");
+ compare.add("霸天虎");
+ Assert.assertEquals(true, set.equals(compare));
+ }
+
+ @Test
+ public void testMerge() {
+ Map map1 = null;
+ Map map2 = Collections.EMPTY_MAP;
+ Map map = CollectionStream.merge(map1, map2, (s1, s2) -> s1.getName() + s2.getName());
+ Assert.assertEquals(map, Collections.EMPTY_MAP);
+ map1 = new HashMap<>();
+ map1.put(1L, new Student(1, 1, 1, "张三"));
+ map = CollectionStream.merge(map1, map2, this::merge);
+ Map temp = new HashMap<>();
+ temp.put(1L, "张三");
+ Assert.assertEquals(map, temp);
+ map2 = new HashMap<>();
+ map2.put(1L, new Student(2, 1, 1, "李四"));
+ map = CollectionStream.merge(map1, map2, this::merge);
+ Map compare = new HashMap<>();
+ compare.put(1L, "张三李四");
+ Assert.assertEquals(true, map.equals(compare));
+ }
+
+ public String merge(Student student1, Student student2) {
+ if (student1 == null && student2 == null) {
+ return null;
+ } else if (student1 == null) {
+ return student2.getName();
+ } else if (student2 == null) {
+ return student1.getName();
+ } else {
+ return student1.getName() + student2.getName();
+ }
+ }
+
+ /**
+ * 班级类
+ */
+ public static class Student {
+ private long termId;//学期id
+ private long classId;//班级id
+ private long studentId;//班级id
+ private String name;//学生名称
+
+ public Student(long termId, long classId, long studentId, String name) {
+ this.termId = termId;
+ this.classId = classId;
+ this.studentId = studentId;
+ this.name = name;
+ }
+
+ public long getTermId() {
+ return termId;
+ }
+
+ public void setTermId(long termId) {
+ this.termId = termId;
+ }
+
+ public long getClassId() {
+ return classId;
+ }
+
+ public void setClassId(long classId) {
+ this.classId = classId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getStudentId() {
+ return studentId;
+ }
+
+ public void setStudentId(long studentId) {
+ this.studentId = studentId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return toString().equals(obj.toString());
+ }
+
+ @Override
+ public String toString() {
+ return "Student{" +
+ "termId=" + termId +
+ ", classId=" + classId +
+ ", studentId=" + studentId +
+ ", name='" + name + '\'' +
+ '}';
+ }
+ }
+}
\ No newline at end of file