
java 中 `int[]` 默认使用引用相等(`==`),`hashset` 的 `contains()` 无法按元素值判断是否重复;需封装数组为自定义类型,并重写 `equals()` 和 `hashcode()`,使用 `arrays.equals()` 和 `arrays.hashcode()` 实现深度比较。
在处理二维整数数组(如矩阵行)的逻辑去重时,一个常见误区是直接将 int[] 存入 HashSet 并期望其自动按内容相等进行判重。但 Java 中数组是对象,int[] 类型未重写 equals() 和 hashCode(),因此 HashSet 内部调用的是 Object 的默认实现——即基于内存地址(引用)的比较。这意味着即使两个 int[] 元素完全相同(如 {1,2,3,4} 和 {1,2,3,4}),只要不是同一对象实例,contains() 就会返回 false,导致重复添加。
要解决该问题,核心思路是将原始数组封装为支持值语义(value-based)的不可变类型,并正确实现 equals() 与 hashCode()。推荐使用 Java 14+ 的 record(简洁安全),也可用普通类或 List
以下是修正后的关键代码片段(已适配 Codewars 题目逻辑):
record Row(int[] row) {
@Override
public int hashCode() {
return Arrays.hashCode(row); // 基于元素内容生成哈希码
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Row row1 = (Row) o;
return Arrays.equals(row, row1.row); // 深度比较数组内容
}
}在主逻辑中,所有 int[] 必须统一包装为 Row 实例:
private static final SetregisteredM = new HashSet<>(); static public int count_different_matrices(int[][] matrices) { registeredM.clear(); // 注意:静态集合需重置,避免多次调用污染 Arrays.stream(matrices) .forEach(m -> { List
variants = unwrapPossibleMatrices(m); if (variants.stream().noneMatch(registeredM::contains)) { registeredM.add(new Row(m)); } }); registeredM.forEach(e -> System.out.println(Arrays.toString(e.row()))); return registeredM.size(); } private static List
unwrapPossibleMatrices(int[] m) { return Arrays.asList( new Row(m), new Row(new int[]{m[2], m[0], m[3], m[1]}), new Row(new int[]{m[3], m[2], m[1], m[0]}), new Row(new int[]{m[1], m[3], m[0], m[2]}) ); }
✅ 关键注意事项:
-
切勿复用原始 int[]:HashSet
永远不会按内容去重,必须全部转为封装类型; - Arrays.hashCode() 与 Arrays.equals() 是标准解法:它们专为数组内容比较设计,高效且语义明确;
- 静态集合需手动清理:本例中 registeredM 是 static final,但方法需支持多次调用,故应在入口处调用 clear();
- record 天然不可变且线程安全:避免因外部修改 row 数组导致哈希不一致(若用普通类,务必确保 row 字段 private final 且不暴露引用)。
通过此方案,输入 {{1,2,3,4},{3,1,4,2},{4,3,2,1},{2,4,1,3}} 将正确识别所有四行均互为旋转等价(题目定义的四种变换),最终只保留一个代表元,输出 1 —— 符合题意预期。










