当前位置: 首页 > news >正文

Java基础关键_036_Stream

目  录

一、概述

二、创建 Stream 的三种方式

1.Collection 接口提供的方法

2.Arrays 类提供的方法

3.Stream 接口提供的方法

三、顺序流与并行流

四、中间操作

1.筛选(filter)

2.映射(map)

3.去重(distinct)

4.排序(sorted)

5.合并(concat)

6.跳过(skip)

7.截断(limit)

五、终止操作

1.遍历(forEach) 

2.匹配(match)

3.归约(reduce)

4.收集(collect)

(1)归集(toList / toSet / toMap)

(2)统计(counting / averaging)

(3)分组(groupingBy)

(4)接合(joining)


一、概述

  1. 从 jdk 8 开始,Java 引入了全新的 Stream API,它将函数式编程的风格运用到了 Java 中,允许开发者在不改变数据源的情况下对集合进行操作;
  2. Collection 是静态的内存数据结构,强调的是数据。而 Stream API 是与集合相关的计算操作,强调的是计算。即 Collection 面向内存,Stream API 面向 CPU;
  3. 步骤
    1. 创建操作:通过数据源获取一个 Stream 对象;
    2. 中间操作:对数据源的数据进行处理,返回一个 Stream 对象,因此可以链式操作;
    3. 终止操作:执行终止操作时,才会真正执行中间操作,并返回一个计算完成的结果。
  4. 特点
    1. 不会存储对象,只能对元素进行计算;
    2. 不会改变数据对象,可能会返回一个持有结果的新 Stream;
    3. Stream 上的操作属于延迟操作,只有等用户真正需要结果的时候才会执行;
    4. 一旦执行终止操作,就不能再调用其他的中间操作或终止操作。

二、创建 Stream 的三种方式

1.Collection 接口提供的方法

public class CollectionCreate {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("《诗经》");
        arrayList.add("《尚书》");
        arrayList.add("《礼记》");
        arrayList.add("《周易》");
        arrayList.add("《春秋》");

        // 顺序流,单线程
        Stream<String> stream = arrayList.stream();
        System.out.println(stream); // java.util.stream.ReferencePipeline$Head@4eec7777

        // 并行流,计算时自动启动多线程
        Stream<String> parallelStream = arrayList.parallelStream();
        System.out.println(parallelStream); // java.util.stream.ReferencePipeline$Head@3b07d329
    }
}

2.Arrays 类提供的方法

public class ArraysCreate {
    public static void main(String[] args) {
        String[] musics = {"《姑娘别哭泣》", "《谁》", "《遇见》", "《轻红》", "《可可托海的牧羊人》"};
        Stream<String> stream = Arrays.stream(musics);
        System.out.println(stream); // java.util.stream.ReferencePipeline$Head@4eec7777
    }
}

3.Stream 接口提供的方法

public class StreamCreate {
    public static void main(String[] args) {
        Stream<String> cinema = Stream.of("《这个杀手不太冷》", "《教父》", "《终结者》", "《建军大业》");
        System.out.println(cinema); // java.util.stream.ReferencePipeline$Head@4eec7777
    }
}

三、顺序流与并行流

        顺序流对 Stream 对象处理是单线程的,效率较低。

        若 Stream 流中处理数据没有顺序要求,且希望可以并行处理数据,则可以使用并行流来处理,从而提高效率。

        将顺序流转换为并行流,只需要调用 Stream 提供的 parallel 方法进行转换,无需编写任何多线程代码。        

public class TransportParallel {
    public static void main(String[] args) {
        Stream<String> country = Stream.of("中国", "俄罗斯", "美国", "英国", "法国");
        System.out.println(country.isParallel());   // false

        Stream<String> parallel = country.parallel();
        System.out.println(parallel.isParallel());  // true

        System.out.println(country == parallel);    // true
    }
}

四、中间操作

