1. 数据准备:

  1. public class TestGroupBy {
  2. @Data
  3. public static class User {
  4. private Integer id;
  5. private Integer schoolId;
  6. private String userName;
  7. private String edu;
  8. private double price;
  9. }
  10. public static List<TestListToMap.User> users = new ArrayList<>();
  11. static {
  12. TestListToMap.User u1 = new TestListToMap.User();
  13. u1.setId(1001);
  14. u1.setSchoolId(100);
  15. u1.setUserName("小1");
  16. u1.setEdu("001");
  17. u1.setPrice(0.01);
  18. TestListToMap.User u2 = new TestListToMap.User();
  19. u2.setId(1002);
  20. u2.setSchoolId(100);
  21. u2.setUserName("小2");
  22. u2.setEdu("002");
  23. u2.setPrice(0.20);
  24. TestListToMap.User u3 = new TestListToMap.User();
  25. u3.setId(2010);
  26. u3.setSchoolId(200);
  27. u3.setUserName("小3");
  28. u3.setEdu("001");
  29. u3.setPrice(3.00);
  30. TestListToMap.User u4 = new TestListToMap.User();
  31. u4.setId(3001);
  32. u4.setSchoolId(300);
  33. u4.setEdu("001");
  34. u4.setPrice(40.0);
  35. users.add(u1);
  36. users.add(u2);
  37. users.add(u3);
  38. users.add(u4);
  39. }
  40. }

对List进行分组,也可以理解为将List转换为Map集合。
若想将返回的结果映射为不同的集合。

  1. public static void main(String[] args) {
  2. List<String> lists=new ArrayList<>();
  3. lists.add("a");
  4. lists.add("b");
  5. lists.add("a");
  6. lists.add("a");
  7. //将最终结果映射为LinkedHashSet结构。
  8. LinkedHashSet<String> collect = lists.stream().
  9. collect(Collectors.toCollection(LinkedHashSet::new));
  10. System.out.println(collect);
  11. }

2. group by的重载方法

group by生成一个拥有分组功能的Collector,有三个重载方法。

  1. 需要一个参数:按照该参数进行分组。结果返回一个Map集合,每个Map的key默认是分组参数的类型,value是一个List集合。

    1. public void test1() {
    2. Map <String,List < User >> collect = users.stream().collect(Collectors.groupingBy(User::getEdu));
    3. }
  2. 需要两个参数:第二参数是Collector类型,可以对value进行处理。

2.1 可以对结果进行映射

  1. public void test2() {
  2. Map <String,List <Integer>> collect = users.stream().collect(Collectors.groupingBy(User::getEdu,
  3. //第二个参数对Map的value进行处理(映射)
  4. Collectors.mapping(User::getId, Collectors.toList())));
  5. }

2.2 可以对结果进行求和

  1. public static void test3() {
  2. Map <String,Double> collect = users.stream().collect(Collectors.groupingBy(User::getEdu,
  3. //对参数进行累计求和
  4. Collectors.summingDouble(User::getPrice)));
  5. System.out.println(collect);
  6. }

