java8函数式编程(四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/litte_frog/article/details/84848706

相关链接:

java8函数式编程(一)
java8函数式编程(二)
java8函数式编程(三)

  其中(一)是在没有开始了解java8新特性之前实际遇到的一个问题,使用for循环实现的代码很繁琐,于是尝试使用新的编码风格,最后很不完美的实现了需求。在进一步学习后,在(二)中完美实现,并对List常用的操作做了一个总结。最后在(三)中对并行化流与并行化数组的操作简单归纳。
  本节主要是对java8流处理一个总结以及一些细节的补充。另外,学习资料主要参考《Java8实战》与《Java8函数式编程》两本书籍。


Stream API

优点:

1 声明性 – 简洁、易读
2 可复合 – 灵活组合
3 可并行 – 高性能

常用操作:
1 筛选
filter  过滤
distinct  去重
limit   截断
skip   跳过
2 映射
map   转换
flatMap   扁平化转换
3 查找与匹配
anyMatch   至少有一个满足
allMatch   所有都满足
noneMatch   没有一个满足
findAny   返回任意一个
findFrist   返回第一个
4 归约
reduce   计算
count   元素个数
5 数值流
mapToInt     IntStream
mapToDouble  DoubleStream
max    最大
min    最小
average   平均
boxed   数值流装箱
range   范围内(不包括end)
reageClosed  范围内(包括end)
6 构建流
of
iterate
generate
7 收集器
collect

数值操作

从1到20间的奇数

IntStream.rangeClosed(1, 20)
                .filter(n -> n%2 != 0)
                .forEach(System.out::println);

100以内奇数的和

int sum = IntStream.range(1, 100)
                .filter(n -> n % 2 != 0)
                .sum();

斐波那契数列(前20位)

Stream.iterate(new int[]{0,1}, n -> new int[]{n[1], n[0] + n[1]})
                .limit(20)
                .map(n -> n[0])
                .forEach(System.out::println);

collect收集器

准备数据依然使用List<Frog>,frog的属性有name名称,age年龄,color颜色,size体积。

        List<Frog> list = new ArrayList<>();
        list.add(new Frog("one", 2 , "red", 5.1));
        list.add(new Frog("two", 3 , "red", 5.0));
        list.add(new Frog("three", 2 , "blue", 5.5));
        list.add(new Frog("four", 1 , "blue", 5.3));
        list.add(new Frog("five", 5 , "yellow", 5.1));
        list.add(new Frog("six", 3 , "yellow", 5.2));

collect方法参数Collector。Collector接口中方法实现对流执行的操作,Collectors实现类提供很多静态方法,下面是对这些静态方法的说明。
为了代码简洁,导入Collectors类中所有方法:

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

summaryStatistics

该方法可以一次性获取到数量、总和、最大值、最小值和平均数

DoubleSummaryStatistics summaryStatistics = frogs.stream()
                .collect(summarizingDouble(Frog::getSize));
System.out.println(summaryStatistics);

打印结果

DoubleSummaryStatistics{count=6, sum=31.200000, min=5.000000, average=5.200000, max=5.500000}

也可以直接通过summaryStatistics 的getter方法获取某一项。

joining

字符串的连接,方法重载

String names = frogs.stream()
                .map(Frog::getName)
                .collect(joining(" ", "[", "]"));

打印结果

[one two three four five six]

groupingBy

1、分组与多级分组

Map<String, List<Frog>> map = frogs.stream()
                .collect(groupingBy(Frog::getColor));

Map<String, Map<Volume, List<Frog>>> map1 = frogs.stream()
                .collect(groupingBy(Frog::getColor,
                        groupingBy(frog -> {
                            if (frog.getSize() < 5.2) {
                                return Volume.SMALL;
                            } else {
                                return Volume.BIG;
                            }
                        })));
                    
public enum Volume {
        BIG,
        SMALL
}

打印结果

{
 red=[Frog{name='one', age=2, color='red', size=5.1}, Frog{name='two', age=3, color='red', size=5.0}],
 blue=[Frog{name='three', age=2, color='blue', size=5.5}, Frog{name='four', age=1, color='blue', size=5.3}],
 yellow=[Frog{name='five', age=5, color='yellow', size=5.1}, Frog{name='six', age=3, color='yellow', size=5.2}]
 }

{
 red={
 	SMALL=[Frog{name='one', age=2, color='red', size=5.1}, Frog{name='two', age=3, color='red', size=5.0}]
 	},
 blue={
 	BIG=[Frog{name='three', age=2, color='blue', size=5.5}, Frog{name='four', age=1, color='blue', size=5.3}]
 	}, 
 yellow={
 	BIG=[Frog{name='six', age=3, color='yellow', size=5.2}], 
 	SMALL=[Frog{name='five', age=5, color='yellow', size=5.1}]
 	}
}

groupingBy第二个参数可以使用mapping方法。例:

Map<String, HashSet<Volume>> map4 = frogs.stream()
                .collect(groupingBy(Frog::getColor,
                        mapping(frog -> {
                            if (frog.getSize() < 5.2) {
                                return Volume.SMALL;
                            } else {
                                return Volume.BIG;
                            }
                        }, toCollection(HashSet::new))));

2、分组后的数据操作,比如按颜色分组,求每组中size最大的元素

Map<String, Frog> map2 = frogs.stream()
                .collect(groupingBy(Frog::getColor,
                        collectingAndThen(minBy(Comparator.comparingDouble(Frog::getSize)), Optional::get)));

等价的写法

Map<String, Frog> map3 = frogs.stream()
                .collect(toMap(Frog::getColor,
                        Function.identity(),
                        BinaryOperator.minBy(Comparator.comparingDouble(Frog::getSize))));

Function.identity():返回一个总是返回其输入参数的函数

partitioningBy

分区,分组的特殊情况,将数据分为true和false两组

Map<Boolean, Long> map = frogs.stream()
                .collect(partitioningBy(t -> t.getAge() > 1, counting()));

打印结果

{false=1, true=5}

实践

项目中遇到一个两级分组的问题,需要先按名称分组,然后再按标志(0/1)分组,对分组后的数量求和(库里String类型,实际需要按double类型求和)。代码如下:

Map<String, Map<String, Double>> map = list.stream()
                .collect(Collectors.groupingBy(Frog::getMc,
                        Collectors.groupingBy(Frog::getBz,
                                Collectors.summingDouble(t -> Double.parseDouble(t.getSl())))));

上面写法有一个计算精度问题,于是改成了一下方式:

Map<String, Map<String, BigDecimal>> map = list.stream()
                .collect(Collectors.groupingBy(Frog::getMc,
                        Collectors.groupingBy(Frog::getBz,
                                Collectors.reducing(new BigDecimal(0),
                                        t -> new BigDecimal(t.getSl()),
                                        (BigDecimal a, BigDecimal b) -> a.add(b)))));

用BigDecimal类型求和后精度是没有问题的,初除此之外还能对结果进行一些处理,比如保留两位小数(四舍五入)

BigDecimal number = new BigDecimal();
number.setScale(2,BigDecimal.ROUND_HALF_UP);

BigDecimal更多用法可以参考:
博主:星朝
链接:BigDecimal的用法详解(保留两位小数,四舍五入,数字格式化,科学计数法转数字,数字里的逗号处理)

猜你喜欢

转载自blog.csdn.net/litte_frog/article/details/84848706