Skip to content

执行器工具

框架在 host.springboot.framework3.core.execute 包下提供了两个实用工具类:

  • MapExecutor:专为复杂嵌套 Map 设计,简化多层级数据结构的操作
  • ObjectExecutor:条件执行器,减少业务代码中的 null 判断和条件分支

本文档侧重介绍 MapExecutor 的应用场景与最佳实践。

MapExecutor - 嵌套 Map 操作利器

在实际开发中,经常会遇到复杂的嵌套 Map 数据结构,例如:

  • Map<String, List<User>>:按部门分组的用户列表
  • Map<String, Map<String, Integer>>:多维度统计数据
  • Map<Long, Map<String, List<Order>>>:用户订单分组

传统写法需要大量 null 判断和临时变量,而 MapExecutor 提供了一套优雅的 API 来简化这些操作。

核心方法速览

方法功能适用数据结构
dynamicPut向嵌套集合/Map 添加单个元素Map<K, Collection> / Map<K, Map>
dynamicPutAll向嵌套集合批量添加元素Map<K, Collection> / Map<K, Map<K, Collection>>
dynamicGet从嵌套 Map 中获取值Map<K, Map<K, V>>
dynamicIncrement递增嵌套 Map 中的计数器Map<K, Integer> / Map<K, Map<K, Integer>>
dynamicDecrement递减嵌套 Map 中的计数器Map<K, Integer> / Map<K, Map<K, Integer>>
dynamicOffset对嵌套 Map 中的数值进行偏移Map<K, Integer> / Map<K, Map<K, Integer>>
dynamicExecute自定义条件执行逻辑任意 Map<K, V>

使用场景与示例

场景1:按分组收集数据 - Map<K, List<V>>

业务需求:将用户列表按部门分组

❌ 传统写法

java
Map<String, List<User>> usersByDept = new HashMap<>();

for (User user : users) {
    String dept = user.getDepartment();
    
    // 每次都要判断 null 并手动创建 List
    List<User> list = usersByDept.get(dept);
    if (list == null) {
        list = new ArrayList<>();
        usersByDept.put(dept, list);
    }
    list.add(user);
}

✅ 使用 MapExecutor

java
Map<String, List<User>> usersByDept = new HashMap<>();

for (User user : users) {
    MapExecutor.dynamicPut(
        usersByDept,                  // 目标 Map
        user.getDepartment(),         // 外层 key
        user,                         // 要添加的元素
        ArrayList::new                // 当 List 不存在时创建新的
    );
}

优势

  • 🚀 一行代码完成判断 + 创建 + 添加
  • 🧹 消除重复的 null 检查逻辑
  • 📖 代码意图更清晰

场景2:多维度统计 - Map<K, Map<K, Integer>>

业务需求:统计每个部门各个岗位的人数

❌ 传统写法

java
Map<String, Map<String, Integer>> deptJobCount = new HashMap<>();

for (User user : users) {
    String dept = user.getDepartment();
    String job = user.getJob();
    
    Map<String, Integer> jobCountMap = deptJobCount.get(dept);
    if (jobCountMap == null) {
        jobCountMap = new HashMap<>();
        deptJobCount.put(dept, jobCountMap);
    }
    
    Integer count = jobCountMap.get(job);
    if (count == null) {
        count = 0;
    }
    jobCountMap.put(job, count + 1);
}

✅ 使用 MapExecutor

java
Map<String, Map<String, Integer>> deptJobCount = new HashMap<>();

for (User user : users) {
    MapExecutor.dynamicIncrement(
        deptJobCount,                 // 目标 Map
        user.getDepartment(),         // 外层 key(部门)
        user.getJob(),                // 内层 key(岗位)
        HashMap::new                  // 当内层 Map 不存在时创建
    );
}

优势

  • 🎯 自动处理两层嵌套的 null 初始化
  • 📊 递增逻辑封装在方法内部,无需手动 +1

场景3:三层嵌套 - Map<K, Map<K, List<V>>>

业务需求:按用户 ID 和订单状态分组订单

✅ 使用 MapExecutor

java
Map<Long, Map<String, List<Order>>> orderMap = new HashMap<>();

for (Order order : orders) {
    MapExecutor.dynamicPut(
        orderMap,                     // 目标 Map
        order.getUserId(),            // 外层 key(用户ID)
        order.getStatus(),            // 内层 key(订单状态)
        order,                        // 要添加的订单
        HashMap::new,                 // 内层 Map 不存在时创建
        ArrayList::new                // List 不存在时创建
    );
}

输出数据结构