2.3 对结果的统计

  1. public static void test4() {
  2. Map < String,Long > collect = users.stream().collect(Collectors.groupingBy(User::getEdu,
  3. //获取count数量
  4. Collectors.counting()));
  5. System.out.println(collect);
  6. }
  1. 需要三个参数,第三个参数添加了对结果Map的生成方式,默认是HashMap
    1. public static void test3() {
    2. Map <String,Double > collect = users.stream().collect(Collectors.groupingBy(User::getEdu,
    3. //决定map的生成方式,使用TreeMap
    4. TreeMap::new,
    5. //对参数进行累计求和
    6. Collectors.summingDouble(User::getPrice)));
    7. System.out.println(collect);
    8. }
    如果kv是唯一对应的,可以使用Collectors.toMap来实现。
    1. public static <T> List<List<String>> splitList(List<String> collection, int splitSize) {
    2. if(CollectionUtils.isEmpty(collection)) {
    3. return Collections.emptyList();
    4. }
    5. int maxSize = collection.size() / splitSize + 1;
    6. return Stream.iterate(0, f -> f + 1)
    7. .limit(maxSize)
    8. .parallel()
    9. .map(a -> collection.parallelStream().skip((long) a * splitSize).limit(splitSize).collect(Collectors.toList()))
    10. .filter(b -> !b.isEmpty())
    11. .collect(Collectors.toList());
    12. }
  1. package cn.gpdi.util;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.concurrent.CountDownLatch;
  5. /**
  6. * 多线程工具类
  7. */
  8. public class ThreadUtil {
  9. /**
  10. * 多线程处理list
  11. * 这里我改造成了静态方法
  12. *
  13. * @param data 数据list
  14. * @param countDownLatch 协调多个线程之间的同步
  15. * @param threadNum 开启的线程数:也可以使用countDownLatch.getCount();//来获取开启的线程数但是要注意这个会是一个风险。因为这是一个可变的数。而控制他改变的罪魁祸首就是countDownLatch.countDown();
  16. */
  17. public static synchronized void handleList(List<String> data,
  18. CountDownLatch countDownLatch, int threadNum) {
  19. int length = data.size();//获取数据的总数
  20. int tl = length % threadNum == 0 ? length / threadNum : (length
  21. / threadNum + 1);//计算每个线程平均处理的数据
  22. for (int i = 0; i < threadNum; i++) {
  23. int end = (i + 1) * tl;//获得每个线程的最后下标(避免有余数导致数据错误所以前面的线程下标+1)
  24. HandleThread thread = new HandleThread("线程[" + (i + 1) + "] ",
  25. data, i * tl, end > length ? length : end, countDownLatch);//最后一个线程拿到的是剩余的数据
  26. thread.start();//开启线程运行run方法进行数据处理
  27. }
  28. }
  29. /**
  30. * 内置类继承线程类
  31. * 这里为了方便大家学习就直接这样写了.
  32. */
  33. static class HandleThread extends Thread {
  34. private String threadName; //线程名称
  35. private List<String> data; //该线程负责的数据
  36. private int start; //开始集合的下标
  37. private int end; //结束集合的下标
  38. private CountDownLatch countDownLatch; //协调多个线程之间的同步
  39. /**
  40. * 无参构造函数
  41. */
  42. public HandleThread() {
  43. super();
  44. }
  45. /**
  46. * 带参构造方法
  47. *
  48. * @param threadName 当前线程名称
  49. * @param data 数据
  50. * @param start 开始的下标
  51. * @param end 结束的下标
  52. * @param countDownLatch 协调多个线程之间的同步
  53. */
  54. public HandleThread(String threadName, List<String> data, int start,
  55. int end, CountDownLatch countDownLatch) {
  56. this.threadName = threadName;
  57. this.data = data;
  58. this.start = start;
  59. this.end = end;
  60. this.countDownLatch = countDownLatch;
  61. }
  62. /**
  63. * 重写Thread的run方法 调用start方法之后自动调用run方法
  64. */
  65. public void run() {
  66. // 这里处理数据
  67. List<String> subList = data.subList(start, end);//获取当前线程需要处理的数据
  68. for (int a = 0; a < subList.size(); a++) {
  69. System.out.println(threadName + "处理了 " + subList.get(a) +
  70. " !");
  71. }
  72. System.out.println(threadName + "处理了 " + subList.size() + "条数据!");
  73. // 执行子任务完毕之后,countDown减少一个点
  74. countDownLatch.countDown();
  75. }
  76. }
  77. /**
  78. * 使用main方法进行测试
  79. *
  80. * @param args
  81. * @throws Exception
  82. */
  83. public static void main(String[] args) throws Exception {
  84. // 准备测试数据
  85. List<String> data = new ArrayList<String>();
  86. for (int i = 0; i < 100; i++) {
  87. data.add("item" + i);
  88. }
  89. int threadNum = 3;//设置需要开启的线程数
  90. CountDownLatch countDownLatch = new CountDownLatch(threadNum);//CountDownLatch实现使用一个计数器,而参数cout就是初始化计数器的值,该值一经初始化就不能再被修改。
  91. ThreadUtil.handleList(data, countDownLatch, threadNum);
  92. countDownLatch.await();// 调用await方法阻塞当前线程,等待子线程完成后在继续执行
  93. System.out.println("=============主线程执行完毕!================");
  94. }
  95. }