实现一个自定义的Collector!

背景

当前有多个用户,产品提出一个需求,根据userStatus分组,然后将每个分组中的用户按照age进行累加得到不同userStatus组下的age总和, 以map<userStatus, age总和>类型返回,而且要求使用stream.collect(Collector.groupingBy())方法一行写完;

解决思路

Collector.groupingBy()方法,默认可以轻松完成将用户按status分组,生成<Key, List<Val>>的map。但是,需求需要我们额外实现对于List中对象的age属性进行累加的操作,此时就需要我们自定义一个collector来完成该操作并传入Collector.groupingBy()方法中~

实操

先上代码:

@Data  
@Builder  
@NoArgsConstructor  
@AllArgsConstructor  
public class User implements Serializable {  
    /**  
     *     */  
    @TableId(type = IdType.AUTO)  
    private Long id;  
  
    /**  
     * 年龄  
     */  
    private Integer age;  
  
  
    /**  
     * 用户状态 0-正常  1-异常
     */  
    private Integer userStatus;
@Test  
void userCombine() {  
    User user1 = new User();  
    user1.setUserName("user1");  
    user1.setAge(10);  
    user1.setUserStatus(1);  
    User user2 = new User();  
    user2.setUserName("user2");  
    user2.setAge(3);  
    user2.setUserStatus(1);  
  
    User user3 = new User();  
    user3.setUserName("user3");  
    user3.setAge(13);  
    user3.setUserStatus(6);  
  
    List<User> users = Arrays.asList(user1, user2, user3);  
  
    // 根据userStatus分组,然后将每个分组中的用户按照age进行累加得到不同userStatus组下的age总和 以map类型返回,使用stream.collect(Collector.groupBy())方法  
    Map<Integer, Integer> collect = users.stream().collect(Collectors.groupingBy(User::getUserStatus,  
            Collector.of(() -> new int[1],  // 注意此处不能直接返回0,其并不是可变容器
                    (sum, user) -> sum[0] += user.getAge(),  // 自定义acculturator
                    (sum1, sum2) -> { sum1[0] += sum2[0];  // 自定义combiner
                        return sum1; }, 
                    sum -> sum[0])));  // 自定义finisher  
    System.out.println(collect);  
}

collector是什么?

首先,collector是什么?

在 Java Stream API 中,Collector 接口用于收集流中的元素,并将其转换为另一种形式,比如转换成集合、计算汇总统计信息等。

我们来看看Collector的构造函数,Collector.of()

  
/**  
 * Returns a new {@code Collector} described by the given {@code supplier},  
 * {@code accumulator}, {@code combiner}, and {@code finisher} functions.  
 * * @param supplier The supplier function for the new collector  
 * @param accumulator The accumulator function for the new collector  
 * @param combiner The combiner function for the new collector  
 * @param finisher The finisher function for the new collector  
 * @param characteristics The collector characteristics for the new  
 *                        collector * @param <T> The type of input elements for the new collector  
 * @param <A> The intermediate accumulation type of the new collector  
 * @param <R> The final result type of the new collector  
 * @throws NullPointerException if any argument is null  
 * @return the new {@code Collector}  
 */public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,  
                                             BiConsumer<A, T> accumulator,  
                                             BinaryOperator<A> combiner,  
                                             Function<A, R> finisher,  
                                             Characteristics... characteristics) {
                                            
......

}

可以看到Collector 接口有三个重要的方法:supplier、accumulator 和 combiner,它们分别对应着收集器的不同阶段。在实现collector的过程中,这三个函数都需要我们自己实现,下面详细分析各个函数~

supplier函数是什么?

Supplier 是 Java 8 中引入的一个函数式接口,它位于 java.util.function 包中。Supplier 接口只有一个抽象方法 get(),该方法不接受任何参数,并返回一个类型为 T 的结果。Supplier 主要用于生成或提供一个对象实例,通常用于延迟初始化或创建对象。

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

示例
假设你需要创建一个 User 对象,你可以使用 Supplier 如下所示:

