1. 数据准备:
public class TestGroupBy {@Datapublic static class User {private Integer id;private Integer schoolId;private String userName;private String edu;private double price;}public static List<TestListToMap.User> users = new ArrayList<>();static {TestListToMap.User u1 = new TestListToMap.User();u1.setId(1001);u1.setSchoolId(100);u1.setUserName("小1");u1.setEdu("001");u1.setPrice(0.01);TestListToMap.User u2 = new TestListToMap.User();u2.setId(1002);u2.setSchoolId(100);u2.setUserName("小2");u2.setEdu("002");u2.setPrice(0.20);TestListToMap.User u3 = new TestListToMap.User();u3.setId(2010);u3.setSchoolId(200);u3.setUserName("小3");u3.setEdu("001");u3.setPrice(3.00);TestListToMap.User u4 = new TestListToMap.User();u4.setId(3001);u4.setSchoolId(300);u4.setEdu("001");u4.setPrice(40.0);users.add(u1);users.add(u2);users.add(u3);users.add(u4);}}
对List进行分组,也可以理解为将List转换为Map集合。
若想将返回的结果映射为不同的集合。
public static void main(String[] args) {List<String> lists=new ArrayList<>();lists.add("a");lists.add("b");lists.add("a");lists.add("a");//将最终结果映射为LinkedHashSet结构。LinkedHashSet<String> collect = lists.stream().collect(Collectors.toCollection(LinkedHashSet::new));System.out.println(collect);}
2. group by的重载方法
group by生成一个拥有分组功能的Collector,有三个重载方法。
需要一个参数:按照该参数进行分组。结果返回一个Map集合,每个Map的key默认是分组参数的类型,value是一个List集合。
public void test1() {Map <String,List < User >> collect = users.stream().collect(Collectors.groupingBy(User::getEdu));}
需要两个参数:第二参数是Collector类型,可以对value进行处理。
2.1 可以对结果进行映射
public void test2() {Map <String,List <Integer>> collect = users.stream().collect(Collectors.groupingBy(User::getEdu,//第二个参数对Map的value进行处理(映射)Collectors.mapping(User::getId, Collectors.toList())));}
2.2 可以对结果进行求和
public static void test3() {Map <String,Double> collect = users.stream().collect(Collectors.groupingBy(User::getEdu,//对参数进行累计求和Collectors.summingDouble(User::getPrice)));System.out.println(collect);}
2.3 对结果的统计
public static void test4() {Map < String,Long > collect = users.stream().collect(Collectors.groupingBy(User::getEdu,//获取count数量Collectors.counting()));System.out.println(collect);}
- 需要三个参数,第三个参数添加了对结果Map的生成方式,默认是HashMap
如果kv是唯一对应的,可以使用Collectors.toMap来实现。public static void test3() {Map <String,Double > collect = users.stream().collect(Collectors.groupingBy(User::getEdu,//决定map的生成方式,使用TreeMapTreeMap::new,//对参数进行累计求和Collectors.summingDouble(User::getPrice)));System.out.println(collect);}
public static <T> List<List<String>> splitList(List<String> collection, int splitSize) {if(CollectionUtils.isEmpty(collection)) {return Collections.emptyList();}int maxSize = collection.size() / splitSize + 1;return Stream.iterate(0, f -> f + 1).limit(maxSize).parallel().map(a -> collection.parallelStream().skip((long) a * splitSize).limit(splitSize).collect(Collectors.toList())).filter(b -> !b.isEmpty()).collect(Collectors.toList());}
package cn.gpdi.util;import java.util.ArrayList;import java.util.List;import java.util.concurrent.CountDownLatch;/*** 多线程工具类*/public class ThreadUtil {/*** 多线程处理list* 这里我改造成了静态方法** @param data 数据list* @param countDownLatch 协调多个线程之间的同步* @param threadNum 开启的线程数:也可以使用countDownLatch.getCount();//来获取开启的线程数但是要注意这个会是一个风险。因为这是一个可变的数。而控制他改变的罪魁祸首就是countDownLatch.countDown();*/public static synchronized void handleList(List<String> data,CountDownLatch countDownLatch, int threadNum) {int length = data.size();//获取数据的总数int tl = length % threadNum == 0 ? length / threadNum : (length/ threadNum + 1);//计算每个线程平均处理的数据for (int i = 0; i < threadNum; i++) {int end = (i + 1) * tl;//获得每个线程的最后下标(避免有余数导致数据错误所以前面的线程下标+1)HandleThread thread = new HandleThread("线程[" + (i + 1) + "] ",data, i * tl, end > length ? length : end, countDownLatch);//最后一个线程拿到的是剩余的数据thread.start();//开启线程运行run方法进行数据处理}}/*** 内置类继承线程类* 这里为了方便大家学习就直接这样写了.*/static class HandleThread extends Thread {private String threadName; //线程名称private List<String> data; //该线程负责的数据private int start; //开始集合的下标private int end; //结束集合的下标private CountDownLatch countDownLatch; //协调多个线程之间的同步/*** 无参构造函数*/public HandleThread() {super();}/*** 带参构造方法** @param threadName 当前线程名称* @param data 数据* @param start 开始的下标* @param end 结束的下标* @param countDownLatch 协调多个线程之间的同步*/public HandleThread(String threadName, List<String> data, int start,int end, CountDownLatch countDownLatch) {this.threadName = threadName;this.data = data;this.start = start;this.end = end;this.countDownLatch = countDownLatch;}/*** 重写Thread的run方法 调用start方法之后自动调用run方法*/public void run() {// 这里处理数据List<String> subList = data.subList(start, end);//获取当前线程需要处理的数据for (int a = 0; a < subList.size(); a++) {System.out.println(threadName + "处理了 " + subList.get(a) +" !");}System.out.println(threadName + "处理了 " + subList.size() + "条数据!");// 执行子任务完毕之后,countDown减少一个点countDownLatch.countDown();}}/*** 使用main方法进行测试** @param args* @throws Exception*/public static void main(String[] args) throws Exception {// 准备测试数据List<String> data = new ArrayList<String>();for (int i = 0; i < 100; i++) {data.add("item" + i);}int threadNum = 3;//设置需要开启的线程数CountDownLatch countDownLatch = new CountDownLatch(threadNum);//CountDownLatch实现使用一个计数器,而参数cout就是初始化计数器的值,该值一经初始化就不能再被修改。ThreadUtil.handleList(data, countDownLatch, threadNum);countDownLatch.await();// 调用await方法阻塞当前线程,等待子线程完成后在继续执行System.out.println("=============主线程执行完毕!================");}}
