Java8新特性之----Stream
- 前言
- Stream介绍
- Stream的操作
- 创建流
- 四种创建流的方法
- 通过一个集合创建Stream
- 通过一个数组创建Stream
- 通过Stream.of
- 创建无限流
- 使用Stream操作数据
- 操作一:筛选和切片
- 操作二:映射
- 操作三:排序
- 终止Stream
- 操作一:匹配和查找
- 操作二:归约
- 操作三:收集
- 结尾
前言
Java作为一门火热的编程语言,为当今的互联网世界立下了汗马功劳,如今她已经25 岁了,从诞生至今随着时间的推移,Java已经更新换代到了Java14了,但是根据JetBrains发布的调查结果来看,Java8,即JDK1.8仍是最常被使用的一个版本,她向我们提供了很多方便的新接口,今天来讲一下Stream这个新特性。
Stream介绍
Stream也是一个流,不过和一般的IO流有一些不一样的地方。
Stream是在Java.util.Stream包路径下,主要作用就是对集合数据进行查找过滤等操作,是一种高效且易用的数据处理方式。
对于一般规模的数据其实和普通的集合没有什么太大的区别,但是数据一旦规模很大,Stream的效果就很明显了。它会将数据处理为一种流。
在大数据领域有一个Stream流实时框架
Stream和Collection的区别:
- Collection只是负责存储数据,不对数据做其他处理主要和内存打交道。
- Stream主要是负责计算数据的,主要和CPU打交道(查询过滤)。
Stream的操作
操作流程比较简单(和IO流类似):
- 创建一个Stream:从一个数据源,如集合、数组中获取流;
- 使用Stream操作数据:一个操作的中间链,对数据源的数据进行操作;
- 终止Stream:一个终止操作,执行中间操作链,并产生结果;
创建流
首先创建一个数据源类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | public class Student implements Comparable{ private Integer id; private String name; private Integer age; private Double score; public Student(Integer id, String name, Integer age, Double score) { this.id = id; this.name = name; this.age = age; this.score = score; } public Integer getId() { return id; } public String getName() { return name; } public Integer getAge() { return age; } public Double getScore() { return score; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } public void setScore(Double score) { this.score = score; } @Override public int compareTo(Object o) { Student o1=(Student)o; return o1.getAge()-this.getAge(); } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } } |
创建数据(仅仅是为了方便数据的写入)
1 2 3 4 5 6 7 8 9 10 | public class StudentData { public static List <Student> getStudents(){ ArrayList <Student> students=new ArrayList<>(); students.add(new Student(1,"赤",21,89.5)); students.add(new Student(2,"橙",22,90.5)); students.add(new Student(3,"黄",23,87.1)); students.add(new Student(4,"绿",24,89.7)); return students; } } |
四种创建流的方法
通过一个集合创建Stream
1 2 3 4 5 6 7 | public static void test1() { List<Student> students = StudentData.getStudents(); // 第一种 返回一个顺序流 Stream<Student> stream = students.stream(); // 第二种 返回一个并行流 Stream<Student> stream1 = students.parallelStream(); } |
通过一个数组创建Stream
1 2 3 4 5 | public static void test2() { // 获取一个整形Stream int[] arr = {1, 34, 2, 54, 56, 34}; IntStream stream = Arrays.stream(arr); } |
通过Stream.of
1 2 3 4 5 6 7 | public static void test3() { Stream<String> stringStream = Stream.of("A", "B", "C", "D"); Stream.of( new Student(1, "Chuki", 20, 90.5), new Student(2, "Zorina", 20, 99.9) ); } |
创建无限流
1 2 3 4 5 6 7 8 9 10 | public static void test4() { // 每隔5个数取一次,从0开始,此时就会无限循环 // 相当于: // for (int i = 0; ; ) { // i += 5; // } Stream<Integer> iterate = Stream.iterate(0, t -> t + 5); // 取出一个随机数 Stream<Double> generate = Stream.generate(Math::random); } |
使用Stream操作数据
操作一:筛选和切片
filter——接收lambda,从流中排除某些操作;
limit——截断流,使其元素不超过给定对象
skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
distinct——筛选,通过流所生成元素的hashCode()和equals去除重复元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public static void test5() { /** * 写成System.out::println这种语法叫做方法引用。该功能特性也是JDK8以后引入的,你可以把它看成lambdas表达式的语法糖 * 可以用lambdas表达式改写成以下代码:list.forEach((t) -> System.out.println(t)); * 这样还不明白的话,也可以这样:list.forEach((String t)-> System.out.println(t)); * 这样的效果跟System.out::println是一样 */ List<Student> list = StudentData.getStudents(); // 过滤:过滤出所有年龄大于22岁的同学 System.out.println("============="); list.stream().filter(item -> item.getAge() > 22).forEach(System.out::println); // 截断流:筛选出前三条 list.stream().limit(3).forEach(System.out::println); // 跳过元素:跳过前两个元素 list.stream().skip(2).forEach(System.out::println); System.out.println("================"); // 过滤重复元素 list.stream().distinct().forEach(System.out::println);//没有重复的,根据hashcode判断 } |
操作二:映射
map——接受Lambda,将元素转换成其他形式或提取信息。接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static void test6() { //map操作 List<String> list = Arrays.asList("hello", "word", "chuki"); Stream<String> stream = list.stream(); //将每一个小写字母都转换为大写字母映射 stream.map(str -> str.toUpperCase()).forEach(System.out::println); //筛选出所有的年龄,再过滤出所有大于23的年龄 List<Student> students = StudentData.getStudents(); Stream<Student> stream1 = students.stream(); //将流中的每一值转换为另一个值 Stream<Integer> stream2 = stream1.map(Student::getAge); stream2.filter(age -> age > 23).forEach(System.out::println); } |
在这里插入图片描述
操作三:排序
sorted()——自然排序(Comparable)
sorted(Comparator com)——定制排序(Comparator)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static void test7() { // 自然排序 List<Integer> list = Arrays.asList(4, 8, 6, 5, 2, 3, 1, 7); Stream<Integer> stream = list.stream(); stream.sorted().forEach(System.out::println); // 对象排序,对象排序可以先实现comparable接口或者直接指定 // 第一种:先实现comparabl System.out.println("=========年龄从大到小排序=========="); List<Student> students = StudentData.getStudents(); students.stream().sorted().forEach(System.out::println); // 第二种:直接指定comparator System.out.println("==========年龄从小到大排序=========="); List<Student> students1 = StudentData.getStudents(); students1.stream().sorted((e1, e2) -> (int) (e1.getAge() - e2.getAge())).forEach(System.out::println); } |
终止Stream
操作一:匹配和查找
allMatch–检查是否匹配所有元素
anyMatch–检查是否至少匹配一个元素
noneMatch–检查是否没有匹配所有元素
findFirst–返回第一个元素
findAny–返回当前流中的任意元素
count–返回流中元素的总个数
max–返回流中最大值
min–返回流中最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static void test8() { List<Student> list = StudentData.getStudents(); //判断所有的学生年龄是否都大于20岁 boolean allMatch = list.stream().allMatch(student -> student.getAge() > 20); //判断是否存在学生的年龄大于20岁 boolean anyMatch = list.stream().anyMatch(student -> student.getAge() > 20); //判断是否不存在学生叫小白 boolean noneMatch = list.stream().noneMatch(student -> student.getName().equals("黄")); //查找第一个学生 Optional<Student> first = list.stream().findFirst(); //查找当前流中的元素 Optional<Student> any = list.stream().findAny(); //查找所有的学生数量 long count = list.stream().count(); //查找成绩大于90的数量 long count1 = list.stream().filter(student -> student.getScore() > 90).count(); //查找学生的最高分数 Stream<Double> doubleStream = list.stream().map(student -> student.getScore()); Optional<Double> max = doubleStream.max(Double::compareTo); System.out.println(max); } |
操作二:归约
reduce–归约操作可以将流中元素反复结合起来,得到一个值
1 2 3 4 5 6 7 8 9 10 11 12 | public static void test9() { //计算数的总和 List<Integer> list = Arrays.asList(4, 5, 6, 1, 8, 9, 2, 3, 7); Integer reduce = list.stream().reduce(0, Integer::sum); System.out.println(reduce); //计算学生总分 List<Student> students = StudentData.getStudents(); Stream<Double> doubleStream = students.stream().map(Student::getScore); Optional<Double> reduce1 = doubleStream.reduce(Double::sum); System.out.println(reduce1.get()); } |
操作三:收集
collect:将流转换为其他形式,接收一个Collector接口实现,用于给Stream中汇总的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static void test10() { //返回一个list List<Student> students = StudentData.getStudents(); List<Student> list = students.stream().filter(student -> student.getScore() > 88).collect(Collectors.toList()); System.out.println(list); //返回一个set Set<Student> set = students.stream().filter(s -> s.getAge() > 23).collect(Collectors.toSet()); System.out.println(set); } List<Student> list = students.stream().filter(student -> student.getScore() > 88).collect(Collectors.toList()); System.out.println(list); //返回一个set Set<Student> set = students.stream().filter(s -> s.getAge() > 23).collect(Collectors.toSet()); System.out.println(set); } |
结尾
stream基本的语法就是这样,你会发现Stream就像是一个下具一样,可以帮我们分析处理数据,极其的好用,但是目前还不知道其效率如何。
根据网上一位大佬的内存时间分析,其实在数据量比较庞大的时候,Stream可以为我们节省大量的时间。