过滤
使用谓词过滤
filter方法接受一个谓词函数为参数,并返回包含所有匹配谓词的元素的流。如下例:
1
|
List<Dish> vegetarianMenu = menu.stream().filter(Dish::isVegetarian).collect(toList());
|
如下图所示:

过滤唯一元素
distinct方法返回包含唯一元素的流。如下例:
1
2
|
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println);
|
如下图所示:

切分流
Java 9添加了两个新方法takeWhile和dropWhile,可以有效地选择流中的元素。
takeWhile
takeWhile方法使用谓词切分流,即使是无限流。一旦发现一个元素不满足条件,takeWhile会立刻停止。
1
|
List<Dish> slicedMenu1 = specialMenu.stream().takeWhile(dish -> dish.getCalories() < 320).collect(toList());
|
dropWhile
dropWhile和takeWhile正好相反。
1
|
List<Dish> slicedMenu2 = specialMenu.stream().dropWhile(dish -> dish.getCalories() < 320).collect(toList());
|
截断流
limit(n)方法返回一个不大于指定长度的流
1
|
List<Dish> dishes = specialMenu.stream().filter(dish -> dish.getCalories() > 300).limit(3).collect(toList());
|
跳过元素
skip(n)方法返回一个丢弃前n个元素的流,如果流中的元素小于n,则返回一个空流
1
|
List<Dish> dishes = menu.stream().filter(d -> d.getCalories() > 300).skip(2).collect(toList());
|
映射
map
map方法接受一个Function<T, R>为参数,将其应用于每一个元素,并映射为一个新的元素
1
|
List<Integer> dishNameLengths = menu.stream().map(Dish::getName).map(String::length).collect(toList());
|
flatMap
如何返回一组单词中所有唯一的字符呢?比如[“Hello”, “World”],你想要返回[“H”, “e”, “l”, “o”, “W”, “r”, “d”] 。第一次尝试如下:
1
|
words.stream().map(word -> word.split("")).distinct().collect(toList());
|
map返回的流为Stream<String[]>,而你想要的流是Stream<String>,这样不对。
如下图所示:

尝试使用map和Arrays.stream
首先你需要一个字符串流而不是数组流。Arrays.stream方法接受一个数组并产生一个包含数组元素的流:
1
2
|
String[] arrayOfWords = {"Goodbye", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);
|
第二次尝试:
1
|
words.stream().map(word -> word.split("")).map(Arrays::stream).distinct().collect(toList());
|
这个方法依然不行,因为map(Arrays::stream)返回一组流,准确的说是List<Stream<String>>。
使用flatMap
可以使用flatMap解决这个问题:
1
|
List<String> uniqueCharacters = words.stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(toList());
|
flatMap将map(Arrays::stream)产生的一组流合并为一个流。
如下图所示:

查找和匹配
匹配至少一个元素
anyMatch 用于查找至少一个匹配给定谓词,返回boolean
1
2
3
|
if (menu.stream().anyMatch(Dish::isVegetarian)) {
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
|
匹配所有元素
allMatch检查是否所有元素匹配给定谓词,返回boolean
1
|
boolean isHealthy = menu.stream().allMatch(dish -> dish.getCalories() < 1000);
|
不匹配任何元素
noneMatch 所有元素都不满足给定谓词,返回boolean
1
|
boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);
|
anyMatch,allMatch 和noneMatch 都具有短路求值的特性。
查找一个元素
findAny返回流中任意一个元素。
1
|
Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();
|
查找第一个元素
findFirst和findAny类似,findFirst查找第一个元素
1
2
|
List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream().map(n -> n * n).filter(n -> n % 3 == 0).findFirst();
|
findFirst在并行处理的时候有更多的限制,如果你不介意返回哪个元素,并行处理的时候选择findAny
归纳
计算元素的和
使用reduce计算流中所有元素的和:
1
|
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
|
reduce接受2个参数:
- 一个初始值
- 一个
BinaryOperator<T>计算2个元素的值并产生一个新值
你可以使用方法引用使得代码更简明:
1
|
int sum = numbers.stream().reduce(0, Integer::sum);
|
如下图所示:

