java8新特性---Stream流

知识点:

Stream流的作用

Steam流是什么

Stream流怎么使用,使用Stream的步骤:创建,中间操作,终止操作

创建流的五种方式,中间操作API以及终止操作API

 

Stream流可能是java8新特性中最成功的、也是用的最爽的一个特性了。首先用简单的介绍一下它的作用

Stream流的作用

有了Stream流,从此集合的操作就可以告别繁琐的for循环了。使用Stream流对集合进行操作,就类似于使用SQL执行数据库的查询,也可以使用Stream API来并行执行操作。简而言之,Stream流提供了一种高效易用的处理集合数据的方式。那接下来就举一个小栗子来说明它的强大之处。

代码示例:

首先需要创建一个员工集合,在博客末尾会贴出Employee类的代码,员工属性分别是:id,姓名,年龄,工资和状态

    private List<Employee> emps= Arrays.asList(
            new Employee(103,"萧何",44,8888.77, Employee.Status.BUSY),
            new Employee(101,"刘邦",55,10000.11,Employee.Status.FREE),
            new Employee(102,"张良",45,9999.99,Employee.Status.VOCATION),
            new Employee(106,"陈平",38,5555.55,Employee.Status.BUSY),
            new Employee(104,"韩信",33,3333.33,Employee.Status.FREE),
            new Employee(105,"周勃",29,6666.66,Employee.Status.FREE),
            new Employee(105,"周勃",29,6666.66,Employee.Status.FREE)
    );

需求1:获取年龄在40岁以上的员工集合

需求2:获取工资在7000元以上的员工

在没有stream流之前的代码可能是这样

//根据年龄过滤   
 public List<Employee> filterEmployeeAge(int age){
        List<Employee> ageEmps=new ArrayList<>();
        for(int i=0;i<emps.size();i++){
            Employee emp=emps.get(i);
            if(emp.getAge()>age){
                ageEmps.add(emp);
            }
        }
        return ageEmps;
    }
//根据薪水过滤
    public List<Employee> filterEmploySalary(double salary){
        List<Employee> salaryEmps=new ArrayList<>();
        for(int i=0;i<emps.size();i++){
            Employee emp=emps.get(i);
            if(emp.getSalary()>salary){
                salaryEmps.add(emp);
            }
        }
        return salaryEmps;
    }

    @Test
    public void test1(){
        List<Employee> ageEmps=filterEmployeeAge(40);
        for(Employee emp:ageEmps){
            System.out.println(emp);
        }
        System.out.println("------------------");
        List<Employee> salaryEmps=filterEmploySalary(7000);
        for(Employee emp:salaryEmps){
            System.out.println(emp);
        }
    }

当然了。上述代码可以使用策略模式进行优化,但其不是本篇的重点,在这里就不进行详细的介绍了。那么如果我们先要根据年龄过滤,再根据薪水过滤,那么就又需要写一个类似的方法,代码中充斥着大量的重复代码。做了很多重复操作,唯一不同的可能就是过滤条件不同了。那接下来使用Stream流又是什么样的呢

使用Stream流的代码示例:

    @Test
    public void test2(){
        //获取年龄大于40岁的员工
        emps.stream()
                .filter((e)->e.getAge()>40)
                .forEach(System.out::println);
        System.out.println("------------------");
        //获取薪水大于7000的员工
        emps.stream()
                .filter((e)->e.getSalary()>7000)
                .forEach(System.out::println);

    }

其运行结果是一致的,如下:

上述案例对比可以看出。Stream流大大的简化了对集合的操作。

什么是Stream流?

简单的讲:Stream流就是一个数据渠道,用于操作数据源(集合、数组)所产生的元素序列。

注意点:

  • Stream自己不会存储元素
  • Stream不会改变数据源对象,返回一个持有结果的新Stream
  • Stream 操作是延迟执行,这意味着它们会等到需要的结果才执行

如何使用Stream流呢?

使用Stream流需要三个步骤:

  1. 创建Stream流:根据数据源(集合、数组),获取一个Stream流
  2. 中间操作:对数据源进行一系列处理
  3. 终止操作:执行中间操作,并产生结果

一)、创建Stream流的方式

1、java8对Collection接口进行了扩张,提供了两个获取流的方法

 default  Stream<E>   stream():返回一个顺序流

default  Stream<E>   parallelStream():返回一个并行流

代码示例:使用员工集合emps创建集合

Stream<Employee> stream = emps.stream(); //创建一个顺序流
Stream<Employee> parallelStream = emps.parallelStream(); //创建一个并行流

 

2、java8中Arrays类中提供了一个静态的方法,使用数组创建流

public  static <T>  Stream<T>   stream(T[] array):返回一个流

