告警ID统计
问题背景
在一个系统中,存在两块独立的单板。每块单板都会产生告警,这些告警的唯一标识(告警ID)分别存储在两个列表 arrayA 和 arrayB 中。为了进行统一的监控和管理,需要将两块单板上报的所有告警ID进行合并、去重和排序。
任务要求
请实现一个程序,统计系统中所有出现过的告警ID。具体处理步骤如下:
- 合并: 将列表
arrayA和arrayB中的所有告警ID合并成一个集合。 - 去重: 如果同一个告警ID在两个列表或单个列表中出现多次,最终结果中只保留一个。
- 排序: 将去重后的告警ID,按照其十六进制数值的升序进行排列。
告警ID格式
- 告警ID是一个无符号整数,以十六进制字符串的形式表示。
- 字符串由
0-9的数字字符和大写字母A-F组成。 - 每个告警ID字符串的长度固定为 8个字符。
输入格式
-
arrayA: 第一个参数,一个字符串列表,代表第一块单板的告警ID。0 <= arrayA.length <= 1000
-
arrayB: 第二个参数,一个字符串列表,代表第二块单板的告警ID。0 <= arrayB.length <= 1000
输出格式
- 一个字符串列表,其中包含所有去重后、并按数值升序排列的告警ID。
样例说明
样例输入
arrayA = ["00001001", "00ABCD00"]
arrayB = ["FFFFFAAB", "FFFFFAAB", "00ABCD00"]
样例输出
["00001001", "00ABCD00", "FFFFFAAB"]
解释
-
合并与去重:
-
将
arrayA和arrayB合并,得到所有出现过的告警ID集合:"00001001"(来自arrayA)"00ABCD00"(在arrayA和arrayB中都存在,去重后保留一个)"FFFFFAAB"(在arrayB中出现两次,去重后保留一个)
-
去重后的唯一告警ID集合为:
{"00001001", "00ABCD00", "FFFFFAAB"}。
-
-
排序:
- 根据十六进制数值的大小对这三个ID进行升序排序。
- 比较数值大小: 。
- (由于所有字符串长度相同,直接按字典序排序即可得到数值排序的结果)
-
最终输出:
- 排序后的列表为
["00001001", "00ABCD00", "FFFFFAAB"]。
- 排序后的列表为
import java.util.*;
/**
* 解决思路:
* 1. **合并与去重**:使用 `HashSet` 数据结构来存储所有告警ID。`Set` 的特性是内部元素不重复,
* 因此将两个数组的所有元素添加到 `Set` 中,就可以自动完成合并和去重。
* 2. **排序**:题目要求按告警ID所代表的**数值大小**升序排列,而不是字符串的字典序。
* 因此,不能直接对字符串列表进行排序。正确的做法是:
* a. 将去重后的 `Set` 转换为 `List`。
* b. 使用 `Collections.sort()` 或 `List.sort()`,并提供一个自定义的 `Comparator`。
* c. 在这个 `Comparator` 中,将两个待比较的十六进制字符串转换为长整型 (`long`) 数值,然后比较这两个数值。
* 3. **数值转换**:由于告警ID是无符号整数,一个8位的十六进制字符串(如 "FFFFFFFF")所代表的数值会超出 Java `int` 的正数范围。
* 因此,使用 `Long.parseUnsignedLong(hexString, 16)` 是最安全和正确的方法,它可以正确地将十六进制字符串解析为无符号数值进行比较。
* 其实8位这个长度用parseLong也行
*/
public class Main {
/**
* 核心逻辑方法:合并、去重并按数值排序告警ID。
*
* @param arrayA 第一个告警ID列表 (十六进制字符串数组)
* @param arrayB 第二个告警ID列表 (十六进制字符串数组)
* @return 一个包含所有唯一告警ID的列表,按其代表的数值大小升序排列。
*/
public static List<String> processAlarms(String[] arrayA, String[] arrayB) {
// 步骤 1 & 2: 合并与去重
// 使用 HashSet 可以高效地完成合并和去重,因为 Set 不允许重复元素。
Set<String> uniqueAlarmsSet = new HashSet<>();
// 将第一个数组的元素添加到 Set 中
if (arrayA != null) {
// Collections.addAll 比手动循环更简洁
Collections.addAll(uniqueAlarmsSet, arrayA);
}
// 将第二个数组的元素添加到 Set 中,重复的ID会被自动忽略
if (arrayB != null) {
Collections.addAll(uniqueAlarmsSet, arrayB);
}
// 步骤 3: 将 Set 转换为 List,以便进行排序
List<String> resultList = new ArrayList<>(uniqueAlarmsSet);
// 步骤 4: 使用自定义比较器进行排序
// sort 方法接收一个 Comparator lambda 表达式
resultList.sort((hexStr1, hexStr2) -> {
// 核心比较逻辑:按十六进制字符串代表的数值大小进行比较
// 使用 Long.parseUnsignedLong(string, 16) 将8位十六进制字符串
// 安全地解析为一个无符号的数值(存储在 long 类型中)。
// 这样做可以正确处理像 "FFFFFFFF" 这样的大数值。
long val1 = Long.parseUnsignedLong(hexStr1, 16);
long val2 = Long.parseUnsignedLong(hexStr2, 16);
// Long.compare(val1, val2) 会返回 -1, 0, 或 1,
// 实现了标准的升序比较。
return Long.compare(val1, val2);
});
// 步骤 5: 返回排序后的列表
return resultList;
}
// --- 主函数处理 ACM 输入输出 ---
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取两行输入
// 假设输入格式为一行一个数组,数组内部元素以空格分隔
// 例如:
// 00001001 00ABCD00
// FFFFFAAB FFFFFAAB 00ABCD00
String lineA = scanner.nextLine();
String lineB = scanner.nextLine();
scanner.close();
// 根据空格分割字符串,并处理可能的空行输入
String[] arrayA = lineA.trim().isEmpty() ? new String[0] : lineA.trim().split("\s+");
String[] arrayB = lineB.trim().isEmpty() ? new String[0] : lineB.trim().split("\s+");
// 调用核心逻辑
List<String> result = processAlarms(arrayA, arrayB);
// 按题目要求的格式打印输出
// 例如:["00001001", "00ABCD00", "FFFFFAAB"]
System.out.println(result.toString().replace(" ", ""));
}
}