java8 学习用流收集数据

前言

JAVA8是意义深远的一个新版本。随着大数据的兴起,函数式编程在处理大数据上的优势开始体现。JAVA8也紧跟时代,引入了函数式语言的特性

1.收集器简介的作用

优点:更易复合和重用,要是做多级分组指令式和函数式之间的区别就会更加明显。在我们java8之前,由于需要好多层嵌套循环和条件,指令式代码很快就变得更难阅读、更难维护、更难修改。相比之下,函数式版本只要加上一个收集器就可以轻松的实现功能了

收集器非常有用,因为用它可以简洁而灵活的定义collect用来生成集合的标准。更具体的说,对流调用collect方法将对流中的元素触发一个规约操作(由Collector来参数化)

// 这是一个苹果的集合
static List<Apple> newApplyList(){
     List<Apple> inventory = new ArrayList<>();
     Apple a1 = new Apple();
     a1.setWeight(1);
     a1.setCountry("中国");
     a1.setColor("red");
     inventory.add(a1);
     Apple a2 = new Apple();
     a2.setWeight(4);
     a2.setCountry("奥大利亚");
     a2.setColor("blue");
     inventory.add(a2);
     Apple a3 = new Apple();
     a3.setWeight(3);
     a3.setCountry("美国");
     a3.setColor("green");
     inventory.add(a3);
     Apple a4 = new Apple();
     a4.setWeight(1);
     a4.setCountry("日本");
     a4.setColor("yellor");
     inventory.add(a4);
     return inventory;
 }

例如实现一个将苹果列表分成多组并且相同质量的为一组的map集合(返回一个Map<Integer,List<Apple>>)

Map<Integer, List<Apple>> collect = apples.stream()  //先stream将apples转成流
            .collect(Collectors.groupingBy(Apple::getWeight)); // groupingBy分组,条件为weight

 // 运行结果为:{1=[Apple(weight=1, country=中国, color=red, carlories=0)], 3=[Apple(weight=3, country=中国1, color=green, carlories=0), Apple(weight=3, country=中国1, color=green, carlories=0)], 4=[Apple(weight=4, country=奥大利亚, color=red, carlories=0)]}

 

以上可见,原来需要一大串for循环与if判断才能实现的数据处理,只需要简短的一行代码便可以简单明了的实现,并且可读性高,也是其一大优点,下面我们来介绍一些日常中常用的一些流收集器中的函数。

首先我们假定已经导入Collectors类的所有静态工厂方法:这样用不着写:

Collectors.groupingBy(Apple::getWeight)之类的了

import static java.util.stream.Collectors.*; 

2.查找流中的最大值和最小值

使用Collectors.maxByCollectors.minBy,来计算流中的最大值和最小值

例:获取质量最大的苹果

Optional<Apple> collect = apples.stream().collect(maxBy(Comparator.comparingInt(Apple::getWeight)));
System.out.println("质量最重的苹果质量为:" + collect.get().getWeight());
// 运行结果为:质量最重的苹果质量为:4

3.使用流进行数据汇总

Collectors类专门为汇总提供了一个工厂方法:

Collectors.summingInt(Collectors.summingDouble、 Collectors.summingLong)

它可接受一个把对象映射为求和所需int的函数,并返回一个收集器;该收集器在传递给普通collect方法后即执行我们需要的求和操作

:使用summingInt计算苹果列表的总质量。

int totalWeight = apples.stream().collect(summingInt(Apple::getWeight));
// 运行结果为:11

但汇总不仅仅是求和还有Collectors.averageingInt,连同对应的averageingLong和averageingDouble可以计算数值的平均数

:使用averageingDouble计算苹果列表的平均质量。

Double aveWeight= apples.stream().collect(averagingDouble(Apple::getWeight));
// 运行结果为:2.75

不过很多时候,你可能想要得到两个或更多这样的结果,而你希望只需要一次操作就可以完成。在这种情况下,你可以使用summarizingInt工厂函数方法返回的收集器

:通过summarzing操作你就可以数出苹果列表元素的个数,并得到重量的总和、平均值、最大值和最小值。

IntSummaryStatistics intSummaryStatistics = apples.stream().collect(summarizingInt(Apple::getWeight));
// 运行结果为:IntSummaryStatistics{count=4, sum=11, min=1, average=2.750000, max=4}

打印intSummaryStatistics会得到以下输出:

IntSummaryStatistics{count=4, sum=9, min=1, average=2.250000, max=4}

它提供了(getter)方法来访问结果

4.连接字符串

Joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串

:把苹果列表中所有产地的名称连接起来并用逗号隔开

String collect = apples.stream().map(Apple::getCountry).collect(joining(","));
// 运行结果为:中国,奥大利亚,中国1,中国1

并且在joining工厂方法中,还可以定义第二个和第三个参数。分别为结果的前缀和后缀

String collect = apples.stream().map(Apple::getCountry).collect(joining(",","{","}"));
// 运行结果为:{中国,奥大利亚,中国1,中国1}

5.广义上的规约汇总

事实上,我们前面讨论的所有收集器,都是一个可以用reduceing工厂方法定义的规约过程的特殊情况而已。可以说,先前讨论的案例仅仅是为了方便程序员而已。

:可以用reduceing方法创建的收集器来计算你苹果列表的总质量

Integer totalWeight = apples.stream().collect(reducing(0, Apple::getWeight, (i, j) -> i + j));
// 运行结果为:11

同样,你可以使用下面这样单参数形式的reduceing来找到质量最大的苹果

Optional<Apple> collect2 = apples.stream().collect(reducing((t, j) -> t.getWeight() > j.getWeight() ? t : j));
// 运行结果为:Optional[Apple(weight=4, country=奥大利亚, color=red, carlories=0)]

你可能想知道Stream接口的collect和reduce有何不同,因为两种方法通常会获得相同的结果。下面我们通过一个例子来说明

:你可以使用reduce方法来实现toListCollector所做的工作,把流转化成list

    Stream<Integer> stream = Arrays.asList(1, 2, 3, 4, 5, 6).stream();
	List<Integer> reduce = stream.reduce(new ArrayList<>(), (List<Integer> l, Integer e) 	-> {
    	l.add(e);
   	 return l;
	}, (List<Integer> l1, List<Integer> l2) -> {
 	   l1.addAll(l2);
  	  return l1;
	});

这解决方案有两个问题一个语义问题和一个实际问题。语义问题在于,reduce方法旨在把两个值结合起来生成一个新值,它是一个不可变规约,与此相反,collect方法的设计就是要改变容器,从而累计要输出的结果,虽然以上代码实现了流至集合的转化,但你会发现,上面的代码片段是在滥用reduce方法,因为它在原地改变了作为累加器的List。以错误的语义使用reduce方法,还会造成一个实际问题:这个规约过程不能并行工作,因为由多个线程并发修改同一个数据结构,可能会破坏List本身。在这种情况下,如果你想要线程安全,就需要每次分配一个新的Lsit,而对象分配又会影响性能。这就是collect方法特别适合表达可变容器上规约的原因,更关键的是它适合并行操作。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值