【Java8】 Stream学习笔记

文章目录

  • 1.Stream概述
  • 2.Stream的创建
    • 2.1 (创建)通过 java.util.Collection.stream() /parallelStream()方法用集合创建流
    • 2.2 (创建)使用java.util.Arrays.stream(T[] array)方法用数组创建流
    • 2.3(创建)使用Stream的静态方法:of()、iterate()、generate()
    • 2.4 stream和parallelStream的简单区分
  • 3. Stream流中的常用方法
    • 3.1 【终端操作】 forEach(遍历/终结方法)
    • 3.2 【中间操作】filter(过滤)
    • 3.3 【中间操作】 map(映射转换)
    • 3.4 【终端操作】count(统计个数)
    • 3.5 【中间操作】limit(截取前几个元素)
    • 3.6 【中间操作】skip(跳过前几个元素)
    • 3.7 【中间操作】concat(组合合并流)
    • 3.8 【中间操作】distinct(筛选)
    • 3.9 【中间操作】 flatMap(映射,打开后再转换)
    • 3.10 【中间操作】 定制排序:sorted
    • 3.11 【终端操作】 检测匹配match
    • 3.12 【终端操作】 查找元素find
    • 3.13【终端操作】 查找最大最小值
    • 3.14 【终端操作】 规约
    • 3.15 【终端操作】 收集
    • 3.16 【中间操作】 iterate(迭代)(也是生成流的方式)
    • 3.17 【中间操作】 peek(查看)

【JAVA】目录贴
参考文章1
参考文章2

1.Stream概述

Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。

  • 那么什么是Stream

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

  • Stream可以由数组或集合创建,对流的操作分为两种
  1. 中间操作,每次返回一个新的流,可以有多个。
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
  • 另外,Stream有几个特性:
  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  2. stream不会改变数据源,通常情况下会产生一个新的集合或一个值
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

2.Stream的创建

获取一个流非常简单,有以下几种常用的方式:

  • 所有的 Collection 集合都可以通过 stream 默认方法获取流(顺序流);
  • 所有的 Collection 集合都可以通过parallelStream获取并行流
  • Stream 接口的静态方法 of 可以获取数组对应的流。
  • Arrays的静态方法stream也可以获取流

2.1 (创建)通过 java.util.Collection.stream() /parallelStream()方法用集合创建流

		//通过 java.util.Collection.stream() 方法用集合创建流
        log.info("========通过 java.util.Collection.stream() 方法用集合创建流====start");
        List<String> list = Arrays.asList("a", "b", "c");
        // 创建一个顺序流
        Stream<String> stream = list.stream();
        // 创建一个并行流
        Stream<String> parallelStream = list.parallelStream();
        System.out.println(stream);
        System.out.println(parallelStream);
        log.info("========通过 java.util.Collection.stream() 方法用集合创建流====end");
  • 除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流

2.2 (创建)使用java.util.Arrays.stream(T[] array)方法用数组创建流

		log.info("========使用java.util.Arrays.stream(T[] array)方法用数组创建流====start");
        //使用java.util.Arrays.stream(T[] array)方法用数组创建流
        int[] array={1,3,5,6,8};
        IntStream stream3 = Arrays.stream(array);
        System.out.println(stream3);
        log.info("========使用java.util.Arrays.stream(T[] array)方法用数组创建流====end");

2.3(创建)使用Stream的静态方法:of()、iterate()、generate()

		log.info("========使用使用Stream的静态方法:of()、iterate()、generate()方法用数组创建流====start");
        Stream<String> stream = Stream.of("1", "2", "3","4");

        Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(4);
        stream2.forEach(System.out::println);

        Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
        stream3.forEach(System.out::println);
        log.info("========使用使用Stream的静态方法:of()、iterate()、generate()方法用数组创建流====end");

2.4 stream和parallelStream的简单区分

stream顺序流,由主线程按顺序对流执行操作,而parallelStream并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求(可能会乱序)。例如筛选集合中的奇数,两者的处理不同之处
【Java8】 Stream学习笔记_第1张图片

 		String info = "stream和parallelStream的简单区分";
        log.info("========{}====start", info);
        Stream<Integer> stream = Stream.of(1, 2, 3, 5, 9, 7, 4, 3, 8, 6, 2, 5);
        stream.filter(x -> x % 2 == 1).forEach(x -> System.out.print(x));//输出:1359735
        System.out.println();
        Stream<Integer> parallelStream = Arrays.asList(1, 2, 3, 5, 9, 7, 4, 3, 8, 6, 2, 5).parallelStream();
        parallelStream.filter(x -> x % 2 == 1).forEach(x -> System.out.print(x));//输出:3559713
        System.out.println();
        log.info("========{}====end", info);

3. Stream流中的常用方法

【Java8】 Stream学习笔记_第2张图片
这些方法可以被分成两种:

延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用

  • 以下方法初始化流代码:
public class User extends Mother{
    private String name;
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
		User user = new User("小红",1);
        User user2 = new User("小白",2);
        String info = "终端操作:forEach(遍历/终结方法)";
        log.info("========{}====start", info);
        List<User> users = Arrays.asList(user, user2);
        Stream<User> stream = users.stream();

3.1 【终端操作】 forEach(遍历/终结方法)

本方法主要用于进行遍历,可简化for循环遍历。参数传入一个函数式接口:Consumer

