java8新特性(五):Stream数据收集

收集器Collector
collect方法接受的参数 函数称为 收集器,也就是实现数据收集的策略。
一般来说,收集器collector会对元素应用一个转换函数,并将结果累积在一个数据结构中,从而产生最终输出。

假设一个需求要将交易数据根据货币类型分组
java7 之前需要这么写
List<Transaction> transactions =  new ArrayList<>();
Map<Currency,List<Transaction>>  resultMap =  new HashMap<>();
for(Transaction transaction : transactions){
    Currency currency = transaction.getCurrency();
    List<Transaction> subList = resultMap.get(currency);
    if(subList ==  null){
        subList =  new ArrayList<>();
        resultMap.put(currency, subList);
    }
    subList.add(transaction);
}

在java8 中 只需要这么写

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

groupingBy就是一个生成收集器的工厂方法


java8中预定义了很多收集器:

求最大值
Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish = menu.stream() .collect(maxBy(dishCaloriesComparator));

求和
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));

平均值
double avgCalories = menu.stream().collect(averagingInt(Dish::getCalories));

统计全部参数: 总数、最大值、最小值、平均值
IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));
IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}

字符串拼接
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));
pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon


求和还可以通过reduce 方法获得
int totalCalories = menu.stream().map(Dish::getCalories).reduce(Integer::sum).get();

int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum();

分组
Map<Dish.Type, List<Dish>> dishesByType = menu.stream().collect(groupingBy(Dish::getType));
{FISH=[prawns, salmon], OTHER=[french fries, rice, season fruit, pizza], MEAT=[pork, beef, chicken]}

多级分组
Map<String, Map<String, List<Dish>>> multiLevelDishes = menu.stream().collect( groupingBy(Dish::getType,  groupingBy(dish -> {
    if(dish.getColories() <=  400)
        return  "DIET";
    else if(dish.getColories() <=  700)
        return  "NORMAL";
    else 
        return  "FAT";
})));
结果是一个两级的Map:
{MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]}, FISH={DIET=[prawns], NORMAL=[salmon]},
OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}}


分组统计

Map<Dish.Type, Long> typesCount = menu.stream().collect( groupingBy(Dish::getType, counting()));
{MEAT=3, FISH=2, OTHER=4}

分组求最大值
Map<Dish.Type, Optional<Dish>> mostCaloricByType = menu.stream()
.collect(groupingBy(Dish::getType, maxBy(comparingInt(Dish::getCalories))));
{FISH=Optional[salmon], OTHER=Optional[pizza], MEAT=Optional[pork]}

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

分区是 分组的一个特殊类型。
Map<Boolean, List<Dish>> partitionMenu = menu.stream().collect(partitioningBy(Dish::isVegetarian));
结果:
{false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}

假设一个需求要将质数和非质数分区
要实现这个需求首先知道质数的定义: 质数定义为只能被1或者它本身整除的自然数。

我们写一个方法,它接收一个参数int n, 并且将前n个自然数分为质数和非质数。
首先要写一个测试摸个数是否质数的方法:

//除了1和n本身不能被其他自然数整除,那么n就是一个质数
public boolean isPrime( int n){
    return IntStream. range( 2, n).noneMatch(i -> n % i ==  0);
}

这个方法还可以优化
//如果n不能被2到它的平方根之间的自然数整除,那么n也不能被大于它的平方根在自然数整除。
public boolean isPrime( int n){
    int root = ( int) Math. sqrt(( double) n);
    return IntStream. range( 2, root).noneMatch(i -> n % i ==  0);
}

将质数和非质数分区
public Map<Boolean, List<Integer>> partitionPrimes( int n){
    return IntStream. range( 2, n).boxed().collect( partitioningBy(i -> isPrime(i)));
}


