Java8新特性-说说Stream的应用
共 6606字,需浏览 14分钟
·
2022-01-01 02:03
点击关注公众号,Java干货及时送达
JDK1.8新特性中较为重要的一块内容就是Stream API了,通过Stream API,我们改变了传统的集合操作,使得对集合的操作更加简单和可控。
初体验
在Stream出现以前,我们是如何对集合进行处理的呢?
比如现在有一个需求,从一个字符串集合中找出长度大于5的字符串,该如何实现呢?代码如下:
public static void main(String[] args) {
List list = Arrays.asList("apple", "banana", "pear", "orange", "peach", "watermelon");
for (int i = 0; i < list.size(); i++) {
if (list.get(i).length() > 5) {
System.out.println(list.get(i));
}
}
}
这段代码非常基础,你也可以使用增强for循环实现这一需求:
public static void main(String[] args) {
List list = Arrays.asList("apple", "banana", "pear", "orange", "peach", "watermelon");
for (String str : list) {
if (str.length() > 5) {
System.out.println(str);
}
}
}
如果要你将字符串长度大于5的字符串收集起来存入一个新的集合,你就得新建一个集合,然后在循环里进行添加:
public static void main(String[] args) {
List list = Arrays.asList("apple", "banana", "pear", "orange", "peach", "watermelon");
List newList = new ArrayList<>();
for (String str : list) {
if (str.length() > 5) {
newList.add(str);
}
}
System.out.println(newList);
}
上述的这些操作,在Stream API中将会非常容易实现,先看一个简单的例子:
public static void main(String[] args) {
List list = Arrays.asList("apple", "banana", "pear", "orange", "peach", "watermelon");
list.forEach(System.out::println);
}
遍历一个集合变得非常简单,虽然这并不是Steam的API,但这一特性仍然是在JDK1.8之后才支持的,接下来我们就用Steam实现一下将字符串长度大于5的字符串收集成为一个新集合的需求:
public static void main(String[] args) {
List list = Arrays.asList("apple", "banana", "pear", "orange", "peach", "watermelon");
List newList = list.stream().map(str -> {
if (str.length() > 5) {
return str;
}
return null;
}).collect(Collectors.toList());
System.out.println(newList);
}
我们首先不必纠结为什么这么写,先来看看它的效果:
[null, banana, null, orange, null, watermelon]
新集合中虽然都是长度大于5的字符串,但多了一些null值,所以,使用Stream的filter显然更适合这一需求:
public static void main(String[] args) {
List list = Arrays.asList("apple", "banana", "pear", "orange", "peach", "watermelon");
List newList = list.stream().filter(str -> {
return str.length() > 5;
}).collect(Collectors.toList());
System.out.println(newList);
}
运行结果:
[banana, orange, watermelon]
熟悉Lambda表达式的同学肯定能发现,这一写法还能能进行简化:
List newList = list.stream().filter(str -> str.length() > 5).collect(Collectors.toList());
Stream的创建
见识到了Stream API的广大神通了吧,接下来我们回到最基本的问题,如何创建一个Stream?
public static void main(String[] args) {
Stream stream = Stream.of(1, 2, 3);
}
最简单的方式便是使用of方法,通过of方法可以创建一个由指定元素组成的Stream,但通常情况下我们并不会使用到该方法,而是使用下面的几个方法:
private static void method(List list,String[] array) {
Stream listStream = list.stream();
Stream arrayStream = Arrays.stream(array);
}
Stream还支持从指定范围的数组中创建一个流(集合不支持):
private static void method(List list,String[] array) {
Stream stream = Arrays.stream(array, 0, 5);
}
创建一个不包含任何元素的流:
public static void main(String[] args) {
Stream
使用generate方法创建流:
public static void main(String[] args) {
Stream stream = Stream.generate(Math::random);
}
创建流的方式还有很多,这里就不一一列举了。
map方法
在最初的例子中,我们用到了map和filter方法,它们的作用分别是什么以及它们的用法是如何的呢?先来所说map方法,map方法类似于遍历操作,它会得到Stream中每个元素的映射,并对其一一生效,比如:
public static void main(String[] args) {
Stream stream = Stream.of(1, 2, 3, 4, 5);
List newList = stream.map(num -> {
return num + 1;
}).collect(Collectors.toList());
System.out.println(newList);
}
这段程序的作用就是对流中的每个元素都做加1操作,运行结果如下:
[2, 3, 4, 5, 6]
对于那些需要对集合/流中的每个元素都要做相同操作的需求,就非常适合使用map方法,比如前后端分离开发中,后端经常需要从数据库中查询出数据,然后将其封装成VO传递给前端,这一需求就能够使用map方法实现:
private static void method() {
List userList = userMapper.findAll();
List
filter方法
filter方法与map类似,它也会作用于流中的每个元素,但与其不同的是,filter方法是用来做过滤操作的,在filter中我们需要返回一个boolean类型的值,如果返回为true,则说明该值是我们需要的,如果为false,该值就会被抛弃掉,比如:
public static void main(String[] args) {
Stream stream = Stream.of(1, 35, 7, 4, 3, 5, 54, 57, 36);
List newList = stream.filter(num -> {
return num > 30;
}).collect(Collectors.toList());
System.out.println(newList);
}
这段程序的作用是取出流中数值大于30的数,运行结果如下:
[35, 54, 57, 36]
filter可以用来对流中的数据做过滤处理,比如只想要性别为男的数据;只想要工资超过2万的员工信息等等。
其它操作
Stream的神奇操作远不止这些,下面再介绍一些比较常用的功能,比如找出一个集合中的最大值:
public static void main(String[] args) {
List list = Arrays.asList(11, 232, 45, 6346, 14);
int max = 0;
for (Integer num : list) {
if(num > max){
max = num;
}
}
System.out.println(max);
}
而如果使用Stream,它将变得非常简单:
public static void main(String[] args) {
List list = Arrays.asList(11, 232, 45, 6346, 14);
Optional max = list.stream().max(Comparator.naturalOrder());
System.out.println(max.get());
}
求最小值,只需使用min方法即可:
public static void main(String[] args) {
List list = Arrays.asList(11, 232, 45, 6346, 14);
Optional max = list.stream().min(Comparator.naturalOrder());
System.out.println(max.get());
}
求和:
public static void main(String[] args) {
List list = Arrays.asList(11, 232, 45, 6346, 14);
Optional sum = list.stream().reduce(Integer::sum);
System.out.println(sum.get());
}
注意事项
在使用Strema API时需要注意的地方就是Lambda表达式的编写,如:
List list = wordList.stream().filter(word -> {
return word.startsWith("p");
list.forEach(System.out::println);
对于这样的一段程序,因为Lambda表达式体中仅包含了一条返回语句,所以表达式可以简写:
List list = wordList.stream().
filter(word -> word.startsWith("p")).collect(Collectors.toList());
list.forEach(System.out::println);
又比如:
List numList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional sum = numList.stream().reduce((x, y) -> {
return x + y;
});
System.out.println(sum);
首先因为Lambda表达式体仅包含一条返回语句,所以可以简写:
Optional sum = numList.stream().reduce((x, y) -> x + y);
我们也可以调用Integer类的静态方法sum进行求和:
Optional sum = numList.stream().reduce((x, y) -> Integer.sum(x, y));
又因为Lambda表达式体中仅包含一条语句,且该语句调用了一个绝对存在的方法,则可以简写为:
Optional sum = numList.stream().reduce(Integer::sum);
这就是Lambda表达式中的方法引用,具体细节可以去了解一下Lambda的相关内容。
最后就是Stream的一些其他类型,当你使用of方法创建一个流时:
Stream stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
调用Stream类的of方法传入的是基本类型数据,但是会得到一个包装类型的Stream,我们知道,基本类型需要装箱成为包装类型,这一操作在数据量庞大的情况下是比较低效的,Stream API也考虑到这一点,所以提供了对应包装类型的Strema,如下:
IntStream intStream = IntStream.of(1, 2, 'a', 'z');
LongStream longStream = LongStream.of(10L);
DoubleStream doubleStream = DoubleStream.of(1.1f, 2.2);