stream对象

  1. Stream
  2. IntStream
  3. LongStream
  4. DoubleStream

创建

常用的三种方式:

● 使用list对象:
○ list.stream() − 为集合创建串行流。
○ list.parallelStream() − 为集合创建并行流。
● Arrays: Arrays.stream( T[] array) − 为数组创建流(可以创建IntStream,LongStream,DoubleStrem)。
● Stream: Stream.of(T… values) − 为一组同类型的数据创建流。

demo:

/**
* 集合接口有两个方法来生成流:
* 按照流的类型可分为串行流和并行流
* stream() − 为集合创建串行流。
* parallelStream() − 为集合创建并行流。
*/
private static Stream<String> createStreamFromCollection() {
List<String> list = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
return list.stream();
}
/**
* 使用Stream.of()创建流
* @return
*/
private static Stream<String> createStreamFromValues() {
return Stream.of("hello", "alex", "wangwenjun", "world", "stream");
}
/**
* 使用Arrays.stream()创建流
* 可以生成IntStream,LongStream,DoubleStream
* @return
*/
private static Stream<String> createStreamFromArrays() {
String[] strings = {"hello", "alex", "wangwenjun", "world", "stream"};
return Arrays.stream(strings);
}

流的操作类型
流的操作类型主要分为两种
● 中间操作 一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有下面即将介绍的filter、map等
● 终端操作 一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。终端操作的执行,才会真正开始流的遍历。如下面即将介绍的count、collect等

中间操作
filter筛选
过滤掉不符合条件的数据。
List integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream stream = integerList.stream().filter(i -> i > 3);

distinct去除重复元素
去除重复的元素
List integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream stream = integerList.stream().distinct();

limit返回指定流个数
通过limit方法指定返回流的个数,limit的参数值必须>=0,否则将会抛出异常

List integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream stream = integerList.stream().limit(3);

skip跳过流中的元素
跳过流中的元素,skip的参数值必须>=0,否则将会抛出异常
List integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
// 跳过前两个元素,所以打印结果为2,3,4,5
Stream stream = integerList.stream().skip(2);
map流映射
所谓流映射就是将接受的元素映射成另外一个元素
List stringList = Arrays.asList(“Java 8”, “Lambdas”, “In”, “Action”);
Stream stream = stringList.stream().map(String::length);
通过map方法可以完成映射,该例子完成中String -> Integer的映射,之前上面的例子通过map方法完成了Dish->String的映射,当然还能完成别的映射。

flatMap流转换
将一个流中的每个值都转换为另一个流
map(w -> w.split(" "))的返回值为Stream<String[]>,我们想获取Stream,可以通过flatMap方法完成Stream<String[]> ->Stream的转换

元素匹配
提供了三种匹配方式
● allMatch匹配所有
List integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
System.out.println(“值都大于3”);
}

通过allMatch方法实现
● anyMatch匹配其中一个
List integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
System.out.println(“存在大于3的值”);
}

// 等同于
for (Integer i : integerList) {
if (i > 3) {
System.out.println(“存在大于3的值”);
break;
}
}

存在大于3的值则打印,java8中通过anyMatch方法实现这个功能
● noneMatch全部不匹配
List integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
System.out.println(“值都小于3”);
}

终端操作
统计流中元素个数
● 通过count方法统计出流中元素个数
List integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();

查找
提供了两种查找方式
● findFirst查找第一个
// 通过findFirst方法查找到第一个大于三的元素并打印
List integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional result = integerList.stream().filter(i -> i > 3).findFirst();

● findAny随机查找一个
List integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional result = integerList.stream().filter(i -> i > 3).findAny();

通过findAny方法查找到其中一个大于三的元素并打印,因为内部进行优化的原因,当找到第一个满足大于三的元素时就结束,该方法结果和findFirst方法结果一样。提供findAny方法是为了更好的利用并行流,findFirst方法在并行上限制更多

reduce将流中的元素组合起来
假设我们对一个集合中的值进行求和
// jdk8之前
int sum = 0;
for (int i : integerList) {
sum += i;
}

// jdk8之后通过reduce进行处理
int sum = integerList.stream().reduce(0, (a, b) -> (a + b));

// 一行就可以完成,还可以使用方法引用简写成:
int sum = integerList.stream().reduce(0, Integer::sum);

reduce接受两个参数,一个初始值这里是0,一个BinaryOperator accumulator 来将两个元素结合起来产生一个新值, 另外reduce方法还有一个没有初始化值的重载方法
获取流中最小最大值
● 通过min/max获取最小最大值
Optional min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);

// 也可以写成:
OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();

● 通过minBy/maxBy获取最小最大值
Optional min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
Optional max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));

● 通过reduce获取最小最大值
Optional min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional max = menu.stream().map(Dish::getCalories).reduce(Integer::max);

求和
● 通过summingInt
int sum = menu.stream().collect(summingInt(Dish::getCalories));

如果数据类型为double 通过summingDouble求和,long通过summingLong求和。

方法进行求和
● 通过reduce
int sum = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);

● 通过sum
int sum = menu.stream().mapToInt(Dish::getCalories).sum();

在上面求和、求最大值、最小值的时候,对于相同操作有不同的方法可以选择执行。可以选择collect、reduce、min/max/sum方法,推荐使用min、max、sum方法。因为它最简洁易读,同时通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作

通过averagingInt求平均值
double average = menu.stream().collect(averagingInt(Dish::getCalories));

如果数据类型为double、long,则通过averagingDouble、averagingLong方法进行求平均

CollectingAndThen求平均值后对平均值进行操作
menu.stream().collect(Collectors.collectingAndThen(Collectors.averagingInt(Dish::getCalories), a -> “The Average Calories is->” + a));

