【问题标题】:Java HashMap with Int Array带有 Int 数组的 Java HashMap
【发布时间】:2011-02-07 08:17:22
【问题描述】:

我正在使用此代码检查 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 中是否存在数组?

【问题讨论】:

    标签: java arrays hashmap


    【解决方案1】:

    int[] 的两个实例不同且不相等。

    一个不错的方法是使用 Arrays.toString(arr) 将 int 数组转换为 String:

    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 AND int[]{12} 将是 #12
    【解决方案2】:

    您正在比较两个不同的参考。 这样的事情会起作用:

    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() 调用中。

    【讨论】:

    【解决方案3】:

    我会使用不同的方法。如前所述,问题在于数组相等性,它基于引用相等性,使您的地图无法满足您的需求。 另一个潜在问题,假设您使用 ArrayList 代替,是一致性问题:如果您在将列表添加到映射后更改列表,您将遇到哈希映射损坏,因为列表的位置将不再反映其哈希码。

    为了解决这两个问题,我会使用某种不可变列表。例如,您可能希望在 int 数组上创建一个不可变的包装器,然后自己实现 equals() 和 hashCode()。

    【讨论】:

    【解决方案4】:

    问题是因为两个int[] 不相等。

    System.out.println(
        (new int[] { 1, 2 }).equals(new int[] { 1, 2 })
    ); // prints "false"
    

    Map 和其他 Java 集合框架类根据 equals 定义其接口。来自Map API

    Collections Framework 接口中的许多方法都是根据equals 方法定义的。例如,containsKey(Object key) 方法的规范说:“当且仅当此映射包含一个键 k 的映射使得 (key==null ? k==null : key.equals(k)) 时才返回 true。”

    请注意,它们不必是同一个对象;他们只需要equals。 Java 中的数组从Object 扩展而来,equals 的默认实现仅在对象标识上返回 true;因此为什么它在 sn-p 上方打印false


    您可以通过多种方式之一解决您的问题:

    • equals 使用java.util.Arrays equals/deepEquals 方法的数组定义您自己的包装类。
      • 别忘了当你@Override equals(Object)时,你也必须@Override hashCode
    • 使用类似List&lt;Integer&gt; 的东西确实根据它们包含的值定义equals
    • 或者,如果您可以使用equals 的引用相等性,您可以坚持现有的。正如您不应该期望上面的 sn-p 会打印 true 一样,您也不应该期望能够仅通过其值找到您的数组;您每次都必须坚持并使用原始参考资料。

    另见:

    API

    • Object.equalsObject.hashCode
      • Java 程序员必须了解这些合约以及如何使它们与系统的其余部分一起工作/用于系统的其余部分

    【讨论】:

      【解决方案5】:

      您确定不想将Strings 映射到数组而不是相反吗?

      无论如何,要回答您的问题,问题是您在调用containsKey() 时正在创建一个new 数组。这将在您之间返回 false 您有两个单独的 newed 数组,它们恰好具有相同的元素和维度。查看 Yuval 的回答,了解检查数组是否作为键包含的正确方法。

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

      【讨论】:

        【解决方案6】:

        数组的hashCode() 实现派生自Object.hashCode(),因此它取决于数组的内存位置。由于这两个数组是分别实例化的,因此它们具有不同的内存位置,因此具有不同的哈希码。如果您制作了一个数组,它将起作用:

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

        【讨论】:

          【解决方案7】:

          你有两个碰巧包含相同值的不同对象,因为你已经调用了两次 new。

          您可能使用的一种方法是创建自己的“持有者”类,并定义该类的 equals 和 hash 方法。

          【讨论】:

            【解决方案8】:

            我认为问题在于您的数组正在进行“==”比较,即它正在检查参考。当您执行 containsKey(new int[] { ... }) 时,它正在创建一个新对象,因此引用不一样。

            如果您将数组类型更改为 ArrayList&lt;Integer&gt; 之类的东西应该可以工作,但是我倾向于避免使用列表作为映射键,因为这不会很有效。

            【讨论】:

              猜你喜欢
              • 2011-07-12
              • 1970-01-01
              • 1970-01-01
              • 2020-11-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-11-06
              • 1970-01-01
              相关资源
              最近更新 更多