先看一个收集器的例子
在一个user列表中,用年龄进行分组
1.8之前的写法
Map<Integer, List<User>> mapNew = new HashMap<>();
for (int i = 0; i < listUser.size(); i++) {
User user = listUser.get(i);
int age = user.getAge();
List<User> users = mapNew.get(age);
if (null != users && users.size() > 0) {
break;
}
List<User> listUserIn = new ArrayList<>();
for (User userIn : listUser) {
if (userIn.getAge() == age) {
listUserIn.add(userIn);
}
}
mapNew.put(age, listUserIn);
}
log.info(mapNew.toString());
jdk1.8 stream 一行代码就完成了上面的功能
Map<Integer, List<User>> collect = listUser.stream().collect(groupingBy(User::getAge));
log.info(collect.toString());
从这个例子就能看出来,函数式编程的优势,我们只需要关心生成的结果,而不需要关注过程;在这个例子中,传递在collect方法参数的是Collector接口的一个实现,也就是给Stream元素做分组汇总的方法groupingBy
,和sql很像。
收集器可以更加简洁而灵活的定义,Collectors类中实现了很多收集器,配合collect()
使用,collect方法将对流的元素触发一个规约操作生成结果。
先初始化一个user集合
@Slf4j
public class ModeCollectors {
List<User> listUser = new ArrayList<>();
@Before
public void initList() {
listUser = this.getListUsers();
}
private List<User> getListUsers() {
List<User> listUser = new ArrayList<User>();
for (int i = 0; i < 10; i++) {
listUser.add(new User("man" + i, i, "男"));
}
for (int i = 0; i < 10; i++) {
listUser.add(new User("woman" + i, i, "女"));
}
return listUser;
}
}
import lombok.Data;
@Data
public class User implements Comparable<User> {
private String name;
private Integer age;
private String sex;
public User(String name, Integer age,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(User o) {
int i = -(this.getAge() - o.getAge());
if (i == 0) {
return -(this.age - o.getAge());
}
return i;
}
public boolean isMan(){
return sex.equals("男");
}
}
/**
* counting
* 对所有的女生进行数量统计
*/
@Test
public void testCount() {
Long wo = listUser.stream().filter(user -> user.getName().substring(0, 2).equals("wo")).collect(counting());
log.info(String.valueOf(wo));
}
/**
* summingInt求和
*/
@Test
public void testSum() {
//找到所有女生 并求年龄之和
Integer collect = listUser.stream().filter(user -> user.getName().substring(0, 2).equals("wo")).collect(summingInt(User::getAge));
log.info(String.valueOf(collect));
}
/**
* averagingInt 平均数
*/
@Test
public void testAverag() {
Double collect = listUser.stream().filter(user -> user.getName().substring(0, 2).equals("wo")).collect(averagingInt(User::getAge));
log.info(String.valueOf(collect));
}
/**
* 最大值
*/
@Test
public void testMax() {
Comparator<User> userAgeComparable = comparingInt(User::getAge);
Optional<User> wo = listUser.stream().filter(user -> user.getName().substring(0, 2).equals("wo")).collect(maxBy(userAgeComparable));
log.info(String.valueOf(wo.get()));
}
/**
* 最小值
*/
@Test
public void testMin() {
Comparator<User> userAgeComparable = comparingInt(User::getAge);
Optional<User> wo = listUser.stream().filter(user -> user.getName().substring(0, 2).equals("wo")).collect(minBy(userAgeComparable));
log.info(String.valueOf(wo.get()));
}
这个收集器是上面所有功能的综合,使用对应的方法可以获取所有的值
/**
* summary 综合
*/
@Test
public void testSummary() {
IntSummaryStatistics wo = listUser.stream().filter(user -> user.getName().substring(0, 2).equals("wo")).collect(summarizingInt(User::getAge));
log.info(String.valueOf(wo.toString()));
wo.getAverage();
wo.getCount();
wo.getMax();
wo.getMin();
wo.getSum();
}
将字符串连接在一起,这个比较实用
/**
* joining 连接字符串
*/
@Test
public void testJoin() {
String wo = listUser.stream()
.filter(user -> user.getName().substring(0, 2).equals("wo"))
.map(User::getName)
.collect(joining(","));
log.info(wo);
}
Reducing 这一块的内容和之前的文章比较像Java Stream非官方教程|第四篇:reduce归约
/**
* reducing
* 这一块可以参考Java Stream非官方教程|第四篇:reduce归约 比较类似
*/
@Test
public void testReducing() {
Integer wo = listUser.stream()
.filter(user -> user.getName().substring(0, 2).equals("wo"))
.collect(reducing(0, User::getAge, (i, j) -> i + j));
log.info(wo.toString());
Integer wo2 = listUser.stream()
.filter(user -> user.getName().substring(0, 2).equals("wo"))
.collect(reducing(0, User::getAge, Integer::sum));
log.info(wo2.toString());
}
/**
* 多级分组
*/
@Test
public void testGroupingByLevel() {
//先按年龄,再按等级
Map<Integer, Map<String, List<User>>> wo = listUser.stream().collect(groupingBy(User::getAge,
groupingBy(user -> {
if (user.getName().substring(0, 2).equals("wo")) {
return "level1";
} else {
return "level2";
}
}
)
));
log.info(wo.toString());
}
/**
* 多级分组 最大
* 获取不同性别中年龄最大
*/
@Test
public void testGroupingByLevelMax() {
Map<String, Optional<User>> collect = listUser.stream().collect(groupingBy(User::getSex,
maxBy(comparingInt(User::getAge))
));
log.info(String.valueOf(collect));
}
执行结果
/**
* 多级分组 count
*/
@Test
public void testGroupingByLevelCount() {
//先按年龄,再按等级
Map<String, Long> collect = listUser.stream().collect(groupingBy(User::getSex, counting()));
log.info(String.valueOf(collect));
}
写了两种不同的版本,set和list,set会去掉重复元素
/**
* 多级分组 mapping
*/
@Test
public void testGroupingByMapping() {
Map<String, List<String>> wo = listUser.stream().collect(groupingBy(User::getSex,
mapping(user -> {
if (user.getName().substring(0, 2).equals("wo")) {
return "level1";
} else {
return "level2";
}
}, toList())
));
log.info(String.valueOf(wo));
Map<String, Set<String>> woSet = listUser.stream().collect(groupingBy(User::getSex,
mapping(user -> {
if (user.getName().substring(0, 2).equals("wo")) {
return "level1";
} else {
return "level2";
}
}, toSet())
));
log.info(String.valueOf(woSet));
}
执行结果
分区是分组的特殊情况,由一个 谓词(返回一个布尔值的函数)作为分类函数,称为分区函数。
通俗来讲就是现在的grouping 只能按true或者false进行分类。
/**
* 分区 Partitioned
*/
@Test
public void testPartitioned() {
Map<Boolean, List<User>> map = listUser.stream().collect(partitioningBy(user -> user.getSex().equals("男")));
log.info(String.valueOf(map));
Map<Boolean, List<User>> map2 = listUser.stream().collect(partitioningBy(User::isMan));
log.info(String.valueOf(map2));
}
/**
* 分区 Partitioned counting
*/
@Test
public void testPartitionedCounting() {
final Map<Boolean, Long> map = listUser.stream().collect(partitioningBy(user -> user.getSex().equals("男"), counting()));
log.info(String.valueOf(map));
}