1.筛选(filter)

        按照一定规则校验流中元素,将符合条件的元素提取到新的流中操作。该操作使用 Stream 接口提供的 【Stream<T> filter(Predicate<? super T> predicate)】方法实现。

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class FilterTest {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 18);
        Student s2 = new Student("李四", 23);
        Student s3 = new Student("王五", 9);
        Student s4 = new Student("赵六", 45);
        Student s5 = new Student("孙七", 13);

        ArrayList<Student> arrayList = new ArrayList<>();
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        arrayList.add(s4);
        arrayList.add(s5);

        // 过滤出成年学生
        // filter 是 Stream 的一个中间操作,返回一个 Stream 对象
        // forEach 是 Stream 的一个终止操作

        // 匿名内部类
        /*arrayList.stream().filter(new Predicate<Student>() {
            @Override
            public boolean test(Student student) {
                return student.getAge() >= 18;
            }
        }).forEach(new Consumer<Student>() {
            @Override
            public void accept(Student student) {
                System.out.println(student);
            }
        });
        */
        
        // Lambda 表达式
        arrayList.stream().filter(student -> student.getAge() >= 18).forEach(System.out::println);
    }
}


2.映射(map)

        将一个流的元素按照一定映射规则映射到另一个流中。该操作使用了 Stream 接口提供的【Stream map(Function<? super T, ? > mapper)】方法实现。

        可以实现将多个集合中的元素映射到另一个流中。该操作使用了 Stream 接口提供的【Stream flatMap(Function<? super T, ? extends Stream<R>> mapper)】方法实现。

public class MapTest {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("A", "BC", "DEF");

        // 全部转换为小写字母

        // 匿名内部类
        /*stream.map(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.toLowerCase();
            }
        }).forEach(new Consumer<String>() {
            @Override
            public void accept(String string) {
                System.out.println(string);
            }
        });
*/
        // Lambda 表达式
        stream.map(str -> str.toLowerCase()).forEach(System.out::println);
    }
}

public class FlatMapTest {
    public static void main(String[] args) {
        Student s1 = new Student("明世隐", 20);
        Student s2 = new Student("李元芳", 18);
        Student s3 = new Student("蔡文姬", 6);
        Student s4 = new Student("伽罗", 22);
        Student s5 = new Student("澜", 27);

        ArrayList<Student> arrayList1 = new ArrayList<>();
        arrayList1.add(s1);
        arrayList1.add(s2);

        ArrayList<Student> arrayList2 = new ArrayList<>();
        arrayList2.add(s3);
        arrayList2.add(s4);
        arrayList2.add(s5);

        Stream<ArrayList<Student>> stream = Stream.of(arrayList1, arrayList2);

        // flatMap()方法,将多个集合中的元素放到一个 Stream,形成一个新的流
        // 匿名内部类
        /*stream.flatMap(new Function<ArrayList<Student>, Stream<Student>>() {
            @Override
            public Stream<Student> apply(ArrayList<Student> students) {
                return students.stream();
            }
        }).forEach(new Consumer<Student>() {
            @Override
            public void accept(Student student) {
                System.out.println(student);
            }
        });
        */

        // lambda 表达式
        stream.flatMap(ArrayList<Student>::stream).forEach(System.out::println);
    }
}


3.去重(distinct)

        去除重复的元素,底层使用了 hashCode() 和 equals(Object obj) 判断元素是否相等。该操作使用了 Stream 接口提供的【Stream distinct()】方法实现。

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class DistinctTest {
    public static void main(String[] args) {
        Student s1 = new Student("小明", 23);
        Student s2 = new Student("小红", 23);
        Student s3 = new Student("小王", 23);
        Student s4 = new Student("小李", 23);
        Student s5 = new Student("小明", 18);
        Student s6 = new Student("小红", 23);

        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        arrayList.add(s4);
        arrayList.add(s5);
        arrayList.add(s6);

        // distinct 去重
        arrayList.stream().distinct().forEach(System.out::println);
    }
}


4.排序(sorted)

         使用 Stream 接口中的【Stream sorted()】方法,用于对元素的自然排序,使用该方法则元素对应的类必须实现 Comparable 接口。

        使用 Stream 接口中的【Stream sorted(Comparator<? super T> comparator)】方法,用于对元素的指定排序,如此可以对一个类实现多种排序规则。

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

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

    @Override
    public int compareTo(Student o) {
        return this.getAge() - o.getAge();
    }
}
public class NaturalSorted {
    public static void main(String[] args) {
        Student s1 = new Student("光光", 18);
        Student s2 = new Student("冬冬", 2);
        Student s3 = new Student("球球", 16);
        Student s4 = new Student("红红", 23);
        Student s5 = new Student("白白", 45);

        Student[] students = {s1, s2, s3, s4, s5};
        Stream<Student> stream = Arrays.stream(students);
        stream.sorted().forEach(System.out::println);
    }
}

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class SpecifySorted {
    public static void main(String[] args) {
        Student s1 = new Student("光光", 18);
        Student s2 = new Student("冬冬", 2);
        Student s3 = new Student("球球", 16);
        Student s4 = new Student("红红", 23);
        Student s5 = new Student("白白", 45);

        ArrayList<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        students.add(s5);

        Stream<Student> stream = students.stream();
        stream.sorted((o1, o2) -> o1.getAge() - o2.getAge()).forEach(System.out::println);
    }
}