重载形式, 能够处理对应基本类型的数组:

 public  static  IntStream  stream(int[] array)

 public  static  LongStream  stream(long[] array)

public  static   DoubleStream  stream(double[] array)

Integer[] nums={2,3,4,5};
Stream<Integer> arrStream = Arrays.stream(nums);//使用数组创建一个流

 

3、使用Stream提供的静态方法of(),显示的创建一个流。它可以接收任意参数

public  static<T>  Stream<T>   of(T... values)

Stream<String> ofStream = Stream.of("hello", "java", "8", "新特性");

4、创建无限流,可以使用Stream.iterate()方法和Stream.generate()方法,创建无限流

迭代:Public  static<T>  Stream<T>   iterate(final T seed, final UnaryOperator<T> f)

生成:public static<T> Stream<T> generate(Supplier<T> s)

Stream<Integer> iterateStream = Stream.iterate(0, (x) -> x + 2);
iterateStream.limit(10).forEach(System.out::println);
Stream<Double> generateStream = Stream.generate(Math::random);
generateStream.limit(2).forEach(System.out::println);

二)、中间操作

多个中间操作可以连接形成一条流水线,除非流水线触发终止操作,否则中间操作不会执行任何处理,而在终止操作时会对中间操作全部处理,这被称之为“惰性求值”。

举个小栗子简单说明:还是对员工集合进行操作,没有调用终止操作

  @Test
    public void test4(){
        emps.stream().filter((e)->{
            System.out.println("只有中间操作,无终止操作");
            return e.getAge()>35;
        }); //filter()方法时一个中间操作方法
    }

执行结果:

有调用终止操作的示例:

    @Test
    public void test5(){
        emps.stream().filter((e)->{//filter()方法时一个中间操作方法
            System.out.println("短路");
            return e.getAge()>35;
        }).limit(2).forEach(System.out::println);  //limit()方法为中间操作方法
    }

执行结果:

中间操作API汇总:

一)、筛选与切片

filter() - 接收一个lambda,从流程中排除某些元素

limit(n) - 截断了,使元素不得超过给定数量

skip(n) - 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补

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

二)映射:

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

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

三)排序

sorted() -- 自然排序

Sorted(Comparator com) --定制排序

 

中间操作API详解

一)、筛选与切片

还是使用员工集合对下方法进行说明介绍,结果就不进行粘贴了。

filter() - 接收一个lambda,从流程中排除某些元素

代码示例:

    public void test6(){
        emps.stream()
                .filter((e)->e.getAge()>35)  //过滤员工年龄大于35岁的员工
                .forEach(System.out::println);
    }

limit(n) - 截断流,使元素不得超过给定数量

代码示例:

 @Test
    public void test7(){
        emps.stream()
                .filter((e)->e.getAge()>35)  //过滤员工年龄大于35岁的员工
                .limit(2)  //且只获取前2条数据
                .forEach(System.out::println);
    }

skip(n) - 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补

代码示例:

    @Test
    public void test8(){
        emps.stream()
                .filter((e)->e.getAge()>35)  //过滤员工年龄大于35岁的员工
                .skip(2)  //跳过前2条数据,获取之后的数据
                .forEach(System.out::println);
    }

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

代码实列:

    @Test
    public void test9(){
        emps.stream()
                .filter((e)->e.getAge()<35)  //过滤员工年龄大于35岁的员工
                .distinct() //如果流中有重复元素,则去除重复元素
                .forEach(System.out::println);
    }

二)映射:

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

代码示例一:获取所有员工的姓名,并添加到集合中返回

    @Test
    public void test10(){
        Stream<String> namesStream=emps.stream()
                .map((e)->e.getName()); //提取所有员工的姓名,生成一个新的存储字符串的流
        List<String> names = namesStream.collect(Collectors.toList());//这是调用一个终结操作,作用就是把流转化为集合
        System.out.println(names);
    }

输出结果:

代码示例二:把字符串数组中的小写字符串转换为大写

   @Test
    public void test11(){
        String[] strs={"aaa","bbb","ccc","ddd"};
        Stream<String> strStream= Arrays.stream(strs)
                .map(String::toUpperCase); //把流中所有的字符串转换为大写,
        String[] strs1= strStream.collect(Collectors.toList()).toArray(new String[0]);
        for(int i=0;i<strs.length;i++){
            System.out.print(strs1[i]+" ");
        }
    }

输出结果:

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

flatMap和map()的区别,有点像集合中的add()方法和addAll()方法

 

三)排序

sorted() -- 自然排序

sorted(Comparator com) --定制排序