Supplier<User> userSupplier = () -> new User("John Doe", 30);
User user = userSupplier.get(); // 创建并返回一个User对象

通俗的来说,他就是java定义的一个不需要输入,只管生成的方法~

**在这次的需求中,就是生成一个只包含一个元素的可变容器 new int[1],用来储存每次的累加和

supplier 是一个 Supplier 函数,它负责提供一个初始的可变结果容器。这个容器用于存储流中的元素或计算的结果。

为什么需要:在收集过程开始之前,需要有一个初始的容器来存储结果。supplier 方法提供了这样一个容器的实例。例如,如果你想要收集元素到一个列表中,supplier 可能会返回一个空列表。

BiConsumer函数是什么?

BiConsumer 是 Java 8 中引入的一个函数式接口,它位于 java.util.function 包中。BiConsumer 接口代表了一个接受两个输入参数且不返回任何内容的操作。它主要用于消费两个输入参数而不产生任何结果。

@FunctionalInterface
public interface BiConsumer<T, U> {
    void accept(T t, U u);
    
    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> { accept(t, u); after.accept(t, u); };
    }
}

BiConsumer 接口的主要方法
accept(T t, U u):这是一个功能性方法,它接受两个参数 t 和 u,并执行某些操作。此方法不返回任何值。

在collecor中该方法充当accumulator的角色,它负责将流中的元素累积到一个可变的结果容器中(supplier函数生成的容器)。每当流中的一个元素被处理时,accumulator 就会被调用来更新结果容器。

在这次的需求中,定义的函数为(sum, user) -> sum[0] += user.getAge(),就是将一个个新进来的元素累加到sum中。

例如在将一个个user放到List中时,此时T就是list 和 U 就是user,而accumulator就需要定义user是如何放入list中,是否需要什么额外的操作,对应常见List的add操作~

BinaryOperator函数是什么?

BinaryOperator 是 Java 8 中引入的一个函数式接口,它位于 java.util.function 包中。BinaryOperator 接口代表了一个接受两个同类型的输入参数,并返回相同类型的单个结果的操作。它通常用于处理两个输入参数,并返回一个与输入参数类型相同的输出。

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    T apply(T t, T u);
    
    default BinaryOperator<T> andThen(BinaryOperator<? super T> after) {
        Objects.requireNonNull(after);
        return (T t, T u) -> after.apply(apply(t, u), u);
    }
    
    static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }
    
    static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

BinaryOperator 接口的主要方法就是 apply(T t, T u):这是一个功能性方法,它接受两个同类型的参数 t 和 u,并返回一个同类型的值。

在collecor中该方法充当combiner的角色,它负责将分开的两个流进行合并。例如当数据量很大时,流可能会被并行处理,这意味着流会被分成多个部分,每个部分都独立地进行处理。combiner 被用来合并这些部分的结果。

在这次的需求中,定义的函数为(sum1, sum2) -> { sum1[0] += sum2[0]; return sum1; },就是将两个sum进行处理,并返回处理后的sum。

Function 函数是什么?

Function 同样是 Java 8 中引入的一个函数式接口,同样位于 java.util.function 包中。Function 接口有一个抽象方法 apply(),该方法接受一个类型为 T 的参数,并返回一个类型为 R 的结果。Function 主要用于将一个输入转换为另一个输出。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

示例
假设你需要将一个字符串转换为大写,你可以使用 Function 如下所示:

Function<String, String> toUpperCaseFunction = String::toUpperCase;
String upperCaseString = toUpperCaseFunction.apply("hello world"); // "HELLO WORLD"

通俗的来说,他就是用于将一个输入转换为另一个输出,接受一个参数,并返回一个结果,类似于stream.map()~

在collecor中该方法充当finisher的角色,它负责将中间结果转换成最终我们需要的对象~

在这次的需求中,定义的函数为sum -> sum[0],我们预期是获取累加的值,是Integer类型,而不是可变容器int[1], 所以返回数组的第一位即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值