一、前言

在java8中添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。让你在处理数据时代码像sql一样整洁。
在本文中,会先简单介绍Stream的基本使用,会在文章的最后使用相对复杂的逻辑对比使用遍历与流处理集合的差别与优劣。
致谢:本文参考博客

二、Stream的操作流程

1、创建Stream
创建一个流,流内的数据可以从一个数据源,如集合、数组中获取。

2、中间操作
将初始流中的数据通过某些操作,产生一个符合条件的新流。

3、终止操作
一个终止操作,产生流处理完成后的结果。这个操作后,这个流就不能再使用了。

Person类

package POJO;


import lombok.Data;

import java.util.Objects;

/**
 * @author wangtianqi
 * @create 2020-08-19 15:59
 */
@Data
public class Person {

    private String name;
    private Integer age;
    private String country;
    private char sex;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    public Person(String name, Integer age, String country, char sex) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", country='" + country + '\'' +
                ", sex=" + sex +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return getName().equals(person.getName()) &&
                getAge().equals(person.getAge());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }
}

Country类

package POJO;

import lombok.Data;

/**
 * @author wangtianqi
 * @create 2020-08-27 16:33
 */
@Data
public class Country {

    private String countryName;

}

这是我的test代码,其中完成了一些基本的业务逻辑,并加以解释,如果以后有时间再整理清楚。

import POJO.Country;
import POJO.CountryCountAge;
import POJO.Person;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/** Stream 流操作相关,将数据转换为流进行操作
 * @author wangtianqi
 * @create 2020-08-27 8:59
 */
public class Test1 {

    public static void main(String[] args) throws IOException {

        List<Person> personList = new ArrayList<>();
        //personList.add(new Person("欧阳霜",18,"中国",'F'));
        personList.add(new Person("欧阳雪",18,"中国",'F'));
        personList.add(new Person("Tom",24,"美国",'M'));
        personList.add(new Person("Harley",22,"英国",'F'));
        personList.add(new Person("向天笑",23,"中国",'M'));
        personList.add(new Person("李康",22,"中国",'M'));
        personList.add(new Person("小梅",20,"中国",'F'));
        personList.add(new Person("何雪",21,"中国",'F'));
        personList.add(new Person("李康",22,"中国",'M'));


        // 从集合中获取数据流
        Stream<Person> personStream = personList.stream();

        // 执行中间操作,例如筛选18岁以上的人,流不会改变数据源,会返回一个新的流,流中存放着符合条件的元素
        Stream<Person> adultStream = personStream.filter(person -> person.getAge() > 18 && person.getCountry().equals("中国"));
        /**
         * 可以调用collect方法,方法中引用Collectors中的静态方法将流转换为集合类型
         * 在collect方法中传入Collectors类中的静态方法调用
         * Collectors类中定义了很多声明式的实用方法
         * */
        List<Person> adultList = adultStream.collect(Collectors.toList());
        int count = adultList.size();
        System.out.println(count);
        adultList.forEach(System.out::println);

        // 通过国家分组,并获取每个组中年龄的和,并返回一个键值对对象
        Map<String, Integer> result = personList.stream().collect(Collectors.groupingByConcurrent(Person::getCountry,Collectors.summingInt(Person::getAge)));
        System.out.println(result);

        /**
         * max方法返回一个Optional类的对象,它是一种包装器对象,要么包装类型T类型的对象,要么不包装对象。
         * 在这里max方法返回一个包装着Person类Optional对象。调用Optional中的get方法,可以获得包装类中的对象
         * 如果集合中有两个符合条件的对象,方法按照集合中数据的顺序返回第一个对象
         * min同理
         */
        Person maxAgePerson = personList.stream().max(Comparator.comparing(Person::getAge)).get();
        Person minAgePerson = personList.stream().min(Comparator.comparing(Person::getAge)).get();
        System.out.println(maxAgePerson);
        System.out.println(minAgePerson);

        // 通过map方法,可以将流转换为在map方法中返回值的类型
        List<Country> countryList = personList.stream().map(person -> {
            Country country = new Country();
            country.setCountryName(person.getCountry());
            return country;
        }).distinct().collect(Collectors.toList());

        countryList.forEach(System.out::println);

        // 获取所有人的年龄总和,这里调用reduce方法,方法中传入Integer类中的sum方法引用,获得一个Optional对象,并调用get方法
        Integer countAge = personList.stream().map(person -> person.getAge()).reduce(Integer::sum).get();
        /**
         * 这里还可以直接调用mapToInt方法,将流转换为IntStream(整形流),并直接调用IntStream类中的sum方法。
         * 同样的还有mapToDouble方法。
         */
        Integer countAge2 = personList.stream().mapToInt(Person::getAge).sum();
        System.out.println("所有人的年龄总和为:" + countAge2);
        System.out.println("所有人的年龄总和为:" + countAge);


        // 分别统计集合中18岁到29岁Person的数量。
        for (Integer i = 18; i < 30; i++) {
            // Lambda表达式中不可以使用非final修饰的变量,所以这里定义一个中间量I
            final Integer I = i;
            // 通过filter获得过滤后的流,再将流通过map方法映射为想要返回的类型的流,再通过collect方法,将流转换为集合
            List<Integer> age = personList.stream().filter(person -> person.getAge() == I).map(Person::getAge).collect(Collectors.toList());
            // 或者直接通过流来输出结果
            Stream<Integer> ageStream = personList.stream().filter(person -> person.getAge() == I).map(Person::getAge);
            System.out.print(ageStream.count()+" ");
            System.out.print(age.size()+" ");
            System.out.println();
        }
    }
}

map方法与flatMap方法的区别