@Test
    public void test12(){
        //自然排序
        List<String> strsList=Arrays.asList("aaa","ccc","bbb");
        strsList.stream()
                .sorted().forEach(System.out::println);
        System.out.println("------------");
        //定制排序,员工如果年龄不同,按照年龄排序,年龄相同,按照姓名排序
         emps.stream()
                .sorted((x, y) -> {
                    if(x.getAge()==y.getAge()){
                        return x.getName().compareTo(y.getName());
                    }else {
                        return Integer.compare(x.getAge(),y.getAge());
                    }
                }).forEach(System.out::println);
    }

输出结果:

3)、终止操作

执行中间操作,产生结果

终止操作API汇总

一)、查找与匹配:

allMatch  --  检查是否匹配所有元素,

anyMatch -- 检查至少匹配一个元素

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

findFirst --  返回第一个元素

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

count -- 返回流中的总个数

max -- 返回流中的最大值

min --  返回最小值

二)、归约

reduce(T identity,BinaryOperator ) /reduce(BinaryOperator): 可以将流中的元素反复结合起来,得到一个值

三)、收集

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

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

 

终止操作API详解:

allMatch  --  检查是否匹配所有元素,

anyMatch -- 检查至少匹配一个元素

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

代码示例:
 

@Test
    public void test13(){
        //所有员工处于忙碌状态,返回true,不是,返回false
       boolean allBusy= emps.stream()
                .allMatch((e)->e.getStatus().equals(Employee.Status.BUSY));
        System.out.println("所有员工都处于忙碌状态吗?"+allBusy);
        //至少有一个员工处于忙碌状态,返回true,不是,返回false
        boolean anyBusy= emps.stream()
                .anyMatch((e)->e.getStatus().equals(Employee.Status.BUSY));
        System.out.println("存在至少一个员工处于忙碌状态吗?"+anyBusy);
        //没有员工处于忙碌状态,返回true,否则返回false
        boolean noneBusy= emps.stream()
                .noneMatch((e)->e.getStatus().equals(Employee.Status.BUSY));
        System.out.println("没有员工处于忙碌状态吗?"+anyBusy);
    }

执行结果:

 

findFirst --  返回第一个元素

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

    @Test
    public void test14(){
        //获取薪水最低的员工
        Optional<Employee> opEmp= emps.stream()
                .sorted((x,y)->Double.compare(x.getSalary(),y.getSalary())) //根据薪水排序
                .findFirst(); //获取排在第一个位置的员工
        System.out.println(opEmp.get());
        //随机获取一个处于忙碌状态的员工
        Optional<Employee> busyEmp=emps.stream()
                .filter((e)->e.getStatus().equals(Employee.Status.BUSY)) //根据状态进行过滤
                .findAny(); //从忙碌状态的员工中随机获取一个
        System.out.println(busyEmp.get());
    }

执行结果:

Optional容器类,它提供了另外一种解决空指针异常的方案,可能会在之后的博客中详解

 

count -- 返回流中的总个数

max -- 返回流中的最大值

min --  返回最小值

@Test
    public void test15(){
        //处于空闲状态的员工总数
       long count= emps.stream()
               .filter((e)->e.getStatus().equals(Employee.Status.FREE))
               .count();
        System.out.println("处于空闲状态的员工总数:"+count);

        //获取最高的工资
       Optional<Double> opSalary= emps.stream()
                .map(Employee::getSalary)
                .max(Double::compare);
        System.out.println("最高的工资是:"+opSalary.get());

        //获取工资最低的员工
        Optional<Employee> opEmp =emps.stream()
                .min((x,y)->Double.compare(x.getSalary(),y.getSalary()));
        System.out.println("工资最低的员工:"+opEmp.get());
    }

运行结果:

二)、归约

reduce(T identity,BinaryOperator )  / reduce(BinaryOperator): 可以将流中的元素反复结合起来,得到一个值

    @Test
    public void test17(){
        List<Integer> intList=Arrays.asList(1,2,3,44,5);
        Integer sum =intList.stream().reduce(0,(x,y)->x+y);
        System.out.println("求和:"+sum);
        System.out.println("---------");
        Optional<Double> sumSalary=emps.stream()
                .map((x)->x.getSalary())
                .reduce(Double::sum);
        System.out.println("所有员工的工资总和"+sumSalary.get());
    }

运行结果:

三)、收集

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

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

代码实列:

    @Test
    public void test18(){
        //获取流中的姓名,并转换为姓名的集合
       List<String> names=emps.stream()
                .map((e)->e.getName())
                .collect(Collectors.toList());
        System.out.println(names);
    }

运行结果:

之后的博客会对Collectors这个类进行详细的介绍。这里只对流中的姓名进行收集,并转换为集合的简单实列

 

总结:

熟练掌握这些API,还是需要反复迭代。并加以理解

 

参考:张长志老师的视频

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值