Java HashMap与整数数组

24
我使用以下代码来检查数组是否存在于 HashMap 中:
public class Test {
    public static void main(String[] arg) {
        HashMap<int[], String> map = new HashMap<int[], String>();
        map.put(new int[]{1, 2}, "sun");
        System.out.println(map.containsKey((new int[]{1, 2})));
    }
}

但是这段代码打印出了False。我该如何检查数组是否存在于HashMap中?

8个回答

41
问题出在两个int[]不相等。
System.out.println(
    (new int[] { 1, 2 }).equals(new int[] { 1, 2 })
); // prints "false"
Map及其他Java集合框架类是根据equals定义其接口的。来自Map API
许多集合框架接口中的方法是根据equals方法定义的。例如,containsKey(Object key)方法的规范说:“当且仅当此映射包含一个键 k,满足(key==null ? k==null : key.equals(k))”时返回true
请注意,它们不必是相同的对象;它们只需要是相等的。Java中的数组继承自Object,其equals的默认实现仅在对象标识上返回true,因此在上面的片段中打印false
你可以通过以下方法之一解决你的问题:
  • 为数组定义自己的包装器类,其中equals使用java.util.Arraysequals/deepEquals方法。
    • 不要忘记当你@Override equals(Object)时,你也必须@Override hashCode
  • 使用像List<Integer>这样的定义了基于其所包含值的equals的东西
  • 或者,如果可以使用引用相等性进行equals,则可以继续使用当前的方法。就像你不应该期望上面的片段会打印出true一样,你也不应该期望仅通过其值找到数组;每次都必须保留原始引用并使用它们。
参见: API:

8
你正在比较两个不同的引用。 像这样的内容可以起作用:
public class Test {
    public static void main(String[] arg)
    {
     HashMap<int[],String> map= new HashMap<int[],String>();
     int[] a = new int[]{1,2};
     map.put(a, "sun");
     System.out.println(map.containsKey(a));
    }
}

由于a是同一引用,您将得到预期的true。如果您的应用程序没有传递引用以进行比较的选项,我会创建一个新的对象类型,其中包含int[]并覆盖equals()方法(同时不要忘记覆盖hashCode()),这样就可以反映在containsKey()调用中。


5
仅实现equals()不够的,您还需要实现hashCode()。请参阅《Effective Java》第3章(可在线获取:http://java.sun.com/developer/Books/effectivejava/Chapter3.pdf)。 - Joachim Sauer

4
我会采用不同的方法。如先前所述,问题在于数组相等性,它基于引用相等性,使您的映射对您的需求无用。另一个潜在的问题是,假设您使用ArrayList,一致性的问题:如果您在将列表添加到映射之后更改了该列表,则会导致哈希映射破坏,因为列表的位置将不再反映其哈希码。
为了解决这两个问题,我会使用某种不可变列表。例如,您可能希望对int数组进行不可变包装,并自行实现equals()和hashCode()。

这里有解释 - https://dev59.com/qmoy5IYBdhLWcg3wF6ML - Ashwin Jayaprakash

2

hashCode()对于数组的实现是从Object.hashCode()派生而来的,因此它取决于数组的内存位置。由于这两个数组是分别实例化的,它们有不同的内存位置,因此具有不同的哈希码。如果您只创建一个数组,则可以正常工作:

int[] arr = {1, 2};
map.put(arr, "sun");
System.out.println(map.containsKey(arr));

2
我认为问题在于你的数组正在进行“==”比较,即它正在检查引用。当你执行containsKey(new int[] { ... })时,它会创建一个新对象,因此引用不同。
如果你将数组类型更改为像ArrayList 这样的东西,那么应该可以工作,但是我倾向于避免使用List作为映射键,因为这不会非常高效。

1

你有两个不同的对象,它们恰好包含相同的值,因为你调用了两次 new。

你可以采用一种方法是创建一个自己的“保持者”类,并定义该类的 equals 和 hash 方法。


1

你确定不想把 Strings 映射到数组而不是反过来吗?

无论如何,回答你的问题,问题在于当你调用 containsKey() 时,你创建了一个新的数组。这会返回 false,因为你有两个分别使用 new 创建的数组,它们恰好具有相同的元素和维度。请参考 Yuval 的答案,以了解检查数组是否作为键包含的正确方法。

另一种更高级的方法是创建一个自己的类,该类包装一个数组并覆盖 hashCode(),以便具有相同维度和元素的两个数组将具有相等的哈希码。


0

这两个 int[] 实例是不同的,而且不相等。

一个好的方法是使用 Arrays.toString(arr) 将 int 数组转换为字符串:

HashMap<String, String> h = new HashMap<>();
int[] a = new int[]{1, 2};
h.put(Arrays.toString(a), "sun");
h.get(Arrays.toString(new int[]{1, 2})); // returns sun

new int[]{12} 也会返回 false,因此我们可以在将其转换为字符串之前放置某种标识符,例如 # 因此, int[]{1,2} 将是 #1#2,而 int[]{12} 将是 #12。 - ArpitA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接