没有初始值
还有一个不带初始值的reduce重载版本,它返回一个Optional对象
1
|
Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
|
查找最大和最小元素
1
2
|
Optional<Integer> max = numbers.stream().reduce(Integer::max);
Optional<Integer> min = numbers.stream().reduce(Integer::min);
|
流API总结表:
| 操作 |
类型 |
返回类型 |
参数(类型/函数接口) |
函数描述符 |
| filter |
中间操作 |
Stream<T> |
Predicate<T> |
T -> boolean |
| distinct |
中间操作(有状态-无上界) |
Stream<T> |
|
|
| takeWhile |
中间操作 |
Stream<T> |
Predicate<T> |
T -> boolean |
| dropWhile |
中间操作 |
Stream<T> |
Predicate<T> |
T -> boolean |
| skip |
中间操作(有状态-有上界) |
Stream<T> |
long |
|
| limit |
中间操作(有状态-有上界) |
Stream<T> |
long |
|
| map |
中间操作 |
Stream<R> |
Function<T, R> |
T -> R |
| flatMap |
中间操作 |
Stream<R> |
Function<T, Stream<R>> |
T -> Stream<R> |
| sorted |
中间操作(有状态-无上界) |
Stream<T> |
Comparator<T> |
(T, T) -> int |
| anyMatch |
结束操作 |
boolean |
Predicate<T> |
T -> boolean |
| noneMatch |
结束操作 |
boolean |
Predicate<T> |
T -> boolean |
| allMatch |
结束操作 |
boolean |
Predicate<T> |
T -> boolean |
| findAny |
结束操作 |
Optional<T> |
|
|
| findFirst |
结束操作 |
Optional<T> |
|
|
| forEach |
结束操作 |
void |
Consumer<T> |
T -> void |
| collect |
结束操作 |
R |
Collector<T, A, R> |
|
| reduce |
结束操作(有状态-有上界) |
Optional<T> |
BinaryOperator<T> |
(T, T) -> T |
| count |
结束操作 |
long |
|
|
数字流
原始类型的专有流
Java8提供3种基础类型的特殊流:IntStream,DoubleStream和LongStream。这些流提供了很多方便用于数值计算的方法,比如sum,max,min,average
映射为数字流
将一个流转换为数字流的最常用的方法是使用mapToInt ,mapToDouble 和mapToLong方法
1
|
int calories = menu.stream().mapToInt(Dish::getCalories).sum();
|
转换回对象流
使用boxed方法将数字流转换为对象流
1
2
|
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
|
默认值
数字流有3个特殊版本的Optinal类型:OptionalInt ,OptionalDouble 和OptionalLong
1
2
|
OptionalInt maxCalories = menu.stream().mapToInt(Dish::getCalories).max();
int max = maxCalories.orElse(1);
|
数字范围
IntStream和LongStream提供2个静态方法range和rangeClosed用于生成数字范围。range的范围是[start, end),rangeClosed的范围是[start, end]。
1
|
IntStream evenNumbers = IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0);
|
数字流实例
查找毕氏三元数(勾股数)
1
2
3
|
Stream<int[]> pythagoreanTriples = IntStream.rangeClosed(1, 100).boxed().flatMap(a -> IntStream.rangeClosed(a, 100).filter(b -> Math.sqrt(a*a + b*b) % 1 == 0).mapToObj(b ->
new int[]{a, b, (int)Math.sqrt(a * a + b * b)}));
pythagoreanTriples.limit(5).forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2]));
|
第二种方法
1
2
|
Stream<double[]> pythagoreanTriples2 = IntStream.rangeClosed(1, 100).boxed().flatMap(a ->
IntStream.rangeClosed(a, 100).mapToObj(b -> new double[]{a, b, Math.sqrt(a*a + b*b)}).filter(t -> t[2] % 1 == 0));
|
创建流
从一组值创建流
Stream.of接受任意个数参数并返回一个流
1
2
|
Stream<String> stream = Stream.of("Modern ", "Java ", "In ", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
|
Stream.empty返回一个空流
从可空元素创建流
Stream.ofNullable可以从可空对象创建流
1
2
3
|
String homeValue = System.getProperty("home");
Stream<String> homeValueStream = homeValue == null ? Stream.empty() : Stream.of(value);
Stream<String> homeValueStream = Stream.ofNullable(System.getProperty("home"));
|
从数组创建流
Arrays.stream可以从数组创建流
1
2
|
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
|
从文件创建流
java的NIO提供了许多静态方法返回流,比如Files.lines
1
2
3
4
5
|
long uniqueWords = 0;
try (Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(""))).distinct().count();
} catch(IOException e){
}
|
从函数创建流
Stream.iterate和Stream.generate可以让你创建一个无限流,通常需要结合limit使用
iterate
iterate接受2个参数:
- 初始值
UnaryOperator<T>用于迭代产生新值
1
|
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
|
在Java9中,iterate被增强了,支持谓词。
1
|
IntStream.iterate(0, n -> n < 100, n -> n + 4).forEach(System.out::println);
|
相当于
1
|
IntStream.iterate(0, n -> n + 4).takeWhile(n -> n < 100).forEach(System.out::println);
|
generate
generate接受一个Supplier<T>参数来产生新值
1
|
Stream.generate(Math::random).limit(5).forEach(System.out::println);
|
使用generate生成前20个Fibonacci序列数字:
1
2
3
4
5
6
7
8
9
10
11
12
|
IntSupplier fib = new IntSupplier() {
private int previous = 0;
private int current = 1;
public int getAsInt(){
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib).limit(20).forEach(System.out::println);
|