Java8新特性(2) Stream API

了解Stream

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。

1.Stream自己不会储存数据 。

2.Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream。

3.Stream操作是延迟执行的。这意味着它们会等到需要结果的时候才执行。

注意:请区别io流和stream流

流组成:数据源,用于传输数据的管道(用于一系列流水线式中间操作),产生的新流。

Stream的三个操作步骤:1.创建Stream        2.中间操作        3.终止操作(终端操作,即输出,如println)

创建Stream

    @Test
    public void test1(){
       //1.可以通过Conllection系列集合提供的stream()或parallelStream获取
       //stream是串行流,parallelStream是并行流
       List<String> list=new ArrayList<>();
       Stream<String> stream=list.stream();

       //2.通过Arrays中的静态方法stream()获取数组流
        Employee[] employees=new Employee[10];
        Stream<Employee> stream1 = Arrays.stream(employees);

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

        //4.创建无限流
        //迭代
        Stream<Integer> stream3 = Stream.iterate(0, x -> x + 2);
        stream3.limit(10).forEach(System.out::println);
        //生成
        Stream.generate(()->Math.random()*10).limit(20).forEach(System.out::println);
    }

Stream的中间操作

中间操作有:1.筛选和切片   2.映射    3.排序

注意:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作(如:forEach),否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为惰性求值,或叫做延迟加载

内部迭代:迭代操作由Stream API完成

