Java8 Stream-2 用流收集数据

2020-03-23  本文已影响0人  巴巴11

Collectors类提供的工厂方法(例如groupingBy)创建的收集器
将流元素归约和汇总为一个值
元素分组
元素分区

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

// 收集器用作高级归约
List<Transaction> transactions =
    transactionStream.collect(Collectors.toList());

Map<Currency, List<Transaction>> transactionsByCurrencies =
    transactions.stream().collect(groupingBy(Transaction::getCurrency));

// 归约和汇总
long howManyDishes = menu.stream().collect(Collectors.counting());
long howManyDishes = menu.stream().count();

// 查找流中的最大值和最小值
Comparator<Dish> dishCaloriesComparator =
    Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish =
    menu.stream()
        .collect(maxBy(dishCaloriesComparator));    

// 汇总
// Collectors.summingLong和Collectors.summingDouble方法的作用完全一样
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

// 汇总不仅仅是求和;还有Collectors.averagingInt,连同对应的averagingLong和
// averagingDouble可以计算数值的平均数
double avgCalories =
    menu.stream().collect(averagingInt(Dish::getCalories));


IntSummaryStatistics menuStatistics =
    menu.stream().collect(summarizingInt(Dish::getCalories));

// 连接字符串
String shortMenu = menu.stream().map(Dish::getName).collect(joining());
String shortMenu = menu.stream().collect(joining());
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));

// 广义的归约汇总
// Collectors.reducing工厂方法是所有这些特殊情况的一般化。
int totalCalories = menu.stream()
    .collect(reducing(0, Dish::getCalories, (i, j) -> i + j));

它需要三个参数。
第一个参数是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值
和而言0是一个合适的值。
第二个参数函数,将菜肴转换成一个表示其所含热量的int。
第三个参数是一个BinaryOperator,将两个项目累积成一个同类型的值。这里它就是对两个int求和。

Optional<Dish> mostCalorieDish =
  menu.stream()
  .collect(reducing((d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2));

int totalCalories = menu.stream().collect(reducing(0,Dish::getCalories,Integer::sum));

// 分组
Map<Dish.Type, List<Dish>> dishesByType =
    menu.stream().collect(groupingBy(Dish::getType));

public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
    else if (dish.getCalories() <= 700) return
    CaloricLevel.NORMAL;
    else return CaloricLevel.FAT;
} ));

// 多级分组
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
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;
} )
)
);

Map<Dish.Type, Long> typesCount = menu.stream().collect(
groupingBy(Dish::getType, counting()));

Map<Dish.Type, Optional<Dish>> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
maxBy(comparingInt(Dish::getCalories))));

Map<Dish.Type, Dish> mostCaloricByType =
menu.stream()
.collect(groupingBy(Dish::getType,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));

Map<Dish.Type, Integer> totalCaloriesByType =
menu.stream().collect(groupingBy(Dish::getType,
summingInt(Dish::getCalories)));

Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
menu.stream().collect(
groupingBy(Dish::getType, mapping(
dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT; },
toSet() )));

Map<Dish.Type, Set<CaloricLevel>> caloricLevelsByType =
menu.stream().collect(
groupingBy(Dish::getType, mapping(
dish -> { if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT; },
toCollection(HashSet::new) )));

// 分区
Map<Boolean, List<Dish>> partitionedMenu =
menu.stream().collect(partitioningBy(Dish::isVegetarian));
List<Dish> vegetarianDishes = partitionedMenu.get(true);

Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));

Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));


Collectors类的静态工厂方法


image.png
image.png

Collector接口包含了一系列方法,为实现具体的归约操作(即收集器)提供了范本。
已经看过了Collector接口中实现的许多收集器,例如toList或groupingBy。
这也意味着,可以为Collector接口提供自己的实现,从而自由地创建自定义归约操作。

package java.util.stream;
public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher();
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
}

自定义收集器

package com.hw;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class MyCollector<T> implements Collector<T, List<T>, List<T>> {
    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }

    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }

    @Override
    public BinaryOperator<List<T>> combiner() {
        return (l1, l2) -> {
            l1.addAll(l2);
            return l1;
        };
    }

    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT));
    }
}

上一篇下一篇

猜你喜欢

热点阅读