		stream.forEach(System.out::print);//User(name=小红, age=1)User(name=小白, age=2)

3.2 【中间操作】filter(过滤)

本方法主要用于进行过滤,可以通过 filter 方法将一个流转换成另一个子集流,从而进行所需的相关过滤。

stream2.filter(o-> o.getAge()==1).forEach(System.out::print);//User(name=小红, age=1)

3.3 【中间操作】 map(映射转换)

如果需要进行映射转换,可以使用 map 方法将流中的元素映射到另一个流中。该接口需要一个 Function 函数式接口参数,java.util.stream.Function 函数式接口中唯一的抽象方法为

R apply(T t);
		stream3.map(new Function<User, String>() {
            @Override
            public String apply(User user) {
                String name = user.getName();
                return name;
            }
        }).forEach(System.out::print);//小红小白

3.4 【终端操作】count(统计个数)

类似集合 Collection 当中的 size 方法一样,Stream流提供 count 方法来统计其中的元素个数,该方法返回一个long值代表元素个数(不再像集合是int值)。

System.out.println(stream4.filter(o -> o.getAge() == 1).count());//1

3.5 【中间操作】limit(截取前几个元素)

limit 方法主要用于对流进行截取,只取用前n个。参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。基本使用方法如下

stream5.limit(2).limit(1).forEach(System.out::print);

3.6 【中间操作】skip(跳过前几个元素)

skip 方法主要用于跳过前几个元素,获取一个截取之后的新流。如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。基本使用方法如下

stream6.skip(1).forEach(System.out::print);

3.7 【中间操作】concat(组合合并流)

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :

System.out.println(Stream.concat(stream7, stream8).count());

3.8 【中间操作】distinct(筛选)

去除流中重复的元素(使用hashcode和equals方法来对比)

System.out.println(Stream.concat(stream9, stream10).distinct().count());

3.9 【中间操作】 flatMap(映射,打开后再转换)

flatMap与map作用类似,内部传入一个Function函数式接口,跟map的区别就是这个会把流中的元素打开,再组合成一个新的流。

@Test
    public void test(){
        List<String> list = Arrays.asList("aa","bb","cc","dd");
        // 1 map输出全为大写
        list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
        // 2.map流里还有其他流,需要两个遍历才可以得到里面内容
        Stream<Stream<Character>> streamStream = list.stream().map(new Function<String, Stream<Character>>() {
            @Override
            public Stream<Character> apply(String s) {
                return null;
            }
        });
        streamStream.forEach(s -> {
            s.forEach(System.out::print);
        });
        System.out.println();
        // 3.flatMap流里还有其他流,使用flatMap可以直接把里面的流打开,一次遍历即可
        Stream<Character> characterStream = list.stream().flatMap(new Function<String, Stream<? extends Character>>() {
            @Override
            public Stream<? extends Character> apply(String str) {
                ArrayList<Character> list = new ArrayList();
                // 将字符串转成字符数组,并遍历加入list集合
                for(Character c : str.toCharArray()){
                    list.add(c);
                }
                // 返回list集合的stream流
                return list.stream();//aa
            }
        });
        characterStream.forEach(System.out::print);
        System.out.println();
    }
    /**
     *  将字符串中的多个字符构成的集合转换为对应的stream
     * @param str
     * @return
     */
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList();

        // 将字符串转成字符数组,并遍历加入list集合
        for(Character c : str.toCharArray()){
            list.add(c);
        }

        // 返回list集合的stream流
        return list.stream();//aa
    }

3.10 【中间操作】 定制排序:sorted

本方法主要用于排序,可以使用Comparator接口进行定制排序。具体使用方法如下:

    public void test2(){
 
        List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);
        
        // 按照自然排序
        integers.stream().sorted().forEach(System.out::println);
 
        List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 定制排序(大到小),需要传入Comparator接口(如果流中的是引用类型,只能用定制排序)
        
        // 简写:integers2.stream().sorted((e1,e2) -> e2-e1).forEach(System.out::println);
        integers2.stream().sorted((e1,e2) -> {
            return e2-e1;
        }).forEach(System.out::println);
    }

3.11 【终端操作】 检测匹配match

本类方法会返回一个Boolean值,具体分类如下:

  • 是否全部匹配:allMatch

  • 是否至少匹配一个:anyMatch

  • 是否没有匹配的:noneMatch

@Test//检测匹配
    public void test3(){
        List<Integer> integers = Arrays.asList(124, 2, 15, 12, 51, -5, 5);
        // 判断是否全部大于5
        boolean b = integers.stream().allMatch(i -> i > 5);
        // 结束输出false
        System.out.println(b);


        List<Integer> integers2 = Arrays.asList(124, 2, 15, 12, 51, -5, 5);
        // 检测是否匹配至少一个元素
        boolean b1 = integers2.stream().anyMatch(i -> i > 5);
        // 输出true
        System.out.println(b1);



        List<Integer> integers3 = Arrays.asList(124, 2, 15, 12, 51, -5, 5);
        // 检查是否没有匹配的元素
        boolean b2 = integers3.stream().noneMatch(i -> i > 1000);
        // 输出true,全部不匹配
        System.out.println(b2);
    }