5.合并(concat)

        将两个 Stream 合并成一个 Stream。该操作使用 Stream 接口提供的【public static Stream concat(Stream<? extends T> a, Stream<? extends T> b)】方法实现。 

public class ConcatTest {
    public static void main(String[] args) {
        Student s1 = new Student("明世隐", 20);
        Student s2 = new Student("李元芳", 18);
        Student s3 = new Student("蔡文姬", 6);
        Student s4 = new Student("伽罗", 22);
        Student s5 = new Student("澜", 27);

        Stream<Student> stream1 = Stream.of(s1, s2, s3);
        Stream<Student> stream2 = Stream.of(s4, s5);

        Stream<Student> concat = Stream.concat(stream1, stream2);
        concat.forEach(System.out::println);
    }
}


6.跳过(skip)

        跳过是跳过 n 个元素开始操作。该操作使用 Stream 接口提供的【Stream skip(long n)】方法实现。

public class SkipTest {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
        stream.skip(3).forEach(System.out::println);
    }
}


7.截断(limit)

        截断是截取 n 个元素的操作。该操作使用 Stream 接口提供的【Stream limit(long maxSize)】方法实现。

        跳过操作和截断操作可以联合使用。

public class SkipTest {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 3, 5, 7, 9, 2, 4, 6, 8, 10);
        stream.limit(3).forEach(System.out::println);
    }
}


五、终止操作

        触发终止操作时才会真正执行中间操作,终止操作执行完毕会返回计算结果。终止操作执行完毕 Stream 就会失效,即不能再执行中间操作或终止操作了。

1.遍历(forEach) 

        使用 Stream 接口提供的【void forEach(Consumer<? super T> action)】方法来遍历计算结果。


2.匹配(match)

  1. 判断 Stream 中是否存在某些元素;
  2. 方法
    1. allMatch(Predicate<? super T> predicate):检查是否匹配所有元素;
    2. anyMatch(Predicate<? super T> predicate):检查是否匹配至少一个元素;
    3. noneMatch(Predicate<? super T> predicate):检查是否一个元素都不匹配;
    4. Optional findFirst:获得第一个元素。Optional是一个值的容器,可以通过 get() 获取容器的值。
public class MatchTest {
    public static void main(String[] args) {
        Student s1 = new Student("炘南", 23);
        Student s2 = new Student("东杉", 27);
        Student s3 = new Student("北淼", 25);
        Student s4 = new Student("坤中", 18);
        Student s5 = new Student("西钊", 24);

        Stream<Student> stream = Stream.of(s1, s2, s3, s4, s5);

        // 判断全部元素年龄大于18岁
//        boolean allMatch = stream.allMatch(s -> s.getAge() > 18);
//        System.out.println(allMatch);   // false

        // 判断是否有元素年龄大于18岁
//        boolean anyMatch = stream.anyMatch(s -> s.getAge() > 18);
//        System.out.println(anyMatch);   // true

        // 判断是否有元素年龄小于18岁
//        boolean noneMatch = stream.noneMatch(s -> s.getAge() > 18);
//        System.out.println(noneMatch);  // false

        // 判断第一个元素年龄大于18岁
        boolean isMatch = stream.findFirst().get().getAge() > 18;
        System.out.println(isMatch);    // true
    }
}

3.归约(reduce)

  1. 将所有元素按照指定规则合并成一个结果;
  2. 方法:
    1. reduce(BinaryOperator<T> accumulator);
    2. reduce(T identity, BinaryOperator<T> accumulator)。
  3. reduce 操作可以实现从一组元素中生成一个值,而 max()、min()、count() 方法都属于 reduce 操作,因为常用所以单独设为方法。
