从Java8开始,新增了一个 java.util.stream
包,这个包下的类和接口用来处理集合中的元素,在这个包下面有一个Stream接口,我们主要使用这个接口来对集合进行操作。
首先来看下创建Stream有哪几种方式。
使用Stream自带的静态方法生成Stream对象,常见的静态方法有以下几个:
现在来看下每个静态方法的作用
Stream.of是由两个重载方法组成,一个传入单值,一个传入数组
String[] arr = {"hello", "world"}; Stream streamArr = Stream.of(arr); String str = "hello world"; Stream streamSingle = Stream.of(str);复制代码
Stream.generate和Stream.iterate可以用来生成具有多个元素的Stream,如果不加控制会一直生成下去,一般配合limit(n)使用
先来看下Stream.iterate
Stream<Integer> stream5 = Stream.iterate(0, n-> n+1) .limit(5); stream5.forEach(System.out::println);复制代码
打印
0 1 2 3 4复制代码
Stream.iterate方法第一参数设定一个初始值,第二个参数表示基于这个初始值,每次循环后返回一个新的值替换这个初始值。limit(5)表示循环5次结束,最后Stream中包含了5个元素。
再来看下Stream.generate
Stream.generate方法只有一个Supplier参数,意思是每次循环执行Supplier接口方法返回一个新的值,放入到Stream中,由于Supplier是一个函数式接口,因此可以直接写成Lambda表达式
AtomicInteger i = new AtomicInteger(); Stream.generate(()-> { return i.getAndIncrement(); }) .limit(5) .forEach(System.out::println);复制代码
上面的代码同样打印0~4。
除了Stream静态方法之外,还可以使用Collection接口中的stream()方法来生成Stream对象。
Collection<String> list = Arrays.asList("hello", "world"); Stream streamList = list.stream();复制代码
同理,只要是Collection接口的子类或实现类都可以使用stream()方法。
Stream中的方法有很多,大致归纳如下表格所示:
方法 | 方法参数 | 返回类型 | 描述 |
---|---|---|---|
filer |
Predicate |
Stream |
过滤数据 |
distinct | 无 |
Stream |
去重 |
map |
Function |
Stream |
返回新的数据 |
flatMap |
Function |
Stream |
返回新的数据,并做扁平化处理 |
sort |
Comparator |
Stream |
对数据进行排序操作 |
limit | long |
Stream |
截取前几条数据 |
skip | long |
Stream |
跳过几条数据 |
anyMatch |
Predicate |
boolean | 匹配任意一条数据,如果匹配到返回true |
noneMatch |
Predicate |
boolean | 如果没有匹配到数据,返回true |
allMatch |
Predicate |
boolean | 如果所有数据全部匹配到,返回true |
findAny | 无 |
Optional |
返回任意一条数据 |
findFirst | 无 |
Optional |
返回第一条数据 |
count | 无 | long | 返回元素个数 |
forEach |
Consumer |
void | 遍历元素,执行Consumer |
collect |
Collector |
R | 元素收集 |
reduce |
BinaryOperator |
Optional |
数据汇总 |
从方法的返回结果可以看出,这些方法可以分为两大类,一类是返回Stream对象,可以继续对Stream操作,这类方法也被称之为 中间操作(Intermediate operations)
,另一类是返回非Stream,结束操作,这类方法也被称之为 中端操作(Terminal operations)
,这两类方法往往一起配合操作。
下面我们挑选其中的几个方法来演示它们的作用。
filter方法用来筛选出我们想要的数据,方法参数是一个Predicate接口,因为Predicate是一个函数式接口,我们可以使用Lambda表达式来写。
Integer[] arr = { 1, 2, 3, 4, 5 }; long count = Stream.of(arr) .filter(i -> i % 2 == 0) .count(); System.out.println("偶数数量:" + count);复制代码
在这个例子中,我们筛选出了偶数数字,并且统计出偶数的数量。如果要打印每个偶数,可以使用forEach方法
Stream.of(arr) .filter(i -> i % 2 == 0) .forEach(System.out::println);复制代码
打印:
2 4复制代码
如果要查找任意一个元素,可以使用findAny
int num = Stream.of(arr) .filter(i -> i % 2 == 0) .findAny() .orElse(0); System.out.println("findAny:" + num);复制代码
注意,findAny()返回的是一个Optional对象,因为有可能没有找到数据,因此需要开发者自己处理没有找到数据的情况。同理 findFirst
也是返回一个Optional对象。
distinct方法会对元素进行去重操作,类似于SQL中的 SELECT distinct xx
Stream.of(1,1,2,3,3,4) .distinct() .forEach(System.out::println)复制代码
打印
1 2 3 4复制代码
使用sorted方法可以对元素进行排序操作
Stream.of(6,1,7,2,8,5) .sorted() .forEach(System.out::println);复制代码
打印
1 2 5 6 7 8复制代码
sorted()默认是从小到大排列,如果要从大到小降序,可以使用 .sorted(Comparator.reverseOrder())
Stream.of(6,1,7,2,8,5) .sorted(Comparator.reverseOrder()) .forEach(System.out::println);复制代码
可以看到,sorted方法允许传入一个比较器 Comparator
让开发者自己实现排序逻辑。下面是一个自定义Comparator例子:
@Data @AllArgsConstructor static class Goods { private String goodsName; private int price; } Stream.of( new Goods("iphoneX", 4000) , new Goods("mate30 pro", 5999) , new Goods("redmek20", 2999) ) .sorted((goods1, goods2) -> { return Integer.compare(goods1.getPrice(), goods2.getPrice()); }) .forEach(System.out::println);复制代码
这个列子演示了按商品价格从低到高排序。此处的sorted部分可以简化为: .sorted(Comparator.comparing(Goods::getPrice))
map方法可以返回一个新的数据对象,组成一个新的Stream。
List<Goods> list = Arrays.asList( new Goods("iphoneX", 4000) , new Goods("mate30 pro", 5999) , new Goods("redmek20", 2999) ); list.stream() .map(goods -> goods.getGoodsName()) .forEach(System.out::println);复制代码
上面的示例演示的是从原有的商品对象中拿到商品名称,然后组成一个新的List,其效果等同于
List<String> goodsNameList = new ArrayList<>(list.size()); for(Goods goods : list) { goodsNameList.add(goods.getGoodsName()); }复制代码
map方法一般配合collect()方法一起使用
List<Goods> list = Arrays.asList( new Goods("iphoneX", 4000) , new Goods("mate30 pro", 5999) , new Goods("redmek20", 2999) ); List<String> nameList = list.stream() .map(goods -> goods.getGoodsName()) .collect(Collectors.toList());复制代码
collect(Collectors.toList())
的意思是将Stream中的元素转换成List
flatMap()方法是map()方法的扁平化处理,与map不同的是,flatMap把返回Stream对象操作交给开发者自己处理。看下面的例子:
Stream<String[]> stream = Stream.of("I am Java", "hello world") .map(s -> s.split(" "));复制代码
这个例子的本意是想要将每个字符串进行拆分,把单词单独放入到Stream中,由于map返回的是一个字符串数组 String[]
,因此得到的Stream对象的泛型参数就是 Stream
,而不是 Stream
。
解决办法是使用flatMap:
Stream<String> stream2 = Stream.of("I am Java", "hello world") .flatMap(s -> Stream.of(s.split(" "))); stream2.forEach(System.out::println);复制代码
打印:
I am Java hello world复制代码
下面来看一个综合示例,演示的功能是: 查询商品名称,价格大于3000,按价格降序
public class StreamTest3 { @Data @AllArgsConstructor static class Goods { private String goodsName; private int price; } public static void main(String[] args) { List<Goods> list = Arrays.asList( new Goods("iphoneX", 4000) , new Goods("mate30 pro", 5999) , new Goods("redmek20", 2999) ); // 查询商品名称,价格大于3000,按价格降序 List<String> nameList = list.stream() .filter(goods -> goods.getPrice() > 3000) .sorted(Comparator.comparing(Goods::getPrice).reversed()) .map(Goods::getGoodsName) .collect(Collectors.toList()); System.out.println(nameList); } }复制代码
打印: [mate30 pro, iphoneX]
代码对应的SQL为:
SELECT goods_name FROM goods WHERE price > 3000 ORDER BY price DESC复制代码
本篇讲解了如何创建Stream以及Stream一些常用方法的使用方式,我们将会在下一篇着重讲解 collect()
和 reduce()
的用法。
定期分享技术干货,一起学习,一起进步!