Stream 中的 Reduce 方法:根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
。
函数式接口
Java 8 的库设计师在 java.util.function 包中引入了几个新的函数式接口.Predicate, Consumer 和 Function
java.util.function.Predicate
接口定义了一个名叫 test() 的抽象方法, 它接受泛型 T 对象, 并返回一个 boolean .
boolean test(T t)
T -> boolean
java.util.function.Consumer
定义了一个名叫 accept() 的抽象方法,它接受泛型T 的对象,没有返回(void )。
你如果需要访问类型 T 的对象,并对其执行某些操作,就可以使用这个接口.
T -> void
Function 接口 java.util.function.Function<T, R>
接口定义了一个叫作 apply 的方法,它接受一个泛型 T 的对象,并返回一个泛型 R 的对象。
如果你需要定义一个 Lambda,将输入对象的信息映射到输出,就可以使用这个接口.
T -> R
Supplier
// 构造函数引用指向默认的Apple()构造函数
Supplier<Apple> c1 = Apple::new;
// 调用Supplier的get方法将产生一个新的Apple对象
Apple a1 = c1.get();
排序
// 多个字段排序
Comparator<People> com
= Comparator.comparing(People::getUpdateDate, Comparator.reverseOrder())
// 枚举值 默认是按照 java.lang.Enum.ordinal()方法 排序的.即在枚举类中声明的顺序排序
.thenComparing(People::getMyEnum, Comparator.reverseOrder())
.thenComparing(People::getId, Comparator.reverseOrder());
list.sort(com);
方法引用
1.Lambda表达式调用静态方法
2.(a, b) -> a.xxx(b)
3.构造器引用
// 方法引用
// 1.Lambda表达式调用静态方法
Function<String, Integer> stringToInteger = (String s) -> Integer.parseInt(s);
// 改为 方法引用
Function<String, Integer> stringToInteger1 = Integer::parseInt;
// 2.Lambda使用其第一个参数,调用其contains 方法。由于第一个参数是List 类型的
// (a, b) -> a.xxx(b)
BiPredicate<List<String>, String> contains = (list, element) -> list.contains(element);
// 改为 方法引用
BiPredicate<List<String>, String> contains1 = List::contains;
// 3.构造器引用
Supplier<Apple> c0 = () -> new Apple(); // ←─利用默认构造函数创建Apple的Lambda表达式
Apple a0 = c0.get(); // ←─调用Supplier的get方法将产生一个新的Apple
// === 改写后
// 构造函数引用指向默认的Apple()构造函数
Supplier<Apple> c1 = Apple::new;
Apple a1 = c1.get(); // 调用Supplier的get方法将产生一个新的Apple对象
集合
1.移除元素
list.removeIf(person -> {
return person.getName() == null;
});
list.stream() // 从menu获得流(菜肴列表)
.filter(d -> d.getCalories() < 400) // 建立操作流水线:首先选出低热量的菜肴
.sorted(comparing(Dish::getCalories)) // 按照热量排序
.map(Dish::getName) // 获取 Dish.菜名
.collect(toList()); // 将结果保存在另一个 List 中
创建流的方式
// Stream.of
// 1.由值创建流
// 使用静态方法Stream.of ,通过显式值创建一个流。它可以接受任意数量的参数。
Stream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
// Stream.empty
// 使用empty 得到一个空流
Stream<String> emptyStream = Stream.empty();
// Arrays.stream
// 2.由数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
System.out.println(Arrays.stream(numbers).sum());
// 3.由文件创建流
long uniqueWords = 0;
// 你可以使用Files.lines 得到一个流,其中的每个元素都是给定文件中的一行
try (Stream<String> lines =
Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) { // ←─流会自动关闭
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))) // ←─生成单词流
.distinct() // ←─删除重复项
.count(); // ←─数一数有多少各不相同的单词
} catch (IOException e) {
// ←─如果打开文件时出现异常则加以处理
}
// 4.由函数生成流:创建无限流
// Stream API提供了两个静态方法来从函数生成流:Stream.iterate 和Stream.generate 。
// 这两个操作可以创建所谓的无限流 :不像从固定集合创建的流那样有固定大小的流。
// 由iterate 和generate 产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!
// 一般来说,应该使用limit(n) 来对这种流加以限制,以避免打印无穷多个值。
// Stream.iterate
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
.filter() 过滤出
查找和匹配
anyMatch() 方法可以回答“流中是否有一个元素能匹配给定的谓词”
allMatch() 所有都匹配
noneMatch() 没有一个匹配
findAny() 方法将返回当前流中的任意元素,返回 Optional
map() 和 flatMap()
Reducing
// 求和,方式2
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 在Java 8中,Integer 类现在有了一个静态的 sum 方法来对两个数求和
int sum2 = numbers.stream().reduce(0, Integer::sum);
// 相乘
int product = numbers.stream().reduce(1, (a, b) -> a * b);
// 最大值
int max2 = numbers.stream().reduce(0, Integer::max);
// 最小值
// 不传初值,返回 Optional
Optional<Integer> min = numbers.stream().reduce(Integer::min);
min.ifPresent(System.out::println);
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum);
Grouping 分组
grouping 分组 返回的一定是 key -> List 结构.
与 map() 区别, map 返回 key -> value 结构.
menu.stream().collect(groupingBy(Dish::getType))
// mapping() 方法接受两个参数:一个函数对流中的元素做变换,另一个则将变换的结果对象收集起来
menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));
// 自定义分组逻辑
menu.stream().collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) {
return CaloricLevel.DIET;
} else if (dish.getCalories() <= 700) {
return CaloricLevel.NORMAL;
} else {
return CaloricLevel.FAT;
}
}));
// TODO 待补充剩余场景
Partitioning 分区
分区是分组的特殊情况:由一个谓词(返回一个布尔值的函数)作为分类函数,它称分区函数。
分区函数返回一个布尔值,这意味着得到的分组Map 的键类型是Boolean ,于是它最多可以分为两组——true 是一组,false 是一组。
menu.stream().collect(partitioningBy(Dish::isVegetarian))
Reducing
// 可以用 reducing() 方法创建的收集器来计算你菜单的总热量
// reducing() 需要三个参数。
// 第一个参数: 初始值. 是归约操作的起始值,也是流中没有元素时的返回值,所以很显然对于数值和而言0 是一个合适的值。
// 第二个参数: 转换函数,将菜肴转换成一个表示其所含热量的int 。
// 第三个参数: 是一个 BinaryOperator ,将两个项目累积成一个同类型的值。这里它就是对两个int 求和。
return menu.stream().collect(reducing(0, Dish::getCalories, (Integer i, Integer j) -> i + j));
// 可以被替换成 map().reduce()形式
Summarizing 统计
TODO