并行分片归集必须用 pecs 是因为 collections.copy(list

直接用符合 PECS 原则的泛型拷贝函数,能安全、高效地把不同子类数据批量写入更宽泛类型的集合中,特别适合在并行分片处理后统一归集结果的场景。
为什么并行分片归集必须用 PECS
海量业务数据常被拆成多个子任务并行处理(如按用户 ID 分片),每个线程产出的是具体子类型数据(如 OrderDetail),但最终要汇总进一个统一容器(如 List<businessevent></businessevent> 或 List<object></object>)。若不用 PECS:
- 硬写
List<businessevent> dest, List<orderdetail> src</orderdetail></businessevent>→ 编译失败,泛型不协变 - 退而求其次用
List<object></object>接收 → 失去类型约束,后续处理需反复 instanceof + 强转 - 手动 new 中间 list 再遍历转换 → 增加 GC 压力,违背“零拷贝”优化目标
标准 copy 方法签名就是为并行归集设计的
Collections.copy(List super T> dest, List extends T> src) 这个签名天然适配分片-归集流程:
-
src是生产者:每个分片线程产出List extends BusinessEvent>(比如List<paymentevent></paymentevent>、List<refundevent></refundevent>) -
dest是消费者:主控线程提供List super BusinessEvent>(如CopyOnWriteArrayList<businessevent></businessevent>,线程安全且支持并发 add) - 编译器自动校验:只要
PaymentEvent是BusinessEvent的子类,调用就合法,无需运行时检查
实战:并行分片 + PECS 归集的典型代码结构
以电商订单事件分片处理为例:
- 定义统一基类:
abstract class BusinessEvent { long timestamp; } - 子类分片处理:
List<paymentevent> payments = processPayments(shard);</paymentevent> - 线程安全归集容器:
List<businessevent> allEvents = new CopyOnWriteArrayList();</businessevent> - 单行完成归集:
Collections.copy(allEvents, payments);(或自定义泛型方法) - 多分片可并行执行:
executor.invokeAll(tasks),每个 task 内部调用 copy,互不干扰
比原始类型或强制转换更可靠
有人倾向用 List<object></object> 当中转容器,但这样会丢失语义:
-
allEvents.get(0)只能当Object用,后续仍要(BusinessEvent)强转 - 若误塞入非事件对象(如
new Date()),编译期无法拦截 - 而
List super BusinessEvent>明确承诺:“我只收 BusinessEvent 及其子类”,编译器全程把关











