Java Stream 小练习

153 阅读2分钟

我正在参加「掘金·启航计划」

基础练习

求一个列表比如 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);