public class ReduceTest {
    public static void main(String[] args) {
        /*
         * 求和
         * */
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        IntStream stream = Arrays.stream(arr);
        OptionalInt reduce = stream.reduce((a, b) -> a + b);
        System.out.println(reduce.getAsInt());  // 55

        /*
         * 比较字符串长度,并将最长的字符串输出
         * */
        String[] arr2 = {"hello", "world", "java", "stream", "test"};
        Stream<String> stream1 = Arrays.stream(arr2);
        String s = stream1.reduce((a, b) -> a.length() > b.length() ? a : b).get();
        System.out.println(s);  // stream

        /*
         * 获得学生的总年龄
         * */
        Student s1 = new Student("老张", 18);
        Student s2 = new Student("老李", 22);
        Student s3 = new Student("老王", 19);
        Student s4 = new Student("老郭", 31);
        ArrayList<Student> students = new ArrayList<>();
        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);

        Stream<Student> stream2 = students.stream();
        Integer sumAge = stream2.map(Student::getAge).reduce((a, b) -> a + b).get();
        System.out.println(sumAge); // 90

        /*
         * count()
         * */
        Stream<String> stream3 = Stream.of("hello", "world", "java", "stream", "test");
        long count = stream3.count();
        System.out.println(count);  // 5

        /*
         * max()
         * */
        Stream<Integer> stream4 = Stream.of(1, 14, 5, 38, 62, 4);
        Optional<Integer> max = stream4.max(Integer::compareTo);
        System.out.println(max.get());  // 62

        /*
         * min()
         * */
        Stream<Integer> stream5 = Stream.of(1, 14, 5, 38, 62, 4);
        Optional<Integer> min = stream5.min(Integer::compareTo);
        System.out.println(min.get());  // 1
    }
}

4.收集(collect)

        把一个流收集起来,最终可以是一个值也可以是一个新的集合。调用 Stream 接口提供的【collect(Collector<? super T, A, R collector)】方法实现,参数中的 Collector 对象大都是直接通过 Collectors 工具类获得,实际上传入的 Collector 决定了 collect() 的行为。

(1)归集(toList / toSet / toMap)

        因为 Stream 流不存储数据,那么在 Stream 流中数据处理完成后,若需要把数据存入集合中,就需要使用归集操作。

        Collectors 提供了 toList、toSet、toMap 操作,此时并没有明确存储数据对应的集合具体类型, 若需要明确具体类型,需要使用toCollection 来实现。

public class CollectTo {
    public static void main(String[] args) {
        /*
         * toList():将流中的元素收集到一个List集合中
         * */
        Student s1 = new Student("孙悟空", 18);
        Student s2 = new Student("猪八戒", 2);
        Student s3 = new Student("唐僧", 45);

        Stream<Student> stream1 = Stream.of(s1, s2, s3);
        List<Student> collect1 = stream1.collect(Collectors.toList());
        System.out.println(collect1);

        /*
         * toSet():将流中的元素收集到一个Set集合中
         * */
        Stream<Integer> stream2 = Stream.of(1, 3, 5, 7, 9);
        Set<Integer> collect2 = stream2.collect(Collectors.toSet());
        System.out.println(collect2);

        /*
         * toMap():将流中的元素收集到一个Map集合中
         * */
        Stream<String> stream3 = Stream.of("1:I ", "2:Love ", "3:You ");
        Map<String, String> collect3 = stream3.collect(Collectors.toMap(new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.substring(0, s.indexOf(":"));
            }
        }, new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s.substring(s.indexOf(":") + 1);
            }
        }));
        collect3.forEach((k, v) -> System.out.println(k + ":" + v));

        /*
         * toCollection()
         * */
        Stream<Double> stream4 = Stream.of(3.14, 5.20, 13.14);
        HashSet<Double> collect4 = stream4.collect(Collectors.toCollection(HashSet::new));
        System.out.println(collect4);
    }
}


(2)统计(counting / averaging)

  1. 计数:counting;
  2. 平均值:averagingInt、averagingLong、averagingDouble;
  3. 最值:maxBy、minBy;
  4. 求和:summingInt、summingLong、summingDouble;
  5. 统计以上所有:summarizingInt、summarizingLong、summarizingDouble。
