Java8之用流收集数据
第六章 用流收集数据
1、 toList()
- 用法
List<Transaction> transactions =
transactionStream.collect(Collectors.toList());
2、归约和汇总
1)count/counting
long howManyDishes = menu.stream().collect(Collectors.counting());
//这还可以写得更为直接:
long howManyDishes = menu.stream().count();
2)找流中的最大值和最小值 --maxBy
- Collectors.maxBy
Comparator<Dish> dishCaloriesComparator =
Comparator.comparingInt(Dish::getCalories);
Optional<Dish> mostCalorieDish =
menu.stream()
.collect(maxBy(dishCaloriesComparator));
3)汇总/平均数Collectors.summingInt等
- Collectors.summingInt 求出菜单列表的总热量:
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
Collectors.summingLong和Collectors.summingDouble方法的作用完全一样,可以用 于求和字段为long或double的情况。
- Collectors.averagingInt,连同对应的averagingLong和averagingDouble可以计算数值的平均数
double avgCalories =
menu.stream().collect(averagingInt(Dish::getCalories));
- 用summarizingInt工厂 方法返回的收集器 , 通过一次summarizing操作你可以就数出菜单中元素的个数,并得 到菜肴热量总和、平均值、最大值和最小值:
IntSummaryStatistics menuStatistics =
menu.stream().collect(summarizingInt(Dish::getCalories));
- 结果
IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800}
4) 连接字符串 -- joining
String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));
- 结果
pork, beef, chicken, french fries, rice, season fruit, pizza, prawns, salmon
3、分组-- Collectors.groupingBy
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]}
- 复杂一点的
public enum CaloricLevel { DIET, NORMAL, FAT }
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel = menu.stream().collect(
Collectors.groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
} ));
1)多级分组
- 要进 行二级分组的话,我们可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流 中项目分类的二级标准
Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel =
menu.stream().collect(
Collectors.groupingBy(Dish::getType,
Collectors.groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
} )
)
);
- 结果
{MEAT={DIET=[chicken], NORMAL=[beef], FAT=[pork]}, FISH={DIET=[prawns], NORMAL=[salmon]}, OTHER={DIET=[rice, seasonal fruit], NORMAL=[french fries, pizza]}}
一般来说,把groupingBy看作“桶”比较容易明白。第一个groupingBy给每个键建立了 一个桶。然后再用下游的收集器去收集每个桶中的元素,以此得到n级分组。
4、 分区
分区是分组的特殊情况:由一个谓词(返回一个布尔值的函数)作为分类函数,它称分区函 数。分区函数返回一个布尔值,这意味着得到的分组Map的键类型是Boolean,于是它最多可以 分为两组——true是一组,false是一组。
Map<Boolean, List<Dish>> partitionedMenu =
menu.stream().collect(partitioningBy(Dish::isVegetarian));
List<Dish> vegetarianDishes =
menu.stream().filter(Dish::isVegetarian).collect(toList());
{false=[pork, beef, chicken, prawns, salmon], true=[french fries, rice, season fruit, pizza]}
1) 分区的优势
- partitioningBy 工厂方法有一个重载版本,可以像下面这样传递第二个收集器:
Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishesByType =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
groupingBy(Dish::getType)));
{false={FISH=[prawns, salmon], MEAT=[pork, beef, chicken]}, true={OTHER=[french fries, rice, season fruit, pizza]}}
- 找到素食和非素 食中热量最高的菜:
Map<Boolean, Dish> mostCaloricPartitionedByVegetarian =
menu.stream().collect(
partitioningBy(Dish::isVegetarian,
collectingAndThen(
maxBy(comparingInt(Dish::getCalories)),
Optional::get)));
{false=pork, true=pizza}
*** Collectors类静态工厂方法的表格 ***
5、收集器接口
1) 自己的ToListCollector方法
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.*;
public class ToListCollector<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 Function<List<T>, List<T>> finisher() {
return Function.indentity();
}
@Override
public BinaryOperator<List<T>> combiner() {
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(
IDENTITY_FINISH, CONCURRENT));
}
}