Java函数式编程三之收集器

Java函数式编程二

方法引用

Lambda 表达式有一个常见的用法,即调用参数。比如获取学生姓名:

student -> student.getName()

java8提供了一个简写语法,叫做方法引用,用方法引用重写上面的 Lambda 表达式,代码如下:

Student::getName

标准语法为 Classname::methodName。(注意方法不加括号,因为这里并不调用该方法)。
凡是使用 Lambda 表达式的地方,就可以使用方法引用,如使用 Lambda 表达式创建一个对象,可以这样Student::new,创建一个字符串数组String[]::new,这样不但使得代码更短更易阅读,同时也使得代码行为意图更加突出。方法引用支持多个参数,当然前提是函数接口支持多个参数!

收集器

上篇文章使用过collect(Collectors.toList())在流中生成列表List,同时通过Collectors.toSet可以生成Set,但是有时人们还希望从流生成其他值或希望定制一个类将你想要的东西抽象出来。这就是收集器,一种通用的、从流生成复杂值的结构。只要将其传与collect方法,所有的流就可以使用它了。

转换成其他集合
如需要将流转换成一个TreeSet,可以这样:

stream.collect(Collectors.toCollection(TreeSet::new));

转换成值
minBymaxBy可以让我们按照特定顺序生成一个值

//找出最长的字符串
String s1 = Stream.of("a", "abc", "ar")
                .collect(Collectors.maxBy(Comparator.comparing(item -> item.length())))
                .get();
System.out.println(s1);
abc
//计算一组字符串的平均长度
 Double collect7 = Stream.of("a", "abc", "ar", "abb")
                .collect(Collectors.averagingInt(item -> item.length()));
 System.out.println(collect7);
2.25

数据分块
一个常用的流操作就是将其分成两个集合,可以使用partitioningBy,它接受一个流,并将其分成两部分,如将一组数据分为偶数和奇数两个集合:

Map> collect9 = Stream.of(2, 5, 8, 9, 0)
                .collect(Collectors.partitioningBy(i -> i % 2 == 0));
System.out.println(collect9);
{false=[5, 9], true=[2, 8, 0]}

数据分块就是将数据按照一定的规则分成true和false两部分!

数据分组
groupingBy数据分组是一种更自然的分割数据操作,与partitioningBy将数据分成true 和 false两部分不同,groupingBy可以使用任意值对数据分组。

//将学生以年龄分组
Map> collect11 = studentsList.stream()
                .collect(Collectors.groupingBy(student -> student.getAge()));
将字符串以长度分组
 Map> collect = Stream.of("a", "abc", "ar", "abb")
                .collect(Collectors.groupingBy(str -> str.length()));
System.out.println(collect);

结果如下

{1=[a], 2=[ar], 3=[abc, abb]}

字符串
很多时候,收集流中的数据是为了在最后生成一个字符串,java8之前一般都是使用StringBuilder,现在可以使用收集器中的joining方法。

//输出班级内所有学生的名字, 以逗号分隔,并最终将字符串放到中括号内
String collect14 = classes4.getStudents().stream()
                //.map(student -> student.getName())
                .map(Student::getName)
                .collect(Collectors.joining(",", "[", "]"));
System.out.println(collect);
[李二,张三,王麻子,王十一,王十二] 	 

joining有多种重载方法

//接受一个参数 作为分隔符
public static Collector joining(CharSequence delimiter) {
     return joining(delimiter, "", "");
 }
 
 //无参 直接将元素连接成字符串
public static Collector joining() {
     return new CollectorImpl(
             StringBuilder::new, StringBuilder::append,
             (r1, r2) -> { r1.append(r2); return r1; },
             StringBuilder::toString, CH_NOID);
 }

组合收集器
将各种收集器组合起来,可以实现更加强大的功能。
要将一个班级列表以班级人数分组很简单:

//将班级列表以各个班级人数分组
Map> collect16 = classList.stream()
               .collect(Collectors.groupingBy(c -> c.getStudents().size()));
System.out.println(collect16);

但是如果我们不想要以人数->班级的形式分组,而是想要以人数->班级名称的形式发分组,该怎么办呢?这里就需要组合收集器:

 Map> collect = classList.stream()
                .collect(Collectors.groupingBy(c -> c.getStudents().size(),
                 Collectors.mapping(c -> c.getName(), Collectors.toList())));
System.out.println(collect);
{2=[三班], 3=[一班, 二班]}

上面的例子,首先使用groupingBy将班级列表按照班级人数分组;然后使用mapping转换value值,即将班级转换为班级名称,并指明以List集合存储。

mapping 允许在收集器的容器上执行类似 map 的操作。

这个例子用到了第二个收集器,用以收集最终结果的一个子集。这些收集器叫作下游收集器。主收集器用于生成最终结果,下游收集器用于生成部分结果!

Java函数式编程四

你可能感兴趣的:(java,JAVA知识,java,函数式编程,收集器,Lambda)