Java工程师面试1000题151- Java8新特性之Stream API

151、了解Stream API吗?说说它

Java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个是StreamAPI。Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用StreamAPI对集合数据进行操作,就类似于使用SQL执行的数据库查询一样简单。也可以使用StreamAPI来执行并行操作。简而言之,StreamAPI提供了一种高效且易于使用的数据处理方式。

流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”

注意:

  • Stream自己不会存储元素
  • Stream不会改变源对象,相反,他们会返回一个持有结果的新Stream。
  • Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才会触发执行。

Stream的操作可分为三个步骤:

  1. 创建Stream:可以通过集合、数组来获取一个流
  2. 中间操作:一个中间操作链,对数据源的数据进行处理
  3. 终止操作:执行中间操作链,并产生结果

第一步:创建Stream(4种方式)

    //创建流的四种方式
    @Test
    public void test1(){
        //1、通过Collection系列集合提供的stream()或者parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        //2、通过Arrays中的静态方法stream()来获取流
        Integer[] emps = new Integer[10];
        Stream<Integer> stream2 = Arrays.stream(emps);

        //3、通过Stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("aa", "bb", "ab");

        //4、通过迭代创建无限流
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        stream4.limit(10).forEach(System.out::println);

        //4、通过生成创建无限流
        Stream<Double> stream5 = Stream.generate(() -> Math.random());
        stream5.limit(5).forEach(System.out::println);
    }

第二步:对流进行一系列中间操作

filter——接收Lambda,从流中排除某些元素

limit——截断流,使其元素不超过给定数量

skip(n) ——跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个时,则返回一个空流。

distinct——筛选,通过流所生成元素的hashCode()和equals()去除重复元素

    //中间操作
    List<Employee> employees = Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",58,5555.55),
            new Employee("王五",26,3333.33),
            new Employee("赵柳",36,6666.66),
            new Employee("天琪",12,8888.88),
            new Employee("天琪",12,8888.88));
    @Test
    public void test2(){
        //过滤年龄大于35岁的,并把其打印出来
        //多个中间操作可以连接起来行成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理
        Stream<Employee> stream = employees.stream().filter((e) -> e.getAge() > 35);
        //执行终止操作,一次性触发全部执行——惰性求值或延迟加载
        stream.forEach(System.out::println);
    }

    @Test
    public void test3(){
        //取出工资大于5000的前两个
        employees.stream().filter((e) -> e.getSalay() > 5000).limit(2).forEach(System.out::println);
    }

    @Test
    public void test4(){
        //去除前两个工资大于5000的,然后打印其他工资大于5000的
        employees.stream().filter((e) -> e.getSalay() > 5000).skip(2).forEach(System.out::println);
    }

    @Test
    public void test5(){
        //去重复  注意:必须要重写employee的hashCode和equals方法,否则无法去重
        employees.stream().filter((e) -> e.getSalay() > 5000).skip(2).distinct().forEach(System.out::println);
    }
public class Employee {
    private String name;
    private int age;
    private double salay;

    public Employee(String name, int age, double salay) {
        this.name = name;
        this.age = age;
        this.salay = salay;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalay() {
        return salay;
    }

    public void setSalay(double salay) {
        this.salay = salay;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Employee employee = (Employee) o;

        if (age != employee.age) return false;
        if (Double.compare(employee.salay, salay) != 0) return false;
        return name != null ? name.equals(employee.name) : employee.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        temp = Double.doubleToLongBits(salay);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salay=" + salay +
                '}';
    }
}

映射操作:

map——接收Lambda,将元素转换为其他形式或者提取元素中的信息,接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

    @Test
    public void test6(){
        //map——接收Lambda,将元素转换为其他形式
        List<String> list = Arrays.asList("aaa","bda","cca","desa","daa");
        //把小写转换为大写
        list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
        System.out.println("----------------------------------");

        //map——接收Lambda,提取元素中的信息
        employees.stream().map(Employee::getName).forEach(System.out::println);
    }

flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后再把所有流连接成一个流。

如果没有flatMap,想把list里面的每个元素都拆分后再换行,需要下面的实现方法:

  public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }
    @Test
    public void test7(){
        List<String> list = Arrays.asList("aaa","bda","cca","desa","daa");
        Stream<Stream<Character>> stream = list.stream().map(TestStreamAPI::filterCharacter);
        stream.forEach((sm) -> sm.forEach(System.out::println));
    }

使用flatMap后,简单一点:

    @Test
    public void test8(){
        List<String> list = Arrays.asList("aaa","bda","cca","desa","daa");
        Stream<Character> sm = list.stream().flatMap(TestStreamAPI::filterCharacter);
        sm.forEach(System.out::println);
    }

查找与匹配:

allMatch——检查是否匹配所有元素

anyMatch——检查是否至少匹配一个

noneMatch——检查是否没有匹配所有元素

findFirst——返回第一个元素

findAny——返回当前流中的任意元素

count——返回流中元素的个数

max——返回流中最大值

min——返回流中最小值

    @Test
    public void test10(){
        boolean match = employees.stream().allMatch((e) -> e.getName().equals("张三"));
        System.out.println(match);

        boolean match1 = employees.stream().anyMatch((e) -> e.getName().equals("张三"));
        System.out.println(match1);

        boolean match2 = employees.stream().noneMatch((e) -> e.getName().equals("张三"));
        System.out.println(match2);

        Optional<Employee> first = employees.stream().sorted((e1, e2) -> -Double.compare(e1.getSalay(), e2.getSalay())).findFirst();
        System.out.println(first.get());

        Optional<Employee> optional = employees.stream().filter((e) -> e.getName().equals("张三")).findAny();
        System.out.println(optional.get());

        long count = employees.stream().count();
        System.out.println(count);

        Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalay(), e2.getSalay()));
        System.out.println(max.get());

        Optional<Double> min = employees.stream().map(Employee::getSalay).min(Double::compare);
        System.out.println(min.get());
    }

归约操作:可以将流中的元素反复结合起来,得到一个值 Map-Reduce模式,适用于大数据Hadoop框架编程。

    @Test
    public void test11(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        //求集合的和
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);

        //求员工工资的总和。总和有可能为空,所以封装到 Optional中去
        Optional<Double> reduce = employees.stream().map(Employee::getSalay).reduce(Double::sum);
        System.out.println(reduce.get());
    }

收集操作:collect()将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

Collector接口中方法的实现决定了如何对流执行收集操作(比如收集到List、Set、Map)。但是Collectors实用类提供了许多静态方法,可以方便地创建常见的收集器实例。

    @Test
    public void test12(){
        //收集员工姓名,放入list中
        List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
        list.forEach(System.out::println);

        //收集员工姓名,放入set中,去重
        Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
        System.out.println(set);

        //收集到特殊集合中——HashSet
        HashSet<String> hashSet = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);

        //获取集合中员工总数
        Long aLong = employees.stream().collect(Collectors.counting());
        System.out.println(aLong);

        //员工平均工资
        Double aDouble = employees.stream().collect(Collectors.averagingDouble(Employee::getSalay));
        System.out.println(aDouble);

        //员工工资总和
        DoubleSummaryStatistics sum = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalay));
        System.out.println(sum);

        //工资最大的员工
        Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalay(), e2.getSalay())));
        System.out.println(max.get());

        //工资的最小值
        Optional<Double> min = employees.stream().map(Employee::getSalay).collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());
    }

猜你喜欢

转载自blog.csdn.net/qq_21583077/article/details/88786497