java8新特性-Lambda表达式-Stream API

Java8中有两个最为重要的改变。第一个是 Lambda 表达式,第二个就是Stream API (java.util.stream.*)

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

流(Stream)到底是什么呢?

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

注意:

  1. Stream 它自己不会存储元素。
  2. Stream 不会改变源对象。它会返回一个持有结果的Stream。
  3. Stream 操作是延迟执行的(惰性求值)。它会等到需要结果的时候才执行(终止操作时一次性全部处理)。

Stream的操作(三个步骤)

  • 创建Stream

    一个数据源(如:集合、数组),获取一个流。

  • 中间操作

    一个中间操作链,对数据源的数据进行处理

  • 终止操作(终端操作)

    一个终止操作,执行中间操作链,产生结果。

  1. 创建Stream
@Test
public void test1(){
    //创建Stream
    //1. 可以通过Collection系列集合提供的stream()或parallelStream()获取流
    List<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();

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

    //3. 通过Stream类中的静态方法 of() 获取流
    Stream<String> stream3 = Stream.of("f", "j", "h");

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

  1. 中间操作
  • 筛选与切片

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

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

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

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

     @Test
    public void test2(){
    List<Animal> animals = Arrays.asList(
    new Animal("cat",1),
    new Animal("dog",2),
    new Animal("bird",3),
    new Animal("fish",4),
    new Animal("fish",4)
    );
    //中间操作:不会执行任何操作
    //内部迭代,迭代操作由StreamAPI完成
    System.out.println("----filter----");
    Stream<Animal> stream = animals.stream().filter((x) -> x.getAge() > 2);
    //终止操作:一次性执行全部内容
    stream.forEach(System.out::println);
    
    System.out.println("----limit----");
    animals.stream().limit(1).forEach(System.out::println);
    
    System.out.println("----skip----");
    animals.stream().skip(2).forEach(System.out::println);
    
    System.out.println("----distinct----");
    //切记实体类需要重写hashcode()和equals()方法
    animals.stream().distinct().forEach(System.out::println);
    }
    

  • 映射

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

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

    ​ 例如{{a,b,c},{d,e,f}} ->{a,b,c,d,e,f},类似于List的add和addAll方法。

    @Test
    public void test3(){
        List<String> list = Arrays.asList("aa","bb","cc");
        List<Animal> animals = Arrays.asList(
            new Animal("cat",1),
            new Animal("dog",2),
            new Animal("bird",3),
            new Animal("fish",4),
            new Animal("fish",4)
        );
        list.stream().map((x)->x.toUpperCase()).forEach(System.out::print);
        System.out.println();
        animals.stream().map(Animal::getName).distinct().forEach(System.out::println);
    
        System.out.println("--------flatMap---------");
        Stream<Stream<Character>> streamMap = list.stream().map(TestStreamAPI::filterCharacter);//流中流
        streamMap.forEach((sm)->{
            sm.forEach(System.out::print);
        });
        //下面代码与上面等价
        System.out.println();
        Stream<Character> streamFlatMap = list.stream().flatMap(TestStreamAPI::filterCharacter);
        streamFlatMap.forEach(System.out::print);
    }
    
    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }
    

  • 排序

    sorted()——自然排序(Comparable)

    sorted(Comparator comparator)——定制排序(Comparator)

    @Test
    public void test4(){
        List<String> list = Arrays.asList("dd","aa","bb","cc");
        list.stream().sorted().forEach(System.out::print);
        System.out.println();
        System.out.println("-------自定义比较-------");
        List<Animal> animals = Arrays.asList(
            new Animal("cat",1),
            new Animal("fish",4),
            new Animal("dog",2),
            new Animal("bird",3),
            new Animal("fish",4)
        );
        animals.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);
    }
    

  1. 终止操作
  • 查找与匹配

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

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

    noneMatch——检查是否所有元素都不匹配。

    findFirst——返回第一个元素。

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

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

    max——返回流中最大值。

    min——返回流中最小值。

    @Test
    public void test5(){
            //查找与匹配
            List<Animal> animals = Arrays.asList(
                    new Animal("cat",1),
                    new Animal("dog",2),
                    new Animal("bird",3)
            );
            System.out.println("--------allMatch--------");
            boolean b1 = animals.stream().allMatch((x) -> x.getName().equals("cat"));
            System.out.println("是否匹配所有元素:"+b1);
    
            System.out.println("--------anyMatch--------");
            boolean b2 = animals.stream().anyMatch((x) -> x.getName().equals("cat"));
            System.out.println("是否至少有一个是匹配的:"+b2);
    
            System.out.println("--------noneMatch--------");
            boolean b3 = animals.stream().noneMatch((x) -> x.getName().equals("cat"));
            System.out.println("是否所有元素都不匹配:"+b3);
    
            System.out.println("--------findFirst--------");
            Optional<Animal> first = animals.stream().findFirst();
            System.out.println("第一个元素是"+first.get());
    
            System.out.println("--------findAny--------");
            Optional<Animal> any = animals.parallelStream().findAny();
            System.out.println("任意的一个元素是"+any.get());
    
            System.out.println("--------count--------");
            long count = animals.stream().count();
            System.out.println("流中元素的总个数是"+count);
    
            System.out.println("--------max--------");
            Optional<Animal> max = animals.stream().max((x,y)->{
                if (x.getAge()==y.getAge()){
                    return x.getName().compareTo(y.getName());
                }
                return Integer.compare(x.getAge(), y.getAge());
            });
            System.out.println("流中最大值是"+max.get());
    
            System.out.println("--------min--------");
            Optional<Integer> min = animals.stream().map(Animal::getAge).min(Integer::compareTo);
            System.out.println("流中最小值是"+min.get());
        }
    

  • 归约与收集

    归约:T reduce(T identity, BinaryOperator accumulator)

    ​ Optional reduce(BinaryOperator accumulator)

    ​ indentity起始值,BinaryOperator二元运算,可以将流中元素反复结合起来,得到一个值。

    收集:

    R collect(Supplier supplier,
    ​ BiConsumer<R, ? super T> accumulator,
    ​ BiConsumer<R, R> combiner);

    ​ <R, A> R collect(Collector<? super T, A, R> collector)

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

    ​ Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)

    ​ Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。

    @Test
    public void test6(){
            //归约
            List<Integer> list = Arrays.asList(1,2,3,4,5,6);
            // identity->x , 流中元素->y,结果->x,下一个流中元素->y
            Integer sum = list.stream().reduce(0, (x, y) -> x + y);
            System.out.println("list总和:"+sum);
            Optional<Integer> op = animals.stream().map(Animal::getAge).reduce(Integer::sum);
            System.out.println("animals年龄总和:"+op.get());
    
            //收集
            //Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map)
            //Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。
            List<String> collectList = animals.stream().map(Animal::getName).collect(Collectors.toList());
            System.out.println(collectList);
            HashSet<String> collectHashSet = animals.stream().map(Animal::getName).collect(Collectors.toCollection(HashSet::new));
            System.out.println(collectHashSet);
            String str = animals.stream().map(Animal::getName).collect(Collectors.joining(","));
            System.out.println(str);
            Double collectAverage = animals.stream().collect(Collectors.averagingDouble(Animal::getAge));//年龄平均值
            System.out.println(collectAverage);
        }
    

并行流与顺序流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Stream API 可以声明性的通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

可以对比ForkJoin,ForkJoin编码较为复杂。

posted @ 2021-07-22 18:17  fjhnb  阅读(84)  评论(0)    收藏  举报