介绍
在Java 8 的Lambda(stream)之前,要在Java代码中实现类似SQL中的group by分组聚合功能,还是比较困难的。这之前Java对函数式编程支持不是很好,Scala则把函数式编程发挥到了机制,实现一个group by聚合对Scala来说就是几行代码的事情:
val birds = List("Golden Eagle","Gyrfalcon", "American Robin", "Mountain BlueBird", "Mountain-Hawk Eagle")
val groupByFirstLetter = birds.groupby(_.charAt(0))
输出:
Map(M -> List(Mountain BlueBird, Mountain-Hawk Eagle), G -> List(Golden Eagle, Gyrfalcon),
A -> List(American Robin))
Java也有一些第三方的函数库来支持,例如Guava的Function,以及functional java这样的库。 但总的来说,内存对Java集合进行GroupBy ,OrderBy, Limit等TopN操作还是比较繁琐。本文实现一个简单的group功能,支持自定义key以及聚合函数,通过简单的几个类,可以实现SQL都比较难实现的先分组,然后组内排序,最后取组内TopN。
源码可以在这里下载;
实现
假设我们有这样一个Person类:
package me.lin;
class Person {
private String name;
private int age;
private double salary;
public Person(String name, int age, double salary) {
super();
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String getNameAndAge() {
return this.getName() + "-" + this.getAge();
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", salary=" + salary
+ "]";
}
}
对于一个Person的List,想要根据年龄进行统计,取第一个值,取salary最高值等。实现如下:
聚合操作
定义一个聚合接口,用于对分组后的元素进行聚合操作,类比到MySQL中的count(*)
、sum()
:
package me.lin;
import java.util.List;
/**
*
* 聚合操作
*
* Created by Brandon on 2016/7/21.
*/
public interface Aggregator<T> {
/**
* 每一组的聚合操作
*
* @param key 组别标识key
* @param values 属于该组的元素集合
* @return
*/
Object aggregate(Object key , List<T> values);
}
我们实现几个聚合操作,更复杂的操作支持完全可以自己定义。
CountAggragator:
package me.lin;
import java.util.List;
/**
*
* 计数聚合操作
*
* Created by Brandon on 2016/7/21.
*/
public class