Java8新特性 - Stream
Java8中有两个非常有名的改进,一个是Lambda表达式,一个是Stream。如果我们了解过函数式编程的话,都知道Stream真正把函数式编程的风格引入到了java中。这篇文章由简入繁逐步介绍Stream。
Stream介绍
Stream是一个流,在Java.util.Stream包路径下,他的主要作用就是对集合数据进行查找过滤等操作。通俗解释就是一种高效且易用的数据处理方式。大数据领域也有一个Steam实时流计算框架,不过和这个可不一样。别搞混了。
Stream和Collection的区别就是:Collection只是负责存储数据,不对数据做其他处理,主要是和内存打交道。但是Stream主要是负责计算数据的,主要是和CPU打交道。现在明白了吧。
举个例子吧,比如说有一个集合Student数据,我们要删选出学生名字为“张三”的学生,或者是找出所有年龄大于18岁的所有学生。此时我们就可以直接使用Stream来筛选。当然了这只是给出了其中一个例子。Stream还有很多其他的功能。
Stream操作步骤
Stream执行流程很简单,主要有三个:
- 1、创建一个Stream:从一个数据源,如集合、数组中获取流。
- 2、使用Stream操作数据:一个操作的中间链,对数据源的数据进行操作。
- 3、终止Stream:一个终止操作,执行中间操作链,并产生结果。
要注意的是,对流的操作完成后需要进行关闭操作。
数据准备
- 首先创建一个Student类,以对该类的使用来了解Stream流。
public class Student {
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;
}
}
- 创建一个StudentData类用于获取数据。
public class StudentData {
public static List<Student> getStudents(){
ArrayList <Student> students = new ArrayList <>();
students.add(new Student(1,"小白",23,89.5));
students.add(new Student(2,"小蔡",22,90.9));
students.add(new Student(3,"小惠",22,87.1));
students.add(new Student(4,"小韩",25, 89.6));
return students;
}
}
创建Stream
- 方式一:通过集合创建Stream。
//通过一个集合创建stream
public static void test1() {
List <Student> students =StudentData.getStudents();
//第一种:返回一个顺序流
Stream <Student> stream = students.stream();
//第二种:返回一个并行流
Stream <Student> stream1 = students.parallelStream();
}
- 方式二:通过一个数组创建stream。
//通过一个数组创建stream
public static void test2() {
//获取一个整形stream
int[]arr = {1, 34, 2, 54, 56, 34};
IntStream stream = Arrays.stream(arr);
}
- 方式三:通过Stream.of。
//通过Stream.of
public static void test3() {
Stream <String> stringStream = Stream.of("1", "4", "34", "23");
Stream <Student> studentStream = Stream.of(
new Student(1, "小白", 23, 89.5),
newStudent(2, "小蔡", 22, 90.9)
);
}
- 方式四:创建一个无限流。
//创建无限流
public static void test4() {
//每隔5个数取一个,从0开始,此时就会无限循环
Stream<Integer> iterate = Stream.iterate(0,t -> t + 5);
//取出一个随机数
Stream<Double> generate = Stream.generate(Math::random);
}
Stream操作数据
- 操作1:筛选和切片。
filter
- - 接收Lambda,从流中排除某些操作;limit
- - 截断流,使其元素不超过给定对象;skip(n)
- - 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补;distinct
- - 筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
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岁的同学
list.stream().filter(item -> item.getAge() > 22).forEach(System.out::println);
//截断流:筛选出前3条
list.stream().limit(3).forEach(System.out::println);
//跳过元素:跳过前2个元素
list.stream().skip(2).forEach(System.out::println);
//过滤重复元素
list.stream().distinct().forEach(System.out::println);
}
- 操作2:映射。
map
- - 接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。flatMap
- - 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
public static void test6() {
//map操作
List <String> list = Arrays.asList("hello", "word", "tulun");
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);
}
- 操作3:排序。
sorted()
- - 自然排序(Comparable);sorted(Comparator com)
- - 定制排序(Comparator);
public static void test7() {
//自然排序
List <Integer> list = Arrays.asList(4, 7, 1, 3, 6, 8, 9, 3);
Stream <Integer> stream = list.stream();
stream.sorted().forEach(System.out::println);
//对象排序,对象排序可以先实现comparable接口或者直接指定
//第一种:先实现comparable接口
List <Student> students = StudentData.getStudents();
students.stream().sorted().forEach(System.out::println);
//第二种:直接指定comparable
List <Student> students1 = StudentData.getStudents();
students1.stream().sorted((e1,e2)-> (int) (e1.getScore()-e2.getScore())).forEach(System.out::println);
}
终止Stream
- 操作1:匹配和查找。
allMatch
- - 检查是否匹配所有元素;anyMatch
- - 检查是否至少匹配一个元素;noneMatch
- - 检查是否没有匹配所有元素;findFirst
- - 返回第一个元素;findAny
- - 返回当前流中的任意元素;count
- - 返回流中元素的总个数;max
- - 返回流中最大值;min
- - 返回流中最小值;
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);
}
- 操作2:归约。
reduce
- - 归约操作可以将流中元素反复结合起来,得到一个值。
public static void test9() {
//计算数的总和
List <Integer> list = Arrays.asList(4, 7, 1, 3, 6, 8, 9, 3);
list.stream().reduce(0, Integer::sum);
//计算学生总分
List <Student> students = StudentData.getStudents();
Stream <Double> doubleStream = students.stream().map(Student::getScore);
doubleStream.reduce(Double::sum);
}
- 操作3:收集。
collect
- - 将流转换为其他形式,接收一个Collector接口实现 ,用于给Stream中汇总的方法。
public static void test10() {
//返回一个list
List <Student> students = StudentData.getStudents();
List <Student> list = students.stream()
.filter(student -> student.getScore() > 88)
.collect(Collectors.toList());
//返回一个set
Set <Student> set = students.stream()
.filter(s -> s.getAge() > 23)
.collect(Collectors.toSet());
}
stream基本的语法就是这样,你会发现Stream就像是一个工具一样,可以帮我们分析处理数据,极其的好用,但是目前还不知道其效率如何。根据网上一位大佬的内存时间分析,其实在数据量比较庞大的时候,Stream可以为我们节省大量的时间,数据量小的时候并不明显。