public class CollectCouAve {
    public static void main(String[] args) {
        /*
         * counting
         * */
        Student s1 = new Student("光光", 18);
        Student s2 = new Student("冬冬", 2);
        Student s3 = new Student("球球", 16);
        Student s4 = new Student("红红", 23);
        Student s5 = new Student("白白", 45);
        Student[] students = {s1, s2, s3, s4, s5};
        Stream<Student> stream1 = Arrays.stream(students);
        Long collect1 = stream1.collect(Collectors.counting());
        System.out.println(collect1);  // 5

        /*
         * averagingInt
         * */
        Stream<Student> stream2 = Arrays.stream(students);
        Double collect2 = stream2.collect(Collectors.averagingInt(Student::getAge));
        System.out.println(collect2);   // 20.8

        /*
         * maxBy
         * */
        Stream<Student> stream3 = Arrays.stream(students);
        Optional<Student> collect3 = stream3.collect(Collectors.maxBy((o1, o2) -> o1.getAge() - o2.getAge()));
        System.out.println(collect3.get()); // Student{name='白白', age=45}

        /*
         * summingInt
         * */
        Stream<Student> stream4 = Arrays.stream(students);
        Integer collect4 = stream4.collect(Collectors.summingInt(Student::getAge));
        System.out.println(collect4);   // 104

        /*
         * summarizingInt
         * */
        Stream<Student> stream5 = Arrays.stream(students);
        IntSummaryStatistics collect5 = stream5.collect(Collectors.summarizingInt(Student::getAge));
        System.out.println(collect5);   // IntSummaryStatistics{count=5, sum=104, min=2, average=20.800000, max=45}
    }
}


(3)分组(groupingBy)

        将 Stream 按条件分为若干个 Map。 

public class GroupingByTest {
    public static void main(String[] args) {
        Student s1 = new Student("宋江", 18);
        Student s2 = new Student("卢俊义", 18);
        Student s3 = new Student("吴用", 23);
        Student s4 = new Student("公孙胜", 23);
        Student s5 = new Student("关胜", 45);
        Student[] students = {s1, s2, s3, s4, s5};
        Stream<Student> stream = Arrays.stream(students);
        Map<Integer, List<Student>> collect = stream.collect(Collectors.groupingBy(Student::getAge));
        collect.forEach((k, v) -> System.out.println(k + ":" + v));
    }
}


(4)接合(joining)

        将 Stream 计算的数据按照一定规则进行拼接。 

public class JoiningTest {
    public static void main(String[] args) {
        Student s1 = new Student("州洲", 18);
        Student s2 = new Student("冬冬", 2);
        Student s3 = new Student("由由", 16);
        Student s4 = new Student("辉辉", 23);
        Student s5 = new Student("季季", 45);

        Student[] students = {s1, s2, s3, s4, s5};
        Stream<Student> stream = Arrays.stream(students);
        Stream<String> stream1 = stream.map(Student::getName);
        String collect = stream1.collect(Collectors.joining(","));
        System.out.println(collect);    // 州洲,冬冬,由由,辉辉,季季
    }
}

相关文章:

  • 使用Python实现矢量路径的压缩、解压与可视化
  • Python | 在Pandas中按照中值对箱形图排序
  • 二叉树的最近公共祖先二叉搜索树的最近公共祖先
  • 25届双非控制硕士求职回顾
  • ARM架构FFmpeg极致优化交叉编译指南
  • Linux:DNS服务配置(课堂实验总结)
  • 怎么免费下载GLTF/GLB格式模型文件,还可以在线编辑修改
  • instructor 库实现缓存
  • 【C#】.NET 8适配器模式实战:用C#实现高可用系统集成与接口桥接艺术
  • AutoGen参数说明
  • Kubernetes中的Label和Selector核心作用与应用场景
  • AI相关视频
  • 字符串与栈和队列-算法小结
  • 驱动开发硬核特训 · Day 10 (理论上篇):设备模型 ≈ 运行时的适配器机制
  • c++中的this
  • 用java代码如何存取数据库的blob字段
  • 02 - spring security基于配置文件及内存的账号密码
  • 设计模式 --- 访问者模式
  • 【LeetCode】算法详解#4 ---合并区间
  • 进程线程回顾
  • 商务部:对原产于美国、欧盟、台湾地区和日本的进口共聚聚甲醛征收反倾销税
  • 杨建全已任天津市委副秘书长、市委市政府信访办主任
  • 李伟任山东省委常委、省纪委书记
  • 舱位已排到月底,跨境电商忙补货!美线订单大增面临爆舱,6月运价或翻倍
  • 龚正市长调研闵行区,更加奋发有为地稳增长促转型,久久为功增强发展后劲
  • 泰山、华海、中路等山东险企综合成本率均超100%,承保业务均亏损