收集器类的静态工厂方法:
工厂方法 返回类型 使用场景 举例
toList List<T> Gather all the stream’s items in a List. List<Dish> dishes = menuStream.collect(toList());
toSet Set<T>
Gather all the stream’s items in a Set
, eliminating duplicates.
Set<Dish> dishes = menuStream.collect(toSet());
toCollection Collection<T> Gather all the stream’s items in the collection created by the provided supplier. Collection<Dish> dishes = menuStream.collect(toCollection(), ArrayList::new);
counting Long Count the number of items in the stream. long howManyDishes = menuStream.collect(counting());
summingInt Integer Sum the values of an Integer property of the items in the stream. int totalCalories = menuStream.collect(summingInt(Dish::getCalories));
averagingInt Double Calculate the average value of an Integer property of the items in the stream. double avgCalories = menuStream.collect(averagingInt(Dish::getCalories));
summarizingInt IntSummary-Statistics Collect statistics regarding an Integer property of the items in the stream, such as the maximum, minimum, total, and average. IntSummaryStatistics menuStatistics = menuStream.collect(summarizingInt(Dish::getCalories));
joining String Concatenate the strings resulting from the invocation of the toString method on each item of the stream. String shortMenu = menuStream.map(Dish::getName).collect(joining(", "));
maxBy Optional<T> An Optional wrapping the maximal element in this stream according to the given comparator or Optional.empty() if the stream is empty. Optional<Dish> fattest = menuStream.collect(maxBy(comparingInt(Dish::getCalories)));
minBy Optional<T> An Optional wrapping the minimal element in this stream according to the given comparator or Optional.empty() if the stream is empty. Optional<Dish> lightest = menuStream.collect(minBy(comparingInt(Dish::getCalories)));
reducing The type produced by the reduction operation Reduce the stream to a single value starting from an initial value used as accumulator and iteratively combining it with each item of the stream using a BinaryOperator. int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
collectingAndThen The type returned by the transforming function Wrap another collector and apply a transformation function to its result. int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size));
groupingBy Map<K, List<T>> Group the items in the stream based on the value of one of their properties and use those values as keys in the resulting Map. Map<Dish.Type,List<Dish>> dishesByType = menuStream.collect(groupingBy(Dish::getType));
partitioningBy Map<Boolean, List<T>> Partition the items in the stream based on the result of the application of a predicate to each of them. Map<Boolean, List<Dish>> vegetarianDishes = menuStream.collect(partitioningBy(Dish::isVegetarian));
 

收集器接口 Collector
collect方法参数类型是Collector接口,
< RAcollect(Collector<?  super  TAR> collector);
也就是说所有的收集器都是Collector接口的实现类型。所以我们如果想要开发自己的收集器就需要实现这个接口。
那么就先来看看这个接口有哪些方法。


Supplier< A> supplier();
这个方法返回一个函数,这个函数用来创建并返回一个可变的容器用来存放收集结果
 
BiConsumer< AT> accumulator();
这个函数是累积器 ,它将元素添加到 supplier 接口创建的结果容器

BinaryOperator< A> combiner();
这个方法返回一个函数,这个函数接收两个部分结果集,然后将这两个部分结果集合并成一个结果集。主要在并发计算的时候会用到子集合合并的操作。

Function< AR> finisher();
执行最终的转换,将中间计算累积的结果转换成最终的结果

Set<Characteristics> characteristics();
这个方法返回一个枚举类型 Characteristics 的集合,他定义了收集器的行为。

enum Characteristics {
    CONCURRENT,
      accumulator可以被多个线程同时调用,也就是可以并行执行。但是如果数据源是有序的,那么必须同时标注 UNORDERED才可以并行执行。

    UNORDERED,
     归约结果不受流中的元素的遍历和累积顺序影响。

    IDENTITY_FINISH
    这个表明accumulator累积器计算的中间结果类型和最终类型是一致的,finisher完成器中的A和R是相同的类型  ,不需要转换。 
}



开发自己的收集器
实现collector接口,提供对应的实现方法就好了。






  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值