1. 数据准备:
public class TestGroupBy {
@Data
public 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的生成方式,使用TreeMap
TreeMap::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("=============主线程执行完毕!================");
}
}