[Java详解]Java8新特性–stream
在食用此文章之前请先移步Lamda表达式
什么是StreamAPI
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程 序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
以上都是废话 实际上StreamAPI给了开发者一个更好的操作数据的方法。让对数据的操作变得更加的方便。给了我们一个新的优雅美丽的方法。
Stream如何创建
简单分成基于数组与基于流吧
- 基于数组
在Java8之后,关于Arrays与Collection的接口被拓展啦。所以可以直接在对象上面调用方法
ArrayList<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
integers.add(4);
Stream<Integer> listStream = integers.stream();
也可以使用Arrays方法
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
- 基于流
调用Stream.of(T... values)
这种方法使用条件是,数据存在且固定:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
调用Stream.concat(Stream1, Stream2)
。
使用条件是有两个流,将两个流中的数据合并成一个
Stream.concat(stream1, stream2);
使用无限流
这种使用条件是,数据需要运算,且数据量未知:
有两种方法
-
迭代Stream.iterate(0, t -> t + 2)
-
生成Stream.generate(Math::random)
比如
// 遍历前100个等差数列
Stream.iterate(1, t -> t * 2).limit(100).forEach(System.out::println);
// 生成无限个整形(会出大问题,只是为了演示,故意写错的)
Stream.generate(new Random()::nextInt).forEach(System.out::println);
注意: 无限流,顾名思义,无穷无尽。在使用这种方法的时候,必须加上.limit(int lenght)来限制范围。
关于迭代生成
表面看上去有点抽象,因此抽出来说了。
对于参数(seed, f),官方注释是这么描述的
seed – the initial element
f – a function to be applied to the previous element to produce a new element
也就是说seed
是初始参数。相当于for(int i = 0; i ++ ; i < 10)中的int i = 0
。 f
是对于seed 的操作。相当于i++ 最后的limit(10)相当于数据大小。也就是i < 10
.
StreamAPI能干什么
正如开头所说,StreamAPI实际上是对数据的一种操作。因此讨论到StreamAPI能干什么的时候,不如去想一下,关于数据需要什么操作
基于遍历
在实际开发中,遍历一组数据的情况确确实实的存在。但是遍历有一个前提:对每一个数据进行同样的操作。
根据操作的方法与操作的结果,分成两类:映射、筛选
映射
可以这样去写出来一个代码:
Integer[] arr = {123,321,12321,123,33,22,11,2132};
for (int num : arr) {
num += 1024;
System.out.println(num); // 输出数组中的每一个元素+1024后的值
}
虽然看上去有点意义不明。但是实际上这是一个非常经典的例子:对数组进行遍历,对元素进行操作。
虽然进行了操作。但是实际上arr数组本身没有变化。也就是这样的操作没有被保存下来,无法进行下一步操作。换句话说:如果要对操作后的数据进行操作,需要更加复杂的代码。
在这种前提下,看一下StreamAPI是怎么做的:
Stream<Integer> stream = Arrays.stream(arr);
stream.map(num -> num + 1024).forEach(System.out::println);
是的,利用Stream和Lamda表达式。用了非常优雅的方式完成了这个操作。
除此之外,仔细观察就能发现Stream.map()的返回值依然是Stream。这意味着想要对结果进行后续操作也十分简单,后面会说。
诸如此类还有一下几种。功能一样,返回值不同
方法描述
map(Function f)
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。mapToDouble(ToDoubleFunction f)
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。mapToInt(ToIntFunction f)
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
4.mapToLong(ToLongFunction f)
接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。flatMap(Function f)
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
筛选与切片
其实上文已经提到过相关的方法了。.limit()其实就是一种切片的方法。除了选出前10个,当然也会存在选出后10个的方法。
截断与跳过
方法描述skip(long n)跳过元素,返回一个扔掉了前 n个元素的流。若流中元素不足 n个,则返回一个空流。与 limit(n) 互补limit(long maxSize)截断流,使其元素不超过给定数量
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
stream.limit(3).forEach(System.out::println);
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
stream.skip(3).forEach(System.out::println);
去重与排除
方 法描 述filter(Predicate p)接收 Lambda, 从流中排除某些元素distinct()筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
stream1.filter(num -> num > 100).forEach(System.out::println);
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
stream1.distinct().forEach(System.out::println);
基于元素
方法描述
allMatch(Predicate p)
检查是否匹配所有元素,anyMatch(Predicate p)
检查是否至少匹配一个元素noneMatch(Predicate p)
检查是否没有匹配所有元素findFirst()
返回第一个元素findAny()
返回当前流中的任意元素count()
返回流中元素总数max(Comparator c)
返回流中最大值min(Comparator c)
返回流中最小值forEach(Consumer c)
内部迭代 (使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
System.out.println(stream.allMatch(num -> num > 100));
stream = Arrays.stream(arr);
System.out.println(stream.noneMatch(num -> num < 100));
stream = Arrays.stream(arr);
System.out.println(stream.anyMatch(num -> num > 100));
stream = Arrays.stream(arr);
System.out.println(stream.max(Integer::compare));
收集
那么对元素处理完成之后呢?Stream实际上不是很常用。我们还是希望能将Stream转化成list更好。所以Stream也对这个东西处理。直接调用就行
Integer[] arr = {123,321,12321,123,33,22,11,2132};
Stream<Integer> stream = Arrays.stream(arr);
List<Integer> list = stream.sorted(Integer::compareTo).toList();
除此之外还有
toList
将结果保存为ListtoSet
将结果保存为SettoMap
将结果保存为Map
实战
小明从Radis、MySQL、内存中,分别得到三个员工列表。小明需要将这三个列表中的数据进行去重、对工资进行排序、只输出工资大于1000的前3个并得到一个列表。请问如何?
可以想到。如果没有Stream的话每一步都是不断的循环。所以Steam的优越性就出来啦; )
Employee employee1 = new Employee(1, 1000.00);
Employee employee2 = new Employee(2, 1200.00);
Employee employee3 = new Employee(3, 1030.00);
Employee employee4 = new Employee(4, 1004.00);
Employee employee5 = new Employee(5, 1100.00);
Employee employee6 = new Employee(6, 1020.00);
Employee employee7 = new Employee(7, 1004.00);
Employee employee8 = new Employee(8, 10010.00);
Employee employee9 = new Employee(9, 21000.00);
List<Employee> employees1 = new ArrayList<>();
employees1.add(employee1);
employees1.add(employee2);
employees1.add(employee3);
employees1.add(employee4);
List<Employee> employees2 = new ArrayList<>();
employees2.add(employee5);
employees2.add(employee6);
employees2.add(employee7);
employees2.add(employee8);
List<Employee> employees3 = new ArrayList<>();
employees3.add(employee9);
employees3.add(employee7);
employees3.add(employee5);
employees3.add(employee3);
// 只有这里是具体操作
Stream<Employee> stream = Stream.concat(employees1.stream(),
Stream.concat(employees2.stream(), employees3.stream()));
// 一步到位
stream.filter(employee -> employee.getSalary() > 1000)
.distinct()
.sorted((e1, e2) -> e2.getSalary().compareTo(e1.getSalary()))
.limit(3)
.toList()
.forEach(System.out::println);
以上就是Java8 Stream的用法。如果对你有帮助,希望能给我点一个免费的赞,您的每一个点赞是我最大的动力。