- Java8提供的Stream支持两种操作
- 一种是中间操作,如filter, map, skip, limit...
- 另外一种是终端操作,如count, findFirst, forEach和reduce...
中间操作不会消耗流,只是将一个流转换成另外一个流,类似于流水线。
而终端操作会消耗流,以产生一个最终结果,终端操作完成后,流就被消耗了,不可再调用相关操作流的方法。
-
Collectors
预定义好的部分终端操作
一、 规约与汇总
1. 查找流中的最大值和最小值
public static void main(String[] args) {
Random random = new Random();
Optional<Integer> minVal = Stream.generate(() -> random.nextInt(1000))
.limit(100)
.collect(Collectors.minBy(Comparator.comparingInt(x -> x)));
minVal.ifPresent(System.out::println);
Optional<Integer> maxVal = Stream.generate(() -> random.nextInt(1000))
.limit(100)
.collect(Collectors.maxBy(Comparator.comparingInt(x -> x)));
maxVal.ifPresent(x -> System.out.println("生成的最大随机值为: " + x));
//或或或或或或或或或或或或或或或或或或或或
Optional<Integer> maxVal2 = Stream.generate(() -> random.nextInt(1000))
.limit(100)
.max(Comparator.comparingInt(x -> x));
}
2. 求和,计算平均值与结果收集器
public void sumAvg() {
Random random = new Random();
// 生成1000范围内的数字的方法
Supplier<Integer> integerSupplier = () -> random.nextInt(1000);
// 生成1000个1000以内的数字,并返回一个列表
List<Integer> integerList = Stream.generate(integerSupplier)
.limit(1000)
.collect(Collectors.toList());
// 求和操作
Integer sum = integerList.stream()
.collect(Collectors.summingInt(x -> x));
System.out.println("求和结果: " + sum);
// 计算平均值操作
Double avg = integerList.stream()
.collect(Collectors.averagingDouble(x -> x));
System.out.println("平均值为: " + avg);
}
- 结果:
求和结果: 514905
平均值为: 514.905
- 在需要同时获取流中元素的个数,求和,平均值,最大值,最小值时,可使用收集器
XxxSummaryStatistics
。
DoubleSummaryStatistics summaryStatistics = integerList.stream()
.collect(Collectors.summarizingDouble(x -> x));
long count = summaryStatistics.getCount();
double average = summaryStatistics.getAverage();
double max = summaryStatistics.getMax();
double min = summaryStatistics.getMin();
double sumResult = summaryStatistics.getSum();
System.out.println(count);
System.out.println(average);
System.out.println(max);
System.out.println(min);
System.out.println(sumResult);
// XxxSummaryStatistics重写了toString()方法
System.out.println(summaryStatistics);
- 结果:
1000
514.905
999.0
0.0
514905.0
DoubleSummaryStatistics{count=1000, sum=514905.000000, min=0.000000, average=514.905000, max=999.000000}
3. 连接并返回字符串Collectors.join(delimiter)
public void joinDemo() {
AppleStream chinaApple = new AppleStream(10, "中国");
AppleStream usApple = new AppleStream(20, "米国");
AppleStream koreaApple = new AppleStream(30, "韩国");
String joinResult = Stream.of(chinaApple, usApple, koreaApple)
// 需要将流转换成Stream<String>
.map(AppleStream::toString)
.collect(Collectors.joining(",", "【", "】"));
System.out.println(joinResult);
System.out.println(IntStream.rangeClosed(1, 20)
.mapToObj(String::valueOf)
.collect(Collectors.joining(",", "", "")));
}
- 结果
【AppleStream(weight=10, country=中国),AppleStream(weight=20, country=米国),AppleStream(weight=30, country=韩国)】
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
4. 自定义规约操作:广义上的规约汇总
-
Collectors.reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)
- 参数
U identity
规约起始值,当流中没有元素时也会返回这个值。 - 参数
Function<? super T, ? extends U> mapper
在执行参数3方法之前对流中的元素进行的操作。 - 参数
BinaryOperator<U> op
即R apply(T t, U u)
,接收两个参数,并返回经过处理之后的值。
- 参数
public void reduceDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
//重量最大的苹果
Optional<Apple> maxWeightApple = Stream.<Apple>of(chinaApple, usApple, koreaApple)
.collect(Collectors.reducing((x1, x2) -> x1.getWeight() > x1.getWeight() ? x1 : x2));
maxWeightApple.ifPresent(System.out::println);
// 最小的重量
Integer minVal = Stream.of(chinaApple, usApple, koreaApple)
.collect(Collectors.reducing(0, Apple::getWeight, Integer::min));
System.out.println(minVal);
// 质量总和
Integer sumVal = Stream.of(chinaApple, usApple, koreaApple)
.collect(Collectors.reducing(0, Apple::getWeight, Integer::sum));
System.out.println(sumVal);
}
二、 分组
-
Collectors.groupingBy()
1. 分组- demo(一级分组): 根据规则 质量<=20为优秀,质量>20为一般 将apple进行分组。
/**
* 质量标准
*/
enum Quality {
/**
* 上乘
*/
PERFECT,
/**
* 普通
*/
NORMAL;
}
public void groupByDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
Map<Quality, List<Apple>> appleQualityMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
.collect(Collectors.groupingBy(curApple -> {
// 质量<=20为优秀,质量>20为一般
if (curApple.getWeight() <= 20) {
return Quality.PERFECT;
} else {
return Quality.NORMAL;
}
}));
System.out.println(JSON.toJSONString(appleQualityMap, true));
}
- 结果:
{
"NORMAL": [
{
"country": "韩国",
"weight": 30
},
{
"country": "日本",
"weight": 40
}
],
"PERFECT": [
{
"country": "中国",
"weight": 10
},
{
"country": "米国",
"weight": 20
}
]
}
- 多级分组
- 先根据质量进行分组,再根据国家进行分组
public void groupByDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple chinaAppleEnhance = new Apple(100, "中国");
Apple chinaAppleDoubleEnhance = new Apple(1000, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
Apple japanAppleEnhance = new Apple(80, "日本");
Apple japanAppleDoubleEnhance = new Apple(120, "日本");
Map<Quality, Map<String, List<Apple>>> appleQualityMap = Stream.of(chinaApple, chinaAppleEnhance, chinaAppleDoubleEnhance, usApple, koreaApple, japanApple, japanAppleEnhance, japanAppleDoubleEnhance)
.collect(Collectors.groupingBy(curApple -> {
// 质量<=20为优秀,质量>20为一般
if (curApple.getWeight() <= 20) {
return Quality.PERFECT;
} else {
return Quality.NORMAL;
}
// 再根据质量进行分组后再根据国家进行分组
}, Collectors.groupingBy(Apple::getCountry)));
System.out.println(JSON.toJSONString(appleQualityMap, true));
}
- 结果
{
"NORMAL": {
"韩国": [
{
"country": "韩国",
"weight": 30
}
],
"中国": [
{
"country": "中国",
"weight": 100
},
{
"country": "中国",
"weight": 1000
}
],
"日本": [
{
"country": "日本",
"weight": 40
},
{
"country": "日本",
"weight": 80
},
{
"country": "日本",
"weight": 120
}
]
},
"PERFECT": {
"米国": [
{
"country": "米国",
"weight": 20
}
],
"中国": [
{
"country": "中国",
"weight": 10
}
]
}
}
- 注意:可以无限叠加层N层Map哟。
2. 按子组收集数据(指定用于处理子组数据的函数)
- 观察源码发现,我们使用最多的接收一个参数的groupingBy()方法其实:
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
默认第二个参数是Collectors.toList()
,实际上可以替换成我们需要的方法,如计算子组的数量:
public void groupByDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple chinaAppleEnhance = new Apple(100, "中国");
Apple chinaAppleDoubleEnhance = new Apple(1000, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
Apple japanAppleEnhance = new Apple(80, "日本");
Apple japanAppleDoubleEnhance = new Apple(120, "日本");
Map<Quality, Long> qualityLongMap = Stream.of(chinaApple, chinaAppleEnhance, chinaAppleDoubleEnhance, usApple, koreaApple, japanApple, japanAppleEnhance, japanAppleDoubleEnhance)
.collect(Collectors.groupingBy(curApple -> {
// 质量<=20为优秀,质量>20为一般
if (curApple.getWeight() <= 20) {
return Quality.PERFECT;
} else {
return Quality.NORMAL;
}
// 再根据质量进行分组后再计算每组的元素的个数
}, Collectors.counting()));
System.out.println(JSON.toJSONString(qualityLongMap, true));
}
- 结果:
{
"PERFECT": 2,
"NORMAL": 6
}
- demo2: 先按质量分组,将子组List中的国家名字拼成String打印出来。
public void groupByDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
Map<Quality, String> qualityCountryMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
.collect(Collectors.groupingBy(curApple -> {
// 质量<=20为优秀,质量>20为一般
if (curApple.getWeight() <= 20) {
return Quality.PERFECT;
} else {
return Quality.NORMAL;
}
// 再将相应分组的国家的名字打印出来
}, Collectors.mapping(Apple::getCountry, Collectors.joining(","))));
System.out.println(JSON.toJSONString(qualityCountryMap, true));
}
- 结果:
{
"PERFECT": "中国,米国",
"NORMAL": "韩国,日本"
}
- demo3: 将子组的结果转换成另外一种格式
collectingAndThen()
先要求: 先根据apple的weight进行分组,再将每组中weight最大的apple找出来,再返回每组最大的apple的weight,即返回
Map<quality, maxWeight>
public void groupByDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
Map<Quality, Integer> qualityCountryMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
.collect(Collectors.groupingBy(curApple -> {
// 质量<=20为优秀,质量>20为一般
if (curApple.getWeight() <= 20) {
return Quality.PERFECT;
} else {
return Quality.NORMAL;
}
}, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Apple::getWeight)), (optionalApple) -> optionalApple.get().getWeight())));
System.out.println(JSON.toJSONString(qualityCountryMap, true));
}
- 结果:
{
"NORMAL":40,
"PERFECT":20
}
三、分区 partitioningBy()
分区是分组的一种特殊情况,分区返回的Map的keys只有
true
和false
。其他用法与groupingBy
基本一致。
public void partitioningByDemo() {
Apple chinaApple = new Apple(10, "中国");
Apple usApple = new Apple(20, "米国");
Apple koreaApple = new Apple(30, "韩国");
Apple japanApple = new Apple(40, "日本");
Map<Boolean, String> appleQualityCountryMap = Stream.of(chinaApple, usApple, koreaApple, japanApple)
//以质量20为分界线进行分区,将分区之后的apple的原产地的国家返回
.collect(Collectors.partitioningBy(x -> x.getWeight() > 20, Collectors.mapping(Apple::getCountry, Collectors.joining(","))));
System.out.println(JSON.toJSONString(appleQualityCountryMap, true));
}
- 结果:
{
"false": "中国,米国",
"true": "韩国,日本"
}
网友评论