3.12 【终端操作】 查找元素find

  1. 找第一个元素:findFirst,返回Optional类型
  2. 查找其中一个元素:findAny,返回Optional类型
  • findAny方法会在流中查找任意一个元素并返回。在顺序流中,它会返回遇到的第一个元素;而在并行流中,它会返回最先找到的元素。请注意,返回的元素是不确定的,并且在不同的运行中可能会有所不同。
  • 和 findAny() 方法一样,find() 方法也是在流中查找元素并返回。但是,它在顺序流中查找的是第一个匹配的元素(根据传递给它的 Predicate 参数),而在并行流中它不是保证返回的是最先找到的元素。此外,find() 方法在流中找不到匹配的元素时会返回一个空的 Optional 对象,而 findAny() 方法则可能返回 empty()。所以说,如果你只想要找到任意一个匹配的元素并不关心它是哪个,你可以使用 findAny() 方法。而如果你想找到一个固定的匹配元素,你可以使用 findFirst() 方法。
public void test4(){
        List<Integer> integers = Arrays.asList(124, 2, 15, 12, 51, -5, 5);

        // 输出第一个元素
        Optional<Integer> first = integers.stream().findFirst();

        // 输出结果是Optional[124]
        System.out.println(first);

        List<Integer> integers2 = Arrays.asList(2,124, 2, 15, 12, 51, -5, 5);

        // 返回其中一个元素
        Optional<Integer> any = integers2.stream().findAny();
        System.out.println(any);
    }

3.13【终端操作】 查找最大最小值

  1. max(comparator c)
  2. min(comparator c)
public void test5(){
        List<User> list = new ArrayList<>();
        list.add(new User("张三",25));
        list.add(new User("李四",27));
        list.add(new User("王五",35));
        list.add(new User("赵六",55));

        //查找年龄最大的人
        Optional<User> max = list.stream().max((e1, e2) -> e1.getAge() - e2.getAge());
        //返回赵六,55岁年龄最大
        System.out.println(max.get());
    }

3.14 【终端操作】 规约

  1. reduce(T identity,BinaryOperator) 第一个参数是初始值,第二个参数是一个函数式接口。
  2. reduce(BinaryOperator) 参数是一个函数式接口
@Test//【终端操作】 规约
    public void test6() {
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 求集合里数字的和(归约)reduce第一个参数是初始值。
        Integer reduce = integers.stream().reduce(2, Integer::sum);
        System.out.println(reduce);

        List<User> list = new ArrayList<>();
        list.add(new User("张三",25));
        list.add(new User("李四",27));
        list.add(new User("王五",35));
        list.add(new User("赵六",55));

        // 求所有人的年龄和(归约)
        // 不用方法引用写法:Optional reduce = list.stream().map(person -> person.getSalary()).reduce((e1, e2) -> e1 + e2);
        Optional<Integer> reduce2 = list.stream().map(User::getAge).reduce(Integer::sum);
        System.out.println(reduce2.get());
    }

3.15 【终端操作】 收集

collect(Collector c):将流转化为其他形式,接收一个Collector接口的实现

@Test//【终端操作】  收集
    public void test7() {

        List<User> list = new ArrayList<>();
        list.add(new User("张三",25));
        list.add(new User("李四",27));
        list.add(new User("王五",35));
        list.add(new User("赵六",55));
        //30岁以上的user转为list
        List<User> collect = list.stream().filter(o->o.getAge()>30).collect(Collectors.toList());
        System.out.println(collect);
        //30岁以上的user转为 set
        Set<User> collect1 = list.stream().filter(o -> o.getAge() > 30).collect(Collectors.toSet());
        System.out.println(collect1);
    }

3.16 【中间操作】 iterate(迭代)(也是生成流的方式)

可以使用Stream.iterate创建流值,即所谓的无限流。

@Test//【中间操作】 iterate(迭代)(也是生成流的方式)
    public  void test8() {
        List<User> list = new ArrayList<>();
        list.add(new User("张三",25));
        list.add(new User("李四",27));
        list.add(new User("王五",35));
        list.add(new User("赵六",55));
        Stream.iterate(1,o->o*2).limit(3).forEach(System.out::println);
    }

3.17 【中间操作】 peek(查看)

peek接收的是一个Consumer函数,peek 操作会按照 Consumer 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性

@Test//【中间操作】 peek(查看)
//    peek接收的是一个Consumer函数,peek 操作会按照 Consumer 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性
    public void test9() {
        List<User> list = new ArrayList<>();
        list.add(new User("张三",25));
        list.add(new User("李四",27));
        list.add(new User("王五",35));
        list.add(new User("赵六",55));
        list.stream().peek(e -> System.out.println("查看刚过滤出的值:" + e)).forEach(System.out::println);
    }

你可能感兴趣的:(学习,笔记,python)