• Java8聚合操作


    Java聚合操作(Aggregate Operations)是对一堆数据进行处理的新的操作方法,我们知道,如果想对一堆数据进行处理,比如一个List对象中的数据进行处理,传统的操作就是遍历List数据然后进行处理;现在有一种新的可以提供相同功能的操作方法,就是聚合操作(Aggregate Operations),它常与与lambda表达式绑定使用,在lambda表达式使用总结一节已经使用到了,这里举一个例子,如下代码,要求打印出List列表中性别为MALE的对象信息: 

    public class Main {
        public static void main(String[] args) {
            Person[] pers = {new Person(12, "h1",Person.Sex.FEMALE),
                    new Person(23, "h2",Person.Sex.MALE),
                    new Person(14, "h3",Person.Sex.FEMALE),
                    new Person(2, "h4",Person.Sex.MALE)};
            List<Person> personList= Arrays.asList(pers);
            //传统的for-each操作
            for(Person p: personList){
                if(p.getGender()== Person.Sex.MALE){
                    System.out.print(p+" ");
                }
            }
            System.out.println("\n--------分割线--------");
            //聚合操作
            personList.stream().filter(obj->obj.getGender()==Person.Sex.MALE).forEach(obj->System.out.print(obj+" "));
        }
    }
    @Data
    @ToString
    class Person{
        int age;
        String name;
        Sex gender;
        public Person(int age,String name,Sex gender){
            this.age=age;
            this.name=name;
            this.gender=gender;
        }
        public Person(int age,String name){
            this.age=age;
            this.name=name;
        }
        public enum Sex {
            MALE,FEMALE
        }
    }

     上面的代码中,聚合操作的代码只一行就解决问题了,聚合操作涉及到两个概念:管道(pipeline)和流(stream),解释如下 

    管道(pipeline)是作用在源数据上的一些聚合操作,可以理解为数据源和多个聚合操作符号的组合,如上面的filter、forEach,一个管道包含以下内容:
    1、一个数据源:可以是一个collection,array,生成函数,或则IO channel,本例中是一个集合。
    2、0个或则多个中间操作,例如filter,可以产生一个新的流。
    3、一个终止操作,例如forEach,产生一个非流的结果(non-stream result)。

    流(stream)是一个个的元素,它不是某种数据结构,不用于存储数据,它的作用就是通过管道从数据源获取并传输数据,在上面的代码中集合personList通过调用stream()方法生成一个流。

    归约操作:返回一个结果对象的操作,例如下面的示例:

     double average = roster

        .stream()
        .filter(p -> p.getGender() == Person.Sex.MALE)
        .mapToInt(Person::getAge)
        .average()
        .getAsDouble(); 

    上面的示例中,计算出所有男性对象的一个平均年龄,在聚合操作(aggregate operation)中有很多终止操作(terminal operation)只返回一个结果,这样的操作就是归约操作(reduction operation),除了返回一个值之外,还有一些归约操作返回一个集合对象。JDK提供了一个一般性的归约操作:reduce和collect方法。具体使用如下:

    1、reduce方法,以一个示例引用使用方法,例如求所有人的年龄和: 

    Integer totalAge = roster
        .stream()
        .mapToInt(Person::getAge)
        .sum();
    Integer totalAgeReduce = roster
       .stream()
       .map(Person::getAge)
       .reduce(
           0,
           (a, b) -> a + b);

    上面的示例中的两种计算方法得出的结果是一样的,更多的使用案例可参考:https://www.cnblogs.com/qinhao517/p/9197885.html

    2、collect方法,reduce是产生一个新值,而collect方法是来修改一个已经存在的值,还是上面的求平均年龄为例,我们可以创建一个对象用于存储计算结果:

     class Averager implements IntConsumer{

        private int total = 0;
        private int count = 0;        
        public double average() {
            return count > 0 ? ((double) total)/count : 0;
        }        
        public void accept(int i) { total += i; count++; }
        public void combine(Averager other) {
            total += other.total;
            count += other.count;
        }
    }
    Averager averageCollect = roster.stream()
        .filter(p -> p.getGender() == Person.Sex.MALE)
        .map(Person::getAge)
        .collect(Averager::new, Averager::accept, Averager::combine);
                       
    System.out.println("Average age of male members: " +
        averageCollect.average()); 

    上面的计算中,collect传入了三个参数,返回了一个Averager对象,传入的三个参数分别是:

    supplier:是一个工厂函数,用于创建新的实例对象

    accumulator:累加器函数用于将一个流元素合成一个结果容器,在这个示例中,它通过给count变量加一和将流元素(stream element)中的值(getAge方法获取到的年龄)加给total来修改Averager的结果

    combiner:合并函数拿出两个结果容器(result container)来合并他们的内容,在这个示例中,它通过将其他结果容器的count值和total值累加到自己的(Averager::new 创建的对象)元素中来生成最终的结果

    上面的示例中,filter方法过滤出所有男性的对象,map方法通过getAage获取年龄将人的对象单个的流对象映射成一个年龄值,collect方法通过这些年龄值的运算返回一个最终的结果,结果值是利用Averager对象最为媒介来返回的。

    collect还可以将对象中的某个值抽取出来作为一个list返回给用户:

    List<String> namesOfMaleMembersCollect = roster
        .stream()
        .filter(p -> p.getGender() == Person.Sex.MALE)
        .map(p -> p.getName())
        .collect(Collectors.toList());

    除了上面的操作,下面还有其他的示例:

    Map<Person.Sex, List<Person>> byGender =
        roster
            .stream()
            .collect(
                Collectors.groupingBy(Person::getGender));

    上面的示例中,是通过性别,将不同的对象放到各自性别的List集合中,那我只想获取不同性别的对象的名字呢?

    Map<Person.Sex, List<String>> namesByGender =
        roster
            .stream()
            .collect(
                Collectors.groupingBy(
                    Person::getGender,                      
                    Collectors.mapping(
                        Person::getName,
                        Collectors.toList())));

    也可以获取每个性别的所有对象的年龄之和:

    Map<Person.Sex, Integer> totalAgeByGender =
        roster
            .stream()
            .collect(
                Collectors.groupingBy(
                    Person::getGender,                      
                    Collectors.reducing(
                        0,
                        Person::getAge,
                        Integer::sum)));

    上面的操作更复杂一些,reducing函数进行归约操作,上面的0是一个计算的基础标识,在此标识的基础上进行操作(operation),至于什么操作,第三个参数的Integer::sum给出了答案,Person::getAge对应的是一个mapper操作,该mapper利用getAge方法抽取出年龄。

    Map<Person.Sex, Double> averageAgeByGender = roster
        .stream()
        .collect(
            Collectors.groupingBy(
                Person::getGender,                      
                Collectors.averagingInt(Person::getAge)));

    上面的是计算每个性别的年龄平均值。

    还有其他很多类型的聚合操作,一个语法的熟练掌握还是需要靠实践来完成....

  • 相关阅读:
    改变UINavigationBar颜色需要注意的地方
    更改ios状态栏颜色
    多线程简单介绍
    GCD多线程的实现方法
    NSUserDefaults的简单介绍
    NSFileManager的简单介绍,在沙盒目录下对文件进行增删改查
    在plist文件中增删改查
    frame bound center等之间的关系
    《汇编语言》——王爽 第五章 [BX]和loop指令
    《汇编语言》——王爽 第四章 第一个程序
  • 原文地址:https://www.cnblogs.com/codeMedita/p/15850097.html
Copyright © 2020-2023  润新知