前言:
上一篇文章 Java 8之stream介绍和使用 中讲解了stream
的定义和用法,简单介绍几个最基本最常用的方法,其实stream
还有更强大的功能,这篇文章就会给大家介绍stream
的进阶用法。
筛选:
在上一篇文章中我们介绍了使用filter
方法来筛选元素,filter
方法接受一个Predicate
类型的参数,我们可以传入一个Lamada表达式或者方法引用,原理在 Java 8之方法引用和Lambda表达式 中已经将结果。我们实际上是传入了一个条件,然后筛选出符合条件的元素,例如下面的这行代码就是筛选出年龄大于20的人。
List<Person> list = peoples.stream().filter(person -> person.getAge()>20).collect(toList());
实际上stream
还有别的方法可以进行筛选,下面我们来介绍几个常用的。
-
distinct
,这个方法可以帮助我们去重List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println);
-
limit
,这个方法可以让我们只取stream中的前几个,值得注意的是当我们用Set集合来存储元素时,因为Set是无序的,所以每次我们取到的前几个元素也会是无序的。List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream().limit(3).forEach(System.out::println);
-
skip
,这个方法可以让我们跳过元素,跳过多少个元素由我们指定。List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4); numbers.stream().skip(3).forEach(System.out::println);
映射:
我们经常遇到从对象中抽取自己需要的元素放入一个新的类型的集合中的情况,这就是映射操作,而stream
在这方面也提供了非常强大的支持。
-
map
,这个方法接受一个Lambda表达式,它会遍历整个流把这个函数作用到每个元素上,然后把输出的结果放到一个新流中。// 获取每个元素的name属性放入一个新流中,然后把这个流转为List类型。 List<Person> names = peoples.stream().map(Person::getName).collect(toList()); // 获取每个元素的字符串长度放入新流中,然后转为List类型。 List<String> words = Arrays.asList("AA", "B", "CCC", "DDDDDD"); List<Integer> wordLengths = words.stream().map(String::length).collect(toList());
-
flatmap
,这个方法比较特殊,它的作用是把多个相同类型的流连成一个流,我们看下面的代码,目的是为了把集合中的字符串都拆成单个字符然后放到一个集合,但是如果直接用map
方法的话返回的List
是String[]
类型的,这是因为word.split("")
返回的结果就是这个类型的。List<String> words = Arrays.asList("Hello", "world"); List<String[]> list = words.stream().map(word -> word.split("")).collect(toList());
在这里要提到
Arrays.stream()
这个方法,它的作用是接受一个数组,然后把这个数组里的元素转为一个流,所以我们可以尝试使用这个方法来改进下上面的代码。但是我们发现还是有问题,返回集合类型是Stream<String>
,因为map(Arrays::stream)
把数组里的每个元素都弄成了一个流。List<String> words = Arrays.asList("Hello", "world"); List<Stream<String>> list = words.stream().map(word -> word.split("")) .map(Arrays::stream).collect(toList());
现在
flatmap
的作用就出来了,我们把代码改进下,改成使用flatmap
接受Arrays::stream
,它的作用正是在上面map(Arrays::stream)
的基础上把集合里的流里面的元素合并成一个流,所以返回的List
类型就是String
类型的。List<String> words = Arrays.asList("Hello", "world"); List<String> list = words.stream().map(word -> word.split("")) .flatmap(Arrays::stream).collect(toList());
匹配:
stream同样也提供了很多方法来检查集合中是否包含了某个指定的值。注意,这些方法都属于 终端操作 ,也就是说调用了这些方法就会关闭流。
-
allMatch
List<String> numbers = Arrays.asList("Hello", "World"); boolean flag = numbers.stream().allMatch(string -> string.contains("z")); System.out.println(flag);
-
anyMatch
,这个方法会检查流中是否至少有一个元素匹配给定的值,返回一个boolean值。List<String> numbers = Arrays.asList("Hello", "World"); boolean flag = numbers.stream().anyMatch(string -> string.contains("z")); System.out.println(flag);
-
noneMatch
,这个方法则是和allMatch
方法做相反的操作。List<String> numbers = Arrays.asList("Hello", "World"); boolean flag = numbers.stream().noneMatch(string -> string.contains("z")); System.out.println(flag);
查找:
对于集合的操作最重要的就是从中查找符合条件的数据了,我们来看下面的方法。
-
findAny
,这个方法需要配合filter
方法使用,返回把筛选出来的第一个元素。注意,这里返回的是Optional
类型的对象,这个对象是Java 8新增的专门为了防止返回数据的时候遇到null
的情况,后续再作详细了解,目前只需要知道它有个isPresent
方法来判断元素是否为空,get
方法用来取值。List<String> numbers = Arrays.asList("Hello", "World"); Optional<String> optional= numbers.stream().filter(string -> string.contains("l")).findAny(); if(optional.isPresent()){ System.out.println(optional.get()); }
-
findFirst
,这个方法是用来取流中第一个元素的,目前看来好像没什么用,但是有时候我们可能会对流进行复杂的筛选,再选取筛选后的流中第一个元素。List<String> numbers = Arrays.asList("Hello", "World"); Optional<String> optional= numbers.stream().findFirst(); System.out.println(optional.toString());
归约:
归约就是把整个流归约成一个值的操作,比如求集合中最大的元素、所有元素值的和之类的操作。
-
reduce
,这个方法就是用来对元素的值进行操作的,我们这里做加法运算。它接受两个参数,第一个是初始值,就是开始计算前就已经有一个数值了。第二个参数是一个Lambda表达式,用来对各个元素做计算。List<Integer> list = Arrays.asList(1,2,3,4,5); int sum = list.stream().reduce(0,(a,b) -> a - b); System.out.println(sum);
在Java 8中
Integer
中新增了一个sum
方法,它的作用和上面的Lambda表达效果一样,所以我们可以使用这个方法的方法引用来简化代码。List<Integer> list = Arrays.asList(1,2,3,4,5); int sum = list.stream().reduce(0,Integer::sum); System.out.println(sum);
我们还可以用这个方法来求最大值和最小值,在
Integer
中还新增了min
、max
方法,等同于(x, y) -> x < y ? x : y
、(x, y) -> x > y ? x : y
,这样我们就可以求出流中的最大值和最小值了。Optional<Integer> min = numbers.stream().reduce(Integer::min); Optional<Integer> min = numbers.stream().reduce(Integer::max);
以上就是stream的筛选、查找、匹配和归约操作中比较常用的方法了,下面还会介绍分组、分区等功能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。