一、JDFrame 介绍
在大数据处理领域,Apache Spark
以其强大的分布式计算能力和丰富的数据处理API
而广受好评。然而,在许多日常的软件开发场景中,我们面临的数据量可能并不需要Spark
这样的分布式系统来处理。相反,我们更希望有一种工具能够在单机环境下提供类似Spark
的便捷数据处理能力。Java 8
的Stream API
虽然提供了一种简化的数据处理方式,但在复杂的数据转换和操作上,它仍显得有些力不从心。正是在这样的需求背景下,JDFrame
应运而生。JDFrame
借鉴了Spark
的API
设计理念,为Java
开发者提供了一套比Java Stream
更为强大和灵活的流式数据处理工具。
JDFrame
的核心优势之一是其丰富的API
。它不仅包含了Java 8 Stream
的基础操作,还增加了更多高级数据处理功能,如窗口函数、复杂条件过滤、自定义聚合,以及类似SQL
的分组、聚合和连接操作。这些额外的功能使得JDFrame
能够轻松应对复杂的数据操作需求,为开发者提供了更大的灵活性和便利性。
总之就是你可以使用SQL
处理的思路去处理数据。
Githb 地址:https://github.com/burukeYou/JDFrame
使用示例:
添加依赖:
<dependency><groupId>io.github.burukeyou</groupId><artifactId>jdframe</artifactId><version>0.0.4</version>
</dependency>
public class JdframeTest {@Data@AllArgsConstructor@NoArgsConstructorpublic static class UserVO {private int id;private String name;private Integer age;private Integer score;}private static final List<UserVO> userVOS = Arrays.asList(new UserVO(1, "张三", 20, 90),new UserVO(2, "李四", 21, 92),new UserVO(3, "王五", 30, 95),new UserVO(4, "赵六", 15, 85),new UserVO(5, "小明", 32, 88),new UserVO(6, "小红", 18, 94),new UserVO(7, "小兰", 19, 75),new UserVO(8, "小青", 12, 60),new UserVO(9, "小王", 16, 34),new UserVO(10, "小李", 17, 49),new UserVO(11, "张三", 19, 59));public static void main(String[] args) {System.out.println("查看分数在 60 - 90 之间的姓名、分数的 Top5");SDFrame<FI2<String, BigDecimal>> sdf1 = SDFrame.read(userVOS).whereNotNull(UserVO::getScore) // 不为空.whereBetween(UserVO::getScore, 60, 90) // 范围过滤.groupBySum(UserVO::getName, UserVO::getScore) // 聚合求和.sortDesc(FI2::getC2) // 根据值降序排列.cutFirst(5); // 保留 Top5sdf1.show(); // 查看数据System.out.println("查看姓名中 张 开头的信息");SDFrame<UserVO> sdf2 = SDFrame.read(userVOS).whereNotNull(UserVO::getName) // 不为空.whereLikeLeft(UserVO::getName, "张"); // 模糊查询sdf2.show();System.out.println("查看80分以上的平均年龄");BigDecimal sdf3 = SDFrame.read(userVOS).whereNotNull(UserVO::getAge) // 不为空.whereNotNull(UserVO::getName) // 不为空.whereGt(UserVO::getScore,80) // 分数大于80.avg(UserVO::getAge); // 计算平均System.out.println(sdf3);}}
运行结果:
二、JDFrame VS Java Stream
JDFrame
在功能上要比 Java Stream
强大,在性能上相比呢,下面构建一千万条数据,进行分组计算后取 Top
数据,看谁处理的更快:
public class JdframePropertyTest {@Data@AllArgsConstructor@NoArgsConstructorpublic static class UserVO {private int id;private String name;private Integer age;private Integer score;}public static void main(String[] args) {List<UserVO> userVOS = new ArrayList<>();Random random = new Random();// 构造一千万条数据for (int i = 0; i < 10000000; i++) {userVOS.add(new UserVO(1, String.valueOf(i), (random.nextInt(91) + 10), (random.nextInt(71) + 30)));}System.out.println("准备测试数据完成,当前数据量:"+userVOS.size());// 根据每个年龄进行分组求和,最后取出分数最大的 top5 的年龄和总分数// java stream 处理System.out.println("开始 java stream 处理。。。");long t1 = System.currentTimeMillis();List<Map.Entry<Integer, Integer>> streamTop = userVOS.stream().filter(Objects::nonNull).filter(u -> Objects.nonNull(u.getAge())).filter(u -> Objects.nonNull(u.getScore())).collect(Collectors.groupingBy(UserVO::getAge, Collectors.summingInt(UserVO::getScore))).entrySet().stream().sorted(Map.Entry.<Integer, Integer>comparingByValue().reversed()).limit(5).collect(Collectors.toList());System.out.println("java stream 耗时:" + (System.currentTimeMillis() - t1) + " , 结果如下:");streamTop.forEach(map -> System.out.println("age: " + map.getKey() + " , score: " + map.getValue()));streamTop.clear();System.out.println("开始 jdFrame 处理。。。");t1 = System.currentTimeMillis();List<FI2<Integer, BigDecimal>> jdFrameTop = SDFrame.read(userVOS).whereNotNull(UserVO::getAge).whereNotNull(UserVO::getScore).groupBySum(UserVO::getAge, UserVO::getScore).sortDesc(FI2::getC2).cutFirst(5).toLists();System.out.println("jdFrame 耗时:" + (System.currentTimeMillis() - t1) + " , 结果如下:");jdFrameTop.forEach(fi2 -> System.out.println("age: " + fi2.getC1() + " , score: " + fi2.getC2()));}}
从结果上看,JDFrame
的速度逊色一些,但如果你处理数据量不大的情况,JDFrame
确实是一个非常好的工具。
比如数据量在一百万的时候,差距明显减少:
下面可以学习下 JDFrame
的 Api
。
三、JDFrame Api
3.1 Where 条件过滤
3.1.1 精确过滤
SDFrame.read(userVOS)// is not nll.whereNotNull(UserVO::getName)// =.whereEq(UserVO::getName, "张三")// != , <>.whereNotEq(UserVO::getName, "李四");
3.1.2 模糊过滤
SDFrame.read(userVOS)// like "%小%".whereLike(UserVO::getName, "小")// like "小%".whereLikeLeft(UserVO::getName, "小")// like "%小".whereLikeRight(UserVO::getName, "小");
3.1.3 范围过滤
SDFrame.read(userVOS)// >.whereGt(UserVO::getScore, 60)// >=.whereGe(UserVO::getScore, 60)// <.whereLe(UserVO::getScore, 60)// <=.whereLt(UserVO::getScore, 60)// >= and <=.whereBetween(UserVO::getScore, 60, 80)// > and <=.whereBetweenR(UserVO::getScore, 60, 80)// >= and <.whereBetweenL(UserVO::getScore, 60, 80)// in.whereIn(UserVO::getScore, Arrays.asList(60, 70, 80))// not in.whereNotIn(UserVO::getScore, Arrays.asList(60, 70, 80));
3.2 数据统计
3.2.1 分组求和
JDFrame<UserVO> frame = JDFrame.read(userVOS);
// select name,sum(score) from userVOs group by name
frame.groupBySum(UserVO::getName, UserVO::getScore);// select name,age,sum(score) from userVOs group by name, age
frame.groupBySum(UserVO::getName, UserVO::getAge, UserVO::getScore);
3.2.2 分组求最大最小值
JDFrame<UserVO> frame = JDFrame.read(userVOS);// select name,max(age) from userVOs group by name
frame.groupByMaxValue(UserVO::getName, UserVO::getScore);// 和 groupByMaxValue 一致,拿到的是对象
frame.groupByMax(UserVO::getName, UserVO::getScore);// select name,min(score) from userVOs group by name
frame.groupByMinValue(UserVO::getName, UserVO::getScore);// 和 groupByMinValue 一致,拿到的是对象
frame.groupByMaxMin(UserVO::getName, UserVO::getScore);
3.2.3 分组计数
JDFrame<UserVO> frame = JDFrame.read(userVOS);// select count(1) from userVOs group by name
frame.groupByCount(UserVO::getName);// select count(1) from userVOs group by name, age
frame.groupByCount(UserVO::getName, UserVO::getAge);// select count(1) from userVOs group by name, age, score
frame.groupByCount(UserVO::getName, UserVO::getAge, UserVO::getScore);// select name,sum(score),count(1) from userVOs group by name
frame.groupBySumCount(UserVO::getName, UserVO::getScore);
3.2.4 整体统计
JDFrame<UserVO> frame = JDFrame.read(userVOS);
// select max(score) from userVOs
frame.maxValue(UserVO::getScore);
// 和 maxValue 一致,拿到的是对象
frame.max(UserVO::getScore);
// select min(score) from userVOs
frame.minValue(UserVO::getScore);
// 和 minValue 一致,拿到的是对象
frame.min(UserVO::getScore);
// select avg(score) from userVOs
frame.avg(UserVO::getScore);
// select sum(score) from userVOs
frame.sum(UserVO::getScore);
// select max(score),min(score) from userVOs
frame.maxMinValue(UserVO::getScore);
// 和 maxMinValue 一致,可以拿到对象
frame.maxMin(UserVO::getScore);
3.3 排序
JDFrame<UserVO> frame = JDFrame.read(userVOS);
// select * from userVOS order by score desc
frame.sortDesc(UserVO::getScore);
// select * from userVOS order by age asc
frame.sortAsc(UserVO::getAge);
// select * from userVOS order by score asc, age asc
frame.sortDesc(Sorter.sortDescBy(UserVO::getScore).sortAsc(UserVO::getAge));
// 自定义排序规则
frame.sortAsc(Comparator.comparing(e -> {if (e.getAge() > 20 && e.getScore() > 80) {return 1;} else {return 0;}
}));
3.4 去重
JDFrame<UserVO> frame = JDFrame.read(userVOS);
// 根据对象去重
frame.distinct();
// 根据某个字段去重
frame.distinct(UserVO::getName);
// 根据多个字段,先后去重
frame.distinct(UserVO::getName).distinct(UserVO::getAge);
3.5 join 连接
JDFrame<UserVO> frame1 = JDFrame.read(userVOS);
JDFrame<UserVO> frame2 = JDFrame.read(userVOS);
// 内连接
JDFrame<UserVO> join = frame1.join(frame2, (f1, f2) -> Objects.equals(f1.getId(), f2.getId()), (f1, f2) -> {// 具体合并曹组f1.setName(f2.getName());return f1;
});
// 左连接
JDFrame<UserVO> leftJoin = frame1.leftJoin(frame2, (f1, f2) -> Objects.equals(f1.getId(), f2.getId()), (f1, f2) -> {// 具体合并曹组f1.setName(f2.getName());return f1;
});
// 右连接
JDFrame<UserVO> rightJoin = frame1.rightJoin(frame2, (f1, f2) -> Objects.equals(f1.getId(), f2.getId()), (f1, f2) -> {// 具体合并曹组f1.setName(f2.getName());return f1;
});
3.6 其他操作
JDFrame<UserVO> frame = JDFrame.read(userVOS);
// 打印数据
frame.show();
// 获取表头
frame.columns();
// 获取某一列数据
frame.col(UserVO::getName);
// 获取第一条数据
frame.head();
// 获取前 5 数据
frame.head(5);
// 获取最后一个数据
frame.tail();
// 获取最后5条数据
frame.tail(5);
// 分页获取数据
frame.page(1, 5);// JDFrame 新增数据
frame.append(new UserVO(12, "小九", 19, 59));
// 多个 JDFrame 合并数据
frame.union(JDFrame.read(userVOS));// 数据截取
// 截取前5个数据
frame.cutFirst(5);
// 截取最后5个数据
frame.cutLast(5);
// 指定范围截取
frame.cut(2, 5);
// 分页截取
frame.cutPage(1, 5);// 数据拆分, 分为 5 个一组
frame.partition(5);