区别外部迭代:

    //外部迭代
    @Test
    public void test6(){
        Iterator<Employee> iterator= employees.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

筛选和切片 

    /*
     *筛选与切片
     * filter - 接收Lambda,从流中排除某些元素
     * limit - 截断流,是其元素不超过给定数量
     * skip(n)- 跳过元素,返回一个扔掉了前n个元素的元素。若流中元素不足n个,则返回一个空流。与limit(n) 互补
     * distinct - 筛选,通过流所生成元素的hashcode()和equal()去除重复元素(需要重写hashcode()和equals方法)
     */
    @Test
    public void test2(){
        //中间操作
        Stream<Employee> employeeStream = employees.stream().filter(x -> {
                                                            System.out.println("Stream API");
                                                            return x.getAge() < 20;});

        //终止操作
        employeeStream.forEach(System.out::println);

        System.out.println("================================");
        employees.stream().filter(x -> x.getAge() > 20).limit(2).forEach(System.out::println);

        System.out.println("================================");
        employees.stream().filter(x -> x.getAge()> 20).skip(2).forEach(System.out::println);

        System.out.println("================================");
        employees.stream().filter(x -> x.getAge()> 20).distinct().forEach(System.out::println);

    }

 映射

 

    /*
    *映射
    * map - 接收Lambda。将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素,
    * 并将其映射成一个新的元素。
    * flatMap - 接收一个函数作为参数,将流中的每个值都换成另一个流,将所有流连接成一个流
    *
    * map和flatMap就相当于add(Object o)和addAll(Collection coll)
    * 若add方法中传入的值为一个集合,该方法会把整个集合添加到当前集合中
    * addAll则是将传入集合中的所有值添加到当前集合中
    */
    @Test
    public void test3(){
        //map作用,将集合流中的所有元素应用到指定函数上,然后生成一个新流
        List<String> list = Arrays.asList("aaa", "bbb", "ccc");
        //将aaa,bbb,ccc映射到toUpperCase函数上,最后产生一个新流
        list.stream().map(str->str.toUpperCase()).forEach(System.out::println);

        System.out.println("================================");
        employees.stream().map(Employee::getName).forEach(System.out::println);

        //flatMap前
        System.out.println("================================");
        Stream<Stream<Character>> streamStream = list.stream().map(Stream1::filterCharater);
        streamStream.forEach((t)->t.forEach(System.out::println));
        //flatMap后
        System.out.println("================================");
        list.stream().flatMap(Stream1::filterCharater).forEach(System.out::println);

    }

    public static Stream<Character> filterCharater(String str){
        List<Character> list=new ArrayList<>();
        for(Character c:str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }

排序 

    /*
    *   排序
    * sorted()- 自然排序(Comparable)
    * sorted(Comparator com)- 定制排序(Comparator)
    * */
    @Test
    public void test4(){
        //自然排序
        List<String> list=Arrays.asList("aaa","bbb","ccc");
        list.stream().sorted().forEach(System.out::println);

        //定制排序
        employees.stream().sorted((x,y)->x.getAge()-y.getAge()).forEach(System.out::println);
    }

终止操作

 

    /*
    * 查找与匹配
    * allMatch - 检查是否匹配所有元素
    * anyMatch - 检查是否至少匹配一个元素
    * noneMatch - 检查是否没有匹配所有元素
    * findFirst - 返回第一个元素
    * findAny - 返回当前流中的任意元素
    * count - 返回流中元素的总个数
    * max - 返回流中最大值
    * min - 返回流中最小值
    * */
    @Test
    public void test5(){
        //allMatch
        boolean bl = employees.stream().allMatch(e -> e.getStatus().equals(Employee.Status.Free));
        System.out.println(bl);

        //anyMatch
        boolean bl1 = employees.stream().anyMatch(e -> e.getStatus().equals(Employee.Status.Free));
        System.out.println(bl1);

        //noneMatch
        boolean bl2 = employees.stream().anyMatch(e -> e.getStatus().equals(Employee.Status.Free));
        System.out.println(bl2);

        //findFirst
        Optional<Employee> first = employees.stream().sorted((x, y) -> (int) (x.getSalary() - y.getSalary())).findFirst();
        //如果Optional容器为空,则可以使用orElse方法存放一个值替代,相当于if(xxx==null)
        first.orElse(new Employee("shen",1,1, Employee.Status.Busy));
        System.out.println(first.get());

        //findAny
        Optional<Employee> any = employees.stream().filter(x -> x.getStatus().equals(Employee.Status.Free)).findAny();
        System.out.println(any.get());
        //区别:parallelStream是多个线程同时找,谁先找到算谁的
        //stream是一个一个找
        Optional<Employee> any1 = employees.parallelStream().filter(x -> x.getStatus().equals(Employee.Status.Free)).findAny();
        System.out.println(any1.get());

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

        //max
        Optional<Employee> max = employees.stream().max((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
        //Optional<Employee> max1 = employees.stream().max(Double::compare);报错原因:Double中compare的两个入参类型为Double
        System.out.println(max.get());

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

规约 

过程:先将第一个参数0赋值给x,再从stream中的第一个元素赋值给y,x+y的返回值赋值给x,在从stream中取出第二个元素当作y,x+y的返回值赋值给x,以此类推。

    /*
    * 归约
    * reduce(T identity,BinaryOperator)/reduce(BinaryOperator) - 可以将流中元素反复结合起来,得到一个值
    * */
    @Test
    public void test7(){
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduce);

        Optional<Double> reduce1 = employees.stream().map(Employee::getSalary).reduce(Double::sum);
        // 等价于:Double reduce1 = employees.stream().map(Employee::getSalary).reduce(0.0, (x, y) -> x + y);
        // 因为没有起始值,所以使用Optional(为了避免空指针),有起始值则使用Double
        System.out.println(reduce1.get());

    }

 收集

 

    /*
    * 收集
    * collect - 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方式
    * */
    @Test
    public void test8(){
        //存在一个支持Collector的工具类:Collectors
        //List
        List<String> collect = employees.stream().map(Employee::getName).collect(Collectors.toList());
        collect.forEach(System.out::println);

        //Set
        Set<String> collect1 = employees.stream().map(Employee::getName).collect(Collectors.toSet());
        collect1.forEach(System.out::println);

        //指定一些特殊的数据结构,如HashSet
        HashSet<String> collect2 = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
        collect2.forEach(System.out::println);

        //总数
        Long collect3 = employees.stream().collect(Collectors.counting());
        System.out.println(collect3);

        //平均值
        Double collect4 = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(collect4);

        //总工资
        Double collect5 = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(collect5);

        //最大值
        Optional<Employee> collect6 = employees.stream().collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
        System.out.println(collect6);

    }

 分组

    /*
    *   分组
    * */
    @Test
    public void test9(){
        Map<Employee.Status, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
        //map不能使用forEach语句
        System.out.println(collect);

        //多级分组
        Map<Employee.Status, Map<String, List<Employee>>> collect1 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((x) -> {
            if (x.getAge() < 18) {
                return "未成年";
            } else {
                return "成年";
            }
        })));
        System.out.println(collect1);
    }

分区

    /*
    *   分区
    * 分为true和false两区
    * */
    @Test
    public void test10(){
        Map<Boolean, List<Employee>> collect = employees.stream().collect(
                Collectors.partitioningBy(e -> e.getAge() > 18)
        );
        System.out.println(collect);
    }

集合Summerizing

    /*
     *   集合总结
     * */
    @Test
    public void test11(){
        DoubleSummaryStatistics collect = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(collect.getMax());
        System.out.println(collect.getMin());
        System.out.println(collect.getAverage());
        System.out.println(collect.getCount());
    }

并行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。Java8对并行进行了优化,我们可以很容易得对数据进行并行操作。Stream API可以声明性地通过parallel()与sequential()在并行流与顺序流之间进行切换。

Fork/Join框架

Fork/Join框架就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行Join汇总。

Fork/Join框架的优势:

采用“工作窃取”模式(work-stealing):当处理多个子问题时,如果某个子线程处于等到状态,那么该线程会主动寻找其他线程尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高了性能。

java8之前并行Fork/Join代码执行过程 

缺点:操作起来非常麻烦

public class ForkJoinCalculate extends RecursiveTask<Long> {
    long start;
    long end;

    long THRESHOLD=10000;

    public ForkJoinCalculate(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = start - end;
        if(length<+THRESHOLD){
            long sum = 0;
            for(long i=start;i<=end;i++){
                sum+=i;
            }
            return sum;
        }else {
            long mid = (start + end) / 2;
            ForkJoinCalculate left=new ForkJoinCalculate(start,mid);
            left.fork();
            ForkJoinCalculate right = new ForkJoinCalculate(mid, end);
            right.fork();
            return left.join()+right.join();
        }
    }

    public static void main(String[] args) {
        //ForkJoin的执行需要一个池
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task=new ForkJoinCalculate(1,100000L);
        Long sum = pool.invoke(task);
        System.out.println(sum);
    }
}

 java8之后执行并行代码,parallel()底层还是Fork/Join

优点:效率更高,代码更简单

    @Test
    public void test13(){
        //顺序流变并行流用parallel(),parallel()底层是ForkJoin
        LongStream.rangeClosed(0,10000).parallel().reduce(0,Long::sum);

    }

Employee素材

    List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 1999,Employee.Status.Free),
            new Employee("李四", 19, 929, Employee.Status.Busy),
            new Employee("王五", 20, 123, Employee.Status.VOCATION),
            new Employee("赵六", 10, 10000, Employee.Status.Free),
            new Employee("田七", 30, 100, Employee.Status.Busy));

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值