java8新特性(四):Stream流的使用
过滤出素食
List<Dish> vegetarian = menu.stream().filter(Dish::isVegetarian).collect(Collectors.toList());
过滤出偶数,并且不重复的元素。
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);
截断流
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());
跳过元素
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());
映射
List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(toList());
List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(toList());
展开流
List<String> uniqueCharacters = words.stream().map(w -> w.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
查找和匹配
任意匹配
if(menu.stream().anyMatch(Dish::isVegetarian)){
System.out.println("The menu is (somewhat) vegetarian friendly!!");
}
全部匹配
boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);
全部不匹配
boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);
获取任意一个元素
Optional<Dish> dish =menu.stream() .filter(Dish::isVegetarian) .findAny();
归约
将一个流中的元素反复结合运算得到一个值。
使用循环求和
List<Integer> numbers = new ArrayList<>();
int sum1 = 0;
for(int x: numbers){
sum1 += x;
}
使用流的API求和
int sum2 = numbers.stream().reduce(0, (a,b) -> a + b);
求最大值
Optional<Integer> max = numbers.stream().reduce(Integer::max);
求最小值
Optional<Integer> min = numbers.stream().reduce(Integer::min);
使用reduce相对于使用逐步迭代的好处在于,外部迭代改成了内部迭代,在需要实现并行执行的操时作变得简单。
有状态操作和无状态操作
比如map或者filter会从输入流中获取每一个元素,并且在输出流中得到一个结果,这些操作没有内部状态,称为无状态操作。
但是像reduce、sum、max这些操作都需要内部状态来累计计算结果,所以称为有状态操作。
还有一些操作sort、distinct,看上去和filter、map差不多,他们接收一个流,再生成一个流,但是区别在于排序和去重复项需要知道先前的历史。比如排序就需要将所有元素放入缓存区后才能给输出流加入一个项目,这个操作对缓存的要求是无上限的,流有多大就需要多大的缓存才能进行运算。这些操作也是有状态操作。
中间操作和终端操作
操作 | 类型 | 返回值 | 使用的函数接口或者类型 |
filter |
中间操作 |
Stream<T> |
Predicate<T> |
distinct |
中间操作,有状态,无边界 | Stream<T> | |
skip |
中间操作,有状态,有边界 | Stream<T> |
long |
limit |
中间操作,有状态,有边界 | Stream<T> |
long |
map |
中间操作 |
Stream<R> |
Function<T, R> |
flatMap |
中间操作 |
Stream<R> |
Function<T, Stream<R>> |
sorted |
中间操作,有状态,无边界 |
Stream<T> |
Comparator<T> |
anyMatch |
终端操作 |
boolean |
Predicate<T> |
noneMatch |
终端操作 |
boolean |
Predicate<T> |
allMatch |
终端操作 |
boolean |
Predicate<T> |
findAny |
终端操作 |
Optional<T> |
findFirst |
终端操作 | Optional<T> | |
forEach |
终端操作 |
void |
Consumer<T> |
collect |
终端操作 | R |
Collector<T, A, R> |
reduce |
终端操作,有状态,有边界 |
Optional<T> |
BinaryOperator<T> |
count |
终端操作 |
long |
中间操作就像是水管的一部分,终端操作就像水龙头,增加水管长度不会消耗水,只有打开水龙头才会开始消耗水。
Java5的时候就引入了自动装箱拆箱的功能, 在对包装类型进行数学计算的时候,包装类型就会被自动拆箱成基本类型, 而将一个基本类型的值赋值给一个包装类型的变量或者放入集合中时基本类型又会被自动装箱成包装类型,这个过程是需要消耗计算性能的。Java8的包装类型的流的计算过程中同样包含了对基本类型的自动装箱和拆箱的过程,所以Java8又引入了三个原始类型的流用来解决这个问题。(IntStream, DoubleStream和LongStream), 分别对应基本类型int, double, long,从而避免了自动装箱和拆箱的性能消耗。
基本类型的使用:
Stream的求和: int sum3 = menu.stream().map(Dish::getColories).reduce(0, Integer::sum);
使用IntStream的求和: int sum4 = menu.stream().mapToInt(Dish::getColories).sum();
创建一个流
1, 使用range方法给定一个范围来创建一个基本类型的流。
IntStream intStream = IntStream.range(1,100);
2,直接给值来创建流
Stream<String> stream = Stream.of("walker","sun","is","the","best");
3,由数组创建流
IntStream stream2 = Arrays.stream(numbers2);
4, 由文件生成流
try {
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}
5,由函数生成流
迭代: Stream.iterate(0, n -> n + 2).limit(10) .forEach(System.out::println);
生成:Stream.generate(Math::random).limit(5) .forEach(System.out::println);
--------------------- 本文来自 剑侠情猿 的**** 博客 ,全文地址请点击:https://blog.****.net/sunjin9418/article/details/53086565?utm_source=copy