通过summarizingInt同时求总和、平均值、最大值、最小值
IntSummaryStatistics intSummaryStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
double average = intSummaryStatistics.getAverage(); //获取平均值
int min = intSummaryStatistics.getMin(); //获取最小值
int max = intSummaryStatistics.getMax(); //获取最大值
long sum = intSummaryStatistics.getSum(); //获取总和
如果数据类型为double、long,则通过summarizingDouble、summarizingLong方法

通过foreach进行元素遍历
List integerList = Arrays.asList(1, 2, 3, 4, 5);
integerList.stream().forEach(System.out::println);
返回集合
List strings = menu.stream().map(Dish::getName).collect(toList());
Set sets = menu.stream().map(Dish::getName).collect(toSet());

通过遍历和返回集合的使用发现流只是把原来的外部迭代放到了内部进行,这也是流的主要特点之一。内部迭代可以减少好多代码量

通过遍历和返回集合的使用发现流只是把原来的外部迭代放到了内部进行,这也是流的主要特点之一。内部迭代可以减少好多代码量
通过joining拼接流中的元素
String result = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));

默认如果不通过map方法进行映射处理拼接的toString方法返回的字符串,joining的方法参数为元素的分界符,如果不指定生成的字符串将是一串的,可读性不强
通过groupingBy进行分组
Map<Type, List> result = dishList.stream().collect(groupingBy(Dish::getType));
在collect方法中传入groupingBy进行分组,其中groupingBy的方法参数为分类函数。还可以通过嵌套使用groupingBy进行多级分类
Map<Type, List> result = menu.stream().collect(groupingBy(Dish::getType,
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
})));

通过partitioningBy进行分区
分区是特殊的分组,它分类依据是true和false,所以返回的结果最多可以分为两组
Map<Boolean, List> result = menu.stream().collect(partitioningBy(Dish :: isVegetarian))
// 等同于
Map<Boolean, List> result = menu.stream().collect(groupingBy(Dish :: isVegetarian))

这个例子可能并不能看出分区和分类的区别,甚至觉得分区根本没有必要,换个明显一点的例子:
List integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List> result = integerList.stream().collect(partitioningBy(i -> i < 3));
返回值的键仍然是布尔类型,但是它的分类是根据范围进行分类的,分区比较适合处理根据范围进行分类

DEMO

聚合操作

package com.java8.demo;

import com.java8.bean.Dish;

import java.util.*;
import java.util.stream.Collectors;

public class CollectorsAction {

public  static List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH));

public static void main(String[] args) {
testAveragingDouble();
System.out.println();
testAveragingInt();
testAveragingLong();
testCollectingAndThen();
testCounting();
testGroupingByFunction();
testGroupingByFunctionAndCollector();
testGroupingByFunctionAndSupplierAndCollector();
testSummarizingInt();
}

/**
* 求平均值
*/
private static void testAveragingDouble() {
System.out.println("************************** Double类型求平均值 **************************");
Optional.ofNullable(menu.stream().collect(Collectors.averagingDouble(Dish::getCalories)))
.ifPresent(System.out::println);
}

/**
* 求平均值
*/
private static void testAveragingInt() {
System.out.println("************************** Int求平均值 ****************************");
Optional.ofNullable(menu.stream().collect(Collectors.averagingInt(Dish::getCalories)))
.ifPresent(System.out::println);
}

/**
* 求平均值
*/
private static void testAveragingLong() {
System.out.println("*************************** Long求平均值 ***************************");
Optional.ofNullable(menu.stream().collect(Collectors.averagingLong(Dish::getCalories)))
.ifPresent(System.out::println);
}

/**
* 求平均值,并对求到的平均值进行操作
*/
private static void testCollectingAndThen() {
System.out.println("******************求平均值后对平均值进行操作*****************************");
Optional.ofNullable(menu.stream().collect(Collectors.collectingAndThen(Collectors.averagingInt(Dish::getCalories), a -> "The Average Calories is->" + a)))
.ifPresent(System.out::println);
}

/**
* 统计流中的数据个数
*/
private static void testCounting() {
System.out.println("*********************统计流中的数据**************************");
Optional.of(menu.stream().collect(Collectors.counting())).ifPresent(System.out::println);
}

/**
* 将流中的数据进行分组,类似于关系型数据库中的分组查询
*/
private static void testGroupingByFunction() {
System.out.println("*********************** 分组 ************************");
Optional.of(menu.stream().collect(Collectors.groupingBy(Dish::getType)))
.ifPresent(System.out::println);
}

/**
* 将流中的数据进行分组并对分组后的数据进行聚合计算,
* 类似于关系型数据库中的分组查询后时候聚合函数
*/
private static void testGroupingByFunctionAndCollector() {
System.out.println("************************ 分组并统计 ***********************");
Optional.of(menu.stream().collect(Collectors.groupingBy(Dish::getType, Collectors.averagingInt(Dish::getCalories))))
.ifPresent(System.out::println);
}
/**
* 将流中的数据进行分组并对分组后的数据进行聚合计算,
* 类似于关系型数据库中的分组查询后时候聚合函数
* 并设置分组后的数据类型
*/
private static void testGroupingByFunctionAndSupplierAndCollector() {
System.out.println("************************分组并统计 设置分组后的数据类型 ***********************");
Map<Dish.Type, Double> map = menu.stream().collect(Collectors.groupingBy(Dish::getType, TreeMap::new, Collectors.averagingInt(Dish::getCalories)));
Optional.of(map.getClass()).ifPresent(System.out::println);
Optional.of(map).ifPresent(System.out::println);
}

/**
* 统计
*/
private static void testSummarizingInt() {
System.out.println("************************统计数值***********************");
IntSummaryStatistics result = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
Optional.of(result).ifPresent(System.out::println);
}

}