json
{
  "1001": {
    "PENDING": [Order(...), Order(...)],
    "COMPLETED": [Order(...)]
  },
  "1002": {
    "PENDING": [Order(...)],
    "CANCELLED": [Order(...)]
  }
}

场景4:批量添加 - dynamicPutAll

业务需求:将多个订单批量添加到用户订单分组中

java
Map<Long, List<Order>> userOrders = new HashMap<>();
List<Order> newOrders = Arrays.asList(order1, order2, order3);

MapExecutor.dynamicPutAll(
    userOrders,                       // 目标 Map
    userId,                           // 用户ID
    newOrders,                        // 要批量添加的订单列表
    ArrayList::new                    // 当 List 不存在时创建
);

场景5:递减与自定义偏移

递减计数器

java
Map<String, Integer> inventory = new HashMap<>();
inventory.put("iPhone", 100);

// 每次销售递减库存
MapExecutor.dynamicDecrement(inventory, "iPhone");  
// inventory.get("iPhone") -> 99

自定义偏移量

java
// 批量销售 10 台
MapExecutor.dynamicOffset(inventory, "iPhone", -10);  
// inventory.get("iPhone") -> 89

// 补货 50 台
MapExecutor.dynamicOffset(inventory, "iPhone", 50);   
// inventory.get("iPhone") -> 139

场景6:条件执行 - dynamicExecute

业务需求:只有当值满足条件时才执行自定义逻辑

java
Map<String, Integer> scores = new HashMap<>();

// 只有分数 >= 60 时才记录
MapExecutor.dynamicExecute(
    scores,                           // 目标 Map
    "math",                           // key
    85,                               // 新分数
    score -> score >= 60,             // 条件:及格才记录
    (newScore, oldScore) -> {         // 自定义逻辑:取最高分
        return oldScore == null ? newScore : Math.max(newScore, oldScore);
    },
    () -> 0                           // 初始值为 0
);

ObjectExecutor - 条件执行简化器

ObjectExecutor 提供了一组条件执行方法,减少业务代码中的 if 判断。

核心方法

方法功能适用场景
execute条件为 true 时执行布尔条件判断
notNullExecute对象非 null 时执行null 安全检查
notBlankExecute字符串非空时执行字符串校验
notEmptyExecute集合非空时执行集合校验

使用示例

传统 if 判断

java
if (user != null) {
    log.info("用户名: {}", user.getName());
}

if (StringUtils.isNotBlank(remark)) {
    order.setRemark(remark);
}

if (CollectionUtils.isNotEmpty(tags)) {
    product.setTags(tags);
}

使用 ObjectExecutor

java
ObjectExecutor.notNullExecute(user, flag -> 
    log.info("用户名: {}", user.getName())
);

ObjectExecutor.notBlankExecute(remark, flag -> 
    order.setRemark(remark)
);

ObjectExecutor.notEmptyExecute(tags, flag -> 
    product.setTags(tags)
);

最佳实践

✅ 推荐做法

  1. 使用 MapExecutor 处理嵌套 Map

    • 代替繁琐的 null 判断和手动初始化
    • 提高代码可读性和维护性
  2. 使用方法引用传递构造函数

    java
    ArrayList::new, HashMap::new, HashSet::new
  3. 统计类场景优先使用 dynamicIncrement/Decrement

    • 自动处理 null 初始化为 0
  4. 批量操作使用 dynamicPutAll

    • 减少循环次数

❌ 不推荐做法

  • ❌ 过度使用导致代码逻辑不清晰
  • ❌ 在简单场景使用,反而增加理解成本
  • ❌ 忽略类型安全,使用 Map<Object, Object> 这种宽泛定义

常见问题 FAQ

Q: MapExecutor 是线程安全的吗?

A: MapExecutor 本身是无状态的工具类,线程安全。但它操作的 Map 对象的线程安全性取决于你传入的具体实现(如 HashMap 非线程安全,ConcurrentHashMap 线程安全)。

Q: 什么时候使用 MapExecutor,什么时候用 Stream API?

A:

  • MapExecutor:适合需要"边遍历边分组/统计"的场景,实时更新数据结构
  • Stream API:适合"先处理后收集"的场景,例如 Collectors.groupingBy()

Q: ObjectExecutor 与传统 if 判断有什么区别?

A:

  • ObjectExecutor 更适合链式调用、函数式编程风格
  • 传统 if 更直观,适合复杂条件分支
  • 选择取决于团队编码风格与场景复杂度

Q: 为什么需要传递 Supplier 来创建集合?

A: 因为 MapExecutor 不知道你想创建 ArrayListLinkedList 还是 HashSet。通过 Supplier,你可以灵活指定具体实现类型,保持类型安全。

Released under the Apache-2.0 License