From 42b3f26e73d75f480a25b0ccd65af9be0178ec8b Mon Sep 17 00:00:00 2001 From: yuejiajun <1530620364@qq.com> Date: Sun, 28 Sep 2025 14:47:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AD=A3=E5=88=99=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E3=80=81map=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/draft/demo043/util/FormatUtil.java | 496 ++++++++++++++++-- .../java/com/example/demo/FormatUtilTest.java | 127 ++++- 2 files changed, 567 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java b/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java index cc8b2a6..a148c51 100644 --- a/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java +++ b/src/main/java/com/example/demo/draft/demo043/util/FormatUtil.java @@ -148,7 +148,7 @@ public class FormatUtil { } } - return result; + return result.trim(); } /** @@ -183,29 +183,393 @@ public class FormatUtil { } /** - * 正则表达式替换 - * - *

使用正则表达式进行字符串替换。

- * - * @param str 原始字符串 - * @param params 参数映射,包含: - * - pattern: 正则表达式模式 - * - replacement: 替换字符串 - * @return 替换后的字符串 + * 正则表达式替换工具方法 + * + *

该方法提供基于配置参数的正则表达式替换功能,支持从参数映射中动态获取 + * 正则模式和替换内容。适用于规则引擎、模板处理等动态配置场景。

+ * + *

功能特性:

+ * + * + *

参数映射要求:

+ * + * + * @param str 要进行替换的源字符串,如果为null则直接返回null + * @param params 参数映射表,包含正则表达式配置信息 + * - key "pattern": 必需,正则表达式模式 + * - key "replacement": 可选,替换内容,默认为空字符串 + * - key "flags": 可选,正则表达式标志,如"CASE_INSENSITIVE" + * @return 替换后的字符串,如果参数无效或发生错误则返回原字符串 + * @throws IllegalArgumentException 当params为null或缺少必需参数时抛出 */ public static String regexReplace(String str, Map params) { - if (str == null || !params.containsKey("pattern")) return str; - - String pattern = params.get("pattern").toString(); - String replacement = params.containsKey("replacement") ? - params.get("replacement").toString() : ""; - + // 定义参数键名常量 + final String PATTERN_KEY = "pattern"; + final String REPLACEMENT_KEY = "replacement"; + final String FLAGS_KEY = "flags"; + + // 参数校验 + if (str == null) { + log.debug("源字符串为null,直接返回null"); + return null; + } + + if (params == null) { + log.warn("参数映射表为null,返回原字符串"); + return str; + } + + if (!params.containsKey(PATTERN_KEY)) { + log.warn("参数映射表中缺少必需的'pattern'键,返回原字符串"); + return str; + } + + // 获取正则表达式模式 + Object patternObj = params.get(PATTERN_KEY); + if (patternObj == null) { + log.warn("参数映射表中'pattern'键的值为null,返回原字符串"); + return str; + } + + String pattern = patternObj.toString(); + if (pattern.trim().isEmpty()) { + log.warn("正则表达式模式为空字符串,返回原字符串"); + return str; + } + + // 获取替换内容(可选参数) + String replacement = ""; + if (params.containsKey(REPLACEMENT_KEY)) { + Object replacementObj = params.get(REPLACEMENT_KEY); + if (replacementObj != null) { + replacement = replacementObj.toString(); + } + } + + log.debug("开始正则表达式替换 - 模式: '{}', 替换内容: '{}', 源字符串长度: {}", + pattern, replacement, str.length()); + try { - Pattern p = Pattern.compile(pattern); - Matcher m = p.matcher(str); - return m.replaceAll(replacement); + // 编译正则表达式(支持标志位) + Pattern compiledPattern = compilePatternWithFlags(pattern, params); + Matcher matcher = compiledPattern.matcher(str); + + // 执行替换 + String result; + if (containsGroupReferences(replacement)) { + // 如果替换字符串包含分组引用(如$1, $2),使用更安全的处理方式 + result = replaceWithGroupHandling(matcher, replacement); + } else { + // 简单的全局替换,不包含分组引用 + result = matcher.replaceAll(replacement); + } + + // 记录替换统计信息 + boolean changed = !result.equals(str); + if (changed) { + log.info("正则表达式替换成功 - 模式: '{}', 源长度: {}, 结果长度: {}", + pattern, str.length(), result.length()); + log.debug("替换详情: '{}' -> '{}'", + str.length() > 50 ? str.substring(0, 50) + "..." : str, + result.length() > 50 ? result.substring(0, 50) + "..." : result); + } else { + log.debug("正则表达式未找到匹配 - 模式: '{}'", pattern); + } + + return result; + } catch (Exception e) { - return str; // 正则表达式错误,返回原字符串 + log.error("正则表达式替换失败 - 模式: '{}', 错误: {}", pattern, e.getMessage()); + log.debug("详细错误信息:", e); + // 发生错误时返回原字符串,保证业务连续性 + return str; + } + } + + /** + * 检查替换字符串是否包含分组引用 + */ + private static boolean containsGroupReferences(String replacement) { + if (replacement == null || replacement.isEmpty()) { + return false; + } + // 简单的检查:是否包含$后跟数字的模式 + return replacement.matches(".*\\$\\d.*"); + } + + /** + * 安全处理包含分组引用的替换 + */ + private static String replaceWithGroupHandling(Matcher matcher, String replacement) { + StringBuffer result = new StringBuffer(); + int matchCount = 0; + + // 重置匹配器到开始位置 + matcher.reset(); + + while (matcher.find()) { + matchCount++; + try { + // 对于每个匹配,手动处理分组引用 + String actualReplacement = processSingleReplacement(matcher, replacement); + matcher.appendReplacement(result, Matcher.quoteReplacement(actualReplacement)); + } catch (Exception e) { + log.warn("分组引用处理失败,使用原始替换内容: {}", e.getMessage()); + matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); + } + } + matcher.appendTail(result); + + log.debug("处理了 {} 个匹配项的分组引用", matchCount); + return result.toString(); + } + + /** + * 处理单个匹配的分组引用 + */ + private static String processSingleReplacement(Matcher matcher, String replacement) { + if (replacement == null || replacement.isEmpty()) { + return replacement; + } + + String result = replacement; + + // 处理分组引用:$1, $2, ..., $n + // 使用正则表达式匹配 $后跟数字的模式 + java.util.regex.Pattern groupPattern = java.util.regex.Pattern.compile("\\$(\\d+)"); + java.util.regex.Matcher groupMatcher = groupPattern.matcher(replacement); + + StringBuffer processed = new StringBuffer(); + while (groupMatcher.find()) { + String groupNumberStr = groupMatcher.group(1); + try { + int groupNumber = Integer.parseInt(groupNumberStr); + String groupValue = ""; + + if (groupNumber <= matcher.groupCount() && groupNumber >= 0) { + groupValue = matcher.group(groupNumber); + if (groupValue == null) { + groupValue = ""; + } + } + + groupMatcher.appendReplacement(processed, Matcher.quoteReplacement(groupValue)); + } catch (NumberFormatException e) { + // 如果无法解析分组数字,保留原始内容 + groupMatcher.appendReplacement(processed, Matcher.quoteReplacement(groupMatcher.group())); + } + } + groupMatcher.appendTail(processed); + + return processed.toString(); + } + + /** + * 编译带标志位的正则表达式模式 + */ + private static Pattern compilePatternWithFlags(String pattern, Map params) { + int flags = 0; + + // 处理正则表达式标志位 + if (params.containsKey("flags")) { + Object flagsObj = params.get("flags"); + if (flagsObj != null) { + flags = parseRegexFlags(flagsObj.toString()); + } + } + + if (flags > 0) { + log.debug("使用正则表达式标志: {}", flags); + return Pattern.compile(pattern, flags); + } else { + return Pattern.compile(pattern); + } + } + + /** + * 解析正则表达式标志字符串 + */ + private static int parseRegexFlags(String flagsStr) { + if (flagsStr == null || flagsStr.trim().isEmpty()) { + return 0; + } + + int flags = 0; + String[] flagArray = flagsStr.split(","); + + for (String flag : flagArray) { + String trimmedFlag = flag.trim().toUpperCase(); + switch (trimmedFlag) { + case "CASE_INSENSITIVE": + case "IGNORE_CASE": + flags |= Pattern.CASE_INSENSITIVE; + break; + case "MULTILINE": + case "MULTI_LINE": + flags |= Pattern.MULTILINE; + break; + case "DOTALL": + case "DOT_ALL": + flags |= Pattern.DOTALL; + break; + case "UNICODE_CASE": + case "UNICODE": + flags |= Pattern.UNICODE_CASE; + break; + case "CANON_EQ": + case "CANONICAL_EQUIVALENCE": + flags |= Pattern.CANON_EQ; + break; + case "UNIX_LINES": + case "UNIX": + flags |= Pattern.UNIX_LINES; + break; + case "LITERAL": + flags |= Pattern.LITERAL; + break; + case "COMMENTS": + flags |= Pattern.COMMENTS; + break; + default: + log.warn("未知的正则表达式标志: {}", trimmedFlag); + } + } + + return flags; + } + + /** + * 测试用例验证方法 + */ + public static void testRegexReplace() { + // 测试用例1:数字替换 + Map params1 = new HashMap<>(); + params1.put("pattern", "\\d+"); + params1.put("replacement", "*"); + + String result1 = regexReplace("abc123def456", params1); + System.out.println("测试1结果: " + result1); // 预期: abc*def* + + // 测试用例2:分组引用替换 + Map params2 = new HashMap<>(); + params2.put("pattern", "(\\d+)-(\\d+)"); + params2.put("replacement", "$2到$1"); + + String result2 = regexReplace("电话: 123-456", params2); + System.out.println("测试2结果: " + result2); // 预期: 电话: 456到123 + + // 测试用例3:大小写不敏感 + Map params3 = new HashMap<>(); + params3.put("pattern", "hello"); + params3.put("replacement", "HI"); + params3.put("flags", "CASE_INSENSITIVE"); + + String result3 = regexReplace("Hello World", params3); + System.out.println("测试3结果: " + result3); // 预期: HI World + } + + /** + * 处理替换内容中的分组引用 + * + * @param matcher 当前匹配器 + * @param replacement 原始替换内容 + * @return 处理后的替换内容(已解析分组引用) + */ + private static String processReplacementGroups(Matcher matcher, String replacement) { + if (replacement == null || replacement.isEmpty()) { + return replacement; + } + + // 简单的分组引用处理(如$1, $2等) + // 更复杂的处理可以使用Matcher.quoteReplacement()结合自定义逻辑 + try { + // 使用Matcher的标准方法处理基本的分组引用 + return matcher.replaceFirst(replacement); + } catch (Exception e) { + log.warn("分组引用处理失败,使用原始替换内容: {}", e.getMessage()); + return replacement; + } + } + + /** + * 增强版正则表达式替换方法 - 支持返回详细统计信息 + * + * @param str 源字符串 + * @param params 参数映射表 + * @return 包含替换结果和统计信息的Map + */ + public static Map regexReplaceWithStats(String str, Map params) { + Map result = new HashMap<>(); + + // 保存原始字符串 + result.put("original", str); + + // 执行替换 + String replaced = regexReplace(str, params); + result.put("result", replaced); + + // 添加统计信息 + result.put("changed", !replaced.equals(str)); + result.put("originalLength", str != null ? str.length() : 0); + result.put("resultLength", replaced != null ? replaced.length() : 0); + + return result; + } + + /** + * 批量正则表达式替换方法 + * + * @param str 源字符串 + * @param patterns 正则表达式模式列表 + * @param replacements 替换内容列表(与patterns一一对应) + * @return 替换后的字符串 + */ + public static String batchRegexReplace(String str, List patterns, List replacements) { + if (str == null || patterns == null || patterns.isEmpty()) { + return str; + } + + String result = str; + + for (int i = 0; i < patterns.size(); i++) { + String pattern = patterns.get(i); + String replacement = i < replacements.size() ? replacements.get(i) : ""; + + Map params = new HashMap<>(); + params.put("pattern", pattern); + params.put("replacement", replacement); + + result = regexReplace(result, params); + } + + return result; + } + + /** + * 验证正则表达式是否有效 + * + * @param pattern 正则表达式模式 + * @return 如果正则表达式有效返回true,否则返回false + */ + public static boolean isValidRegex(String pattern) { + if (pattern == null || pattern.trim().isEmpty()) { + return false; + } + + try { + Pattern.compile(pattern); + return true; + } catch (Exception e) { + return false; } } @@ -800,12 +1164,12 @@ public class FormatUtil { return handleError(value, params, e); } } - + /** * 数据KV映射处理 - * + * *

提供键值对数据的映射、转换和格式化功能。

- * + * *

支持的功能:

*
    *
  • 键值映射:根据映射表转换键或值
  • @@ -814,7 +1178,7 @@ public class FormatUtil { *
  • 数据分组:按特定规则对键值对进行分组
  • *
  • 数据排序:对键值对进行排序
  • *
- * + * * @param data 原始数据(Map、List of Map或JSON字符串) * @param params 参数映射,包含: * - mapping: 键值映射表(Map类型) @@ -822,7 +1186,7 @@ public class FormatUtil { * - valueFormat: 值的格式化规则 * - filter: 过滤条件(Lambda表达式或条件字符串) * - groupBy: 分组字段 - * - sortBy: 排序字段和方向 + * - sortBy: 排序字段和方向(如 "key_asc", "key_desc", "value_asc", "value_desc") * - outputFormat: 输出格式("map", "json", "list") * @return 处理后的数据 */ @@ -830,38 +1194,106 @@ public class FormatUtil { if (data == null) { return null; } - + try { // 解析输入数据 Map processedData = parseInputData(data); - + // 应用键值映射 if (params.containsKey("mapping")) { processedData = applyKeyValueMapping(processedData, params.get("mapping")); } - + // 格式化键 if (params.containsKey("keyFormat")) { processedData = formatKeys(processedData, params.get("keyFormat").toString()); } - + // 格式化值 if (params.containsKey("valueFormat")) { processedData = formatValues(processedData, params.get("valueFormat").toString()); } - + // 数据过滤 if (params.containsKey("filter")) { processedData = filterData(processedData, params.get("filter")); } - + + // 数据排序 - 新增排序功能 + if (params.containsKey("sort") || params.containsKey("sortBy")) { + String sortParam = params.containsKey("sort") ? + params.get("sort").toString() : params.get("sortBy").toString(); + processedData = sortData(processedData, sortParam); + } + // 转换为指定输出格式 return convertToOutputFormat(processedData, params); - + } catch (Exception e) { return handleKvMappingError(data, params, e); } } + + /** + * 对Map数据进行排序 + */ + private static Map sortData(Map data, String sortParam) { + if (data == null || data.isEmpty() || sortParam == null) { + return data; + } + + try { + // 解析排序参数格式:field_direction + String[] parts = sortParam.split("_"); + if (parts.length < 2) { + log.warn("排序参数格式错误,应为: field_direction (如: key_asc, value_desc)"); + return data; + } + + String field = parts[0].toLowerCase(); // key 或 value + String direction = parts[1].toLowerCase(); // asc 或 desc + + // 创建有序的Map + Map sortedMap = new LinkedHashMap<>(); + + // 获取排序后的条目 + List> entries = new ArrayList<>(data.entrySet()); + + // 根据字段和方向排序 + entries.sort((entry1, entry2) -> { + int result = 0; + + if ("key".equals(field)) { + // 按键排序 + result = entry1.getKey().compareTo(entry2.getKey()); + } else if ("value".equals(field)) { + // 按值排序(转换为字符串比较) + String value1 = entry1.getValue() != null ? entry1.getValue().toString() : ""; + String value2 = entry2.getValue() != null ? entry2.getValue().toString() : ""; + result = value1.compareTo(value2); + } + + // 处理降序排序 + if ("desc".equals(direction)) { + result = -result; + } + + return result; + }); + + // 将排序后的条目放入LinkedHashMap + for (Map.Entry entry : entries) { + sortedMap.put(entry.getKey(), entry.getValue()); + } + + log.debug("数据排序完成: 按{} {}排序,共{}个条目", field, direction, sortedMap.size()); + return sortedMap; + + } catch (Exception e) { + log.warn("数据排序失败,返回原始数据: {}", e.getMessage()); + return data; + } + } // ========== 极值/边界值检查处理的辅助方法 ========== diff --git a/src/test/java/com/example/demo/FormatUtilTest.java b/src/test/java/com/example/demo/FormatUtilTest.java index 5008d02..b912dda 100644 --- a/src/test/java/com/example/demo/FormatUtilTest.java +++ b/src/test/java/com/example/demo/FormatUtilTest.java @@ -1,5 +1,6 @@ package com.example.demo; +import com.alibaba.fastjson2.JSON; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.BeforeEach; @@ -9,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.*; import com.example.demo.draft.demo043.util.FormatUtil; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; @@ -61,7 +63,7 @@ public class FormatUtilTest { void tearDown() { params.clear(); } - + // ==================== 时间格式化模块测试 ==================== /** @@ -124,18 +126,16 @@ public class FormatUtilTest { log.info(" - 时间字符串: '{}' (空字符串)", inputTime); log.info(" - 源格式: {}", params.get("fromFormat")); log.info(" - 目标格式: {}", params.get("toFormat")); - - // 预期结果 - log.info("预期结果: 非空结果(空字符串或默认值)"); - + // 执行测试 Object result = FormatUtil.formatTime(inputTime, params); - - // 实际结果 - log.info("实际结果: {}", result); - + // 断言验证 + String expectedResult = ""; + log.info("预期结果: '',(空字符串)"); + log.info("实际结果: '{}'", result); assertNotNull(result); + assertEquals(expectedResult, result); log.info("✓ 测试通过 - 空值处理正确"); } @@ -191,7 +191,7 @@ public class FormatUtilTest { log.info("========== 时间范围提取模块测试 - 正常提取开始时间 =========="); // 输入参数 - String inputTimeRange = "2024-01-15 14:30:25 - 2024-01-15 16:45:30"; + String inputTimeRange = "2024-01-15 14:30:25 ~ 2024-01-15 16:45:30"; params.put("type", "start"); log.info("输入参数:"); @@ -209,6 +209,8 @@ public class FormatUtilTest { log.info("实际结果: {}", result); // 断言验证 + log.info("预期结果:'{}'", expectedResult); + log.info("实际结果:'{}'", result); assertEquals(expectedResult, result); log.info("✓ 测试通过 - 开始时间提取正确"); } @@ -240,12 +242,14 @@ public class FormatUtilTest { // 执行测试 Object result = FormatUtil.extractTimeRange(inputTimeRange, params); - - // 实际结果 - log.info("实际结果: {}", result); - + // 断言验证 + String expectedResult = "2024-01-15 14:30:25"; + log.info("预期结果: '{}'", expectedResult); + log.info("实际结果: '{}'", result); assertNotNull(result); + assertEquals(expectedResult, result); + log.info("✓ 测试通过 - 无分隔符处理正确"); } @@ -495,7 +499,7 @@ public class FormatUtilTest { String inputString = "abc123def456"; String regex = "\\d+"; String replacement = "*"; - params.put("regex", regex); + params.put("pattern", regex); params.put("replacement", replacement); log.info("输入参数:"); @@ -517,6 +521,66 @@ public class FormatUtilTest { assertEquals(expectedResult, result); log.info("✓ 测试通过 - 正则替换正确"); } + + /** + * 测试regexReplace方法 - 正则表达式替换 + * + *

测试目的:验证regexReplace方法能够正确进行正则替换

+ *

测试场景:替换字符串中的数字

+ *

预计结果:替换结果正确

+ */ + @Test + @DisplayName("regexReplace方法测试 - 三种测试") + void testRegexReplace_NormalCase3() { + System.out.println(); + log.info("========== 正则表达式替换模块测试 - 正则替换 =========="); + + // 输入参数 + String inputString = "abc123def456"; + String regex = "\\d+"; + String replacement = "*"; + params.put("pattern", regex); + params.put("replacement", replacement); + + + // 测试用例1:数字替换 + Map params1 = new HashMap<>(); + params1.put("pattern", "\\d+"); + params1.put("replacement", "*"); + String result1 = FormatUtil.regexReplace("abc123def456", params1); + log.info("实际结果 (1): " + result1); + // 断言验证 + String expectedResult1 = "abc*def*"; + log.info("预期结果:'{}'", expectedResult1); + log.info("实际结果:'{}'", result1); + assertEquals(expectedResult1, result1); + + // 测试用例2:分组引用替换 + Map params2 = new HashMap<>(); + params2.put("pattern", "(\\d+)-(\\d+)"); + params2.put("replacement", "$2到$1"); + String result2 = FormatUtil.regexReplace("电话: 123-456", params2); + log.info("实际结果 (2): " + result2); + // 断言验证 + String expectedResult2 = "电话: 456到123"; + log.info("预期结果:'{}'", expectedResult2); + log.info("实际结果:'{}'", result2); + assertEquals(expectedResult2, result2); + + // 测试用例3:大小写不敏感 + Map params3 = new HashMap<>(); + params3.put("pattern", "hello"); + params3.put("replacement", "HI"); + params3.put("flags", "CASE_INSENSITIVE"); + String result3 = FormatUtil.regexReplace("Hello World", params3); + log.info("实际结果 (3): " + result3); + // 断言验证 + String expectedResult3 = "HI World"; + log.info("预期结果:'{}'", expectedResult3); + log.info("实际结果:'{}'", result3); + assertEquals(expectedResult3, result3); + log.info("✓ 测试通过 - 正则替换正确"); + } /** * 测试toUpperCase方法 - 大小写转换 @@ -647,19 +711,19 @@ public class FormatUtilTest { log.info("输入参数:"); log.info(" - 输入字符串: {}", inputString); - log.info(" - 替换映射: {}", replacementMap); + log.info(" - 替换映射: {}", JSON.toJSONString(replacementMap)); log.info(" - 大小写敏感: {}", caseSensitive); log.info(" - 注意: 'Hello'与'hello'大小写不匹配"); // 预期结果 String expectedResult = "hello universe"; - log.info("预期结果: {}", expectedResult); + log.info("预期结果:'{}'", expectedResult); // 执行测试 String result = FormatUtil.stealBeamsAndReplacePillars(inputString, replacementMap, caseSensitive); // 实际结果 - log.info("实际结果: {}", result); + log.info("实际结果:'{}'", result); // 断言验证 assertEquals(expectedResult, result); @@ -780,9 +844,12 @@ public class FormatUtilTest { // 实际结果 log.info("实际结果: {}", result); - + // 断言验证 - assertNotNull(result); + String expectedResult = "116°23'45.67\""; + log.info("预期结果:'{}'", expectedResult); + log.info("实际结果:'{}'", result); + assertEquals(expectedResult, result); log.info("✓ 测试通过 - 十进制转度分秒转换正确"); } @@ -1083,7 +1150,7 @@ public class FormatUtilTest { log.info("========== 数据KV映射模块测试 - 数据过滤 =========="); // 输入参数 - Map inputMap = new HashMap<>(); + Map inputMap = new LinkedHashMap<>(); inputMap.put("name", "张三"); inputMap.put("age", 25); inputMap.put("address", null); @@ -1104,7 +1171,10 @@ public class FormatUtilTest { log.info("实际结果: {}", result); // 断言验证 - assertNotNull(result); + assertNotNull(result instanceof Map); + if(result instanceof Map) { + boolean flag = false; + } log.info("✓ 测试通过 - 数据过滤正确"); } @@ -1122,7 +1192,7 @@ public class FormatUtilTest { log.info("========== 数据KV映射模块测试 - 分组排序 =========="); // 输入参数 - Map inputMap = new HashMap<>(); + Map inputMap = new LinkedHashMap<>(); inputMap.put("z_key", "value3"); inputMap.put("a_key", "value1"); inputMap.put("m_key", "value2"); @@ -1143,7 +1213,16 @@ public class FormatUtilTest { log.info("实际结果: {}", result); // 断言验证 - assertNotNull(result); + Map expectedResult = new LinkedHashMap<>() {{ + put("a_key", "value1"); + put("m_key", "value2"); + put("z_key", "value3"); + }}; + String expectedResultString = JSON.toJSONString(expectedResult); + String resultString = JSON.toJSONString(result); + log.info("预期结果:'{}'", expectedResult); + log.info("实际结果:'{}'", result); + assertEquals(expectedResultString, resultString, "排序失败"); log.info("✓ 测试通过 - 分组排序正确"); }