如何将二维数组元素作为Map的Key正确存入?
嘿,我发现你现在的写法会踩一个Java里的常见坑——用数组作为HashMap的Key时,默认的行为和你预期的不一样!
原因很简单:Java中的数组是对象,但它继承的是Object类的equals()和hashCode()方法,这两个方法是基于对象引用来判断的,而不是数组里的内容。也就是说,哪怕两个数组的元素完全相同,只要它们是不同的数组对象(比如你代码里的points[0]和一个新创建的int[]{1,2}),HashMap就会把它们当成完全不同的Key,根本不会认为是同一个键。
那该怎么解决呢?给你三种靠谱的方案,按需选择:
方案一:用List<Integer>代替int[]作为Key
List接口的实现类(比如ArrayList)已经帮你重写了equals()和hashCode(),是基于集合内容来判断的,完美符合你的需求。写法也很简单:
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; public class TrySome { static int[][] points = {{1,2}, {1,1}, {5,7}}; public static void some() { HashMap<List<Integer>, Integer> map = new HashMap<>(); for(int[] point : points) { List<Integer> key = new ArrayList<>(); key.add(point[0]); key.add(point[1]); map.put(key, 1); } for(Entry<List<Integer>, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } } public static void main(String[] args) { some(); } }
这个方案的优点是零额外代码,直接用JDK自带的类就能搞定,适合快速实现。缺点是如果数组元素很多,包装成List的性能会稍逊于自定义类。
方案二:自定义一个包装类,重写equals()和hashCode()
如果你追求更好的性能,或者想让代码语义更清晰(比如明确这是一个坐标点),可以自己写一个简单的类来包装数组元素:
import java.util.HashMap; import java.util.Map.Entry; class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } // 重写equals:只有x和y都相等时,才认为两个Point对象相等 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Point point = (Point) o; return x == point.x && y == point.y; } // 重写hashCode:基于x和y计算哈希值,保证equals相等的对象hashCode也相等 @Override public int hashCode() { int result = x; result = 31 * result + y; // 31是常用的质数,能减少哈希冲突 return result; } // 重写toString,方便打印输出 @Override public String toString() { return "[" + x + ", " + y + "]"; } } public class TrySome { static int[][] points = {{1,2}, {1,1}, {5,7}}; public static void some() { HashMap<Point, Integer> map = new HashMap<>(); for(int[] point : points) { Point key = new Point(point[0], point[1]); map.put(key, 1); } for(Entry<Point, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } } public static void main(String[] args) { some(); } }
这个方案的优势是性能更高,而且代码可读性强,别人一看就知道这是一个坐标点。如果你的数组是更高维度的,只需要扩展这个类的属性就行。
方案三:借助第三方工具类(可选)
如果你已经在项目中使用了Apache Commons Lang库,可以直接用ArrayUtils.hashCode(int[])和ArrayUtils.isEquals(int[], int[])来辅助,但需要自己包装一下。不过因为要引入第三方依赖,一般不是首选,除非项目里已经有了。
最后再强调一下你原来代码的问题:假设你现在用map.get(new int[]{1,2}),会返回null,因为这个新数组和points[0]是不同的对象,HashMap认不出来。而用上面的方案一或二,map.get(List.of(1,2))或者map.get(new Point(1,2))就能正确返回1了。
内容的提问来源于stack exchange,提问作者user7748519

