我正在参加「掘金·启航计划」
基础练习
求一个列表比如 var list = List.of(1, 2, 9, 8, 6, 6, 5, 3, 7) 的奇数和与偶数和 ?
版本一 filter + reduce:
var list = List.of(1, 2, 9, 8, 6, 6, 5, 3, 7);
Integer oddTotal = list.stream().filter(item -> (item & 1) == 1).reduce(0, Integer::sum);
Integer evenTotal = list.stream().filter(item -> (item & 1) == 0).reduce(0, Integer::sum);
版本二 partitioningBy + suming (最优解) :
var list = List.of(1, 2, 9, 8, 6, 6, 5, 3, 7);
Map<Boolean, Integer> collect = list.stream().collect(
Collectors.partitioningBy(x -> x % 2 == 0, Collectors.summingInt(Integer::intValue)));
进阶练习
var list = List.of(1, 2, 9, 8, 6, 6, 5, 3, 7, 9, 11, 20, 17, 6, 33, 25, 8, 0, 2, 5, 10) 按 %5 的值分组求和。
版本一 groupingBy (最优解) :
var list = List.of(1, 2, 9, 8, 6, 6, 5, 3, 7, 9, 11, 20, 17, 6, 33, 25, 8, 0, 2);
list.stream().collect(
Collectors.groupingBy(
x -> x % 5,
Collectors.summingInt(Integer::intValue)
)
);
版本二 groupingBy + pair:
class Pair {
private final Integer first;
private final Integer second;
Pair(Integer first, Integer second) {
this.first = first;
this.second = second;
}
public Integer getFirst() {
return this.first;
}
public Integer getSecond() {
return this.second;
}
}
var list = List.of(1, 2, 9, 8, 6, 6, 5, 3, 7, 9, 11, 20, 17, 6, 33, 25, 8, 0, 2);
list.stream().map(x -> new Pair(x % 5, x)).collect(
Collectors.groupingBy(
Pair::getFirst,
Collectors.summingInt(Pair::getSecond)
)
);
高阶练习
先构造 [1, 4, 7, 10, …… 103],以它作为输入。
然后将其按 5 个砍成一段,最后一段可以不足 5 个,生成 [[1, 4, 7, 10, 13], [16, 19, 22, 25, 28], .....[……103]]
List<List<Integer> chunk(List<Integer> list) {
}
版本一 skip + limit:
// 构建数组
var list = Stream.iterate(0, n -> n + 1).limit(104).
filter(item -> item % 3 == 1).collect(Collectors.toList());
public static List<List<Integer>> chunk(List<Integer> list) {
var chunkSize = 5;
int limit = (list.size() + chunkSize - 1) / chunkSize;
return Stream.iterate(0, x -> x < limit, x -> x+ 1).map(item ->list.stream().skip((long) item * chunkSize).limit(chunkSize).collect(Collectors.toUnmodifiableList())).collect(Collectors.toUnmodifiableList());
}
版本二 优化:
// Java9 iterate 新的实现
var list = Stream.iterate(1, x -> x < 104, x -> x+ 3).collect(Collectors.toUnmodifiableList());
public static List<List<Integer>> chunk(List<Integer> list) {
var chunkSize = 5;
// 利用数组本身的长度做分片
return Stream.iterate(0, x -> x < list.size(), x -> x + chunkSize).
map( pos -> list.stream().skip(pos).limit(chunkSize).collect(Collectors.toUnmodifiableList())
).collect(Collectors.toUnmodifiableList());
}
版本三 泛化:
public static <T> List<List<T>> chunk(List<T> list, int chunkSize) {
return Stream.iterate(0, x -> x < list.size(), x -> x + chunkSize).
map( pos -> list.stream().skip(pos).limit(chunkSize).collect(Collectors.toUnmodifiableList())
).collect(Collectors.toUnmodifiableList());
}
// 生成 26 个英文字母
var list = Stream.iterate(1, x -> x < 27, x -> x+ 1).map(i -> (char)(96+i)).collect(Collectors.toUnmodifiableList());
chunk(list, 4)
其它
对于 Java 11,不推荐用 toList 而都要用 toUnmodifiableList。
对于 Java 17,不推荐 stream.collect (toUnmodifiableList) 而是直接 stream.toList(得到的是不可变列表)
PS: 下图为 toList 与 toUnmodifiableList 的对比。
函数式接口的实现 Java8 特性
// 定义函数接口
@FunctionalInterface
public static interface Func {
abstract Integer remain(Integer num);
}
// 定义方法
public static Integer remain(Func func, Integer num) {
return func.remain(num);
}
// 两种使用方式 实现取余函数
// 接口方式实现函数式
var r1 = remain(num -> num % 5, 8);
// 重写方法使用
var r2 = remain(new Func(){
@Override
public Integer remain(Integer num){
return num % 5;
}}, 8);