【问题标题】:Unexplained behavior while using hashmaps使用哈希图时出现无法解释的行为
【发布时间】:2013-05-19 18:22:53
【问题描述】:

下面的精简程序对我来说效果很好,但是当我将 hashVal 声明(粗体)移到 for 循环之外时,我的程序无法正常运行。在插入哈希映射时,为什么我需要它在 for 循环内?在我快速而草率的实现工作之后,我在考虑潜在的优化时发现了这一点。然而,现在看来,快速而草率的实现是可行的,但不是我认为应该是优化的版本。

public class X
{
   public static void foo()
   {
      Integer x1 = 0;
      HashMap<Integer, BigInteger[]> map = new HashMap<Integer, BigInteger[]>();
      int hashKey;
      /* **BigInteger[] hashVal  = new BigInteger[2];**  <-----Does not run correctly 
      if I keep the hashVal declaration here. (1) */
      for(x1 = 0; x1 <= 1048576; x1++)
      {
        BigInteger bx1 = BigInteger.valueOf(x1.intValue());
        **BigInteger[] hashVal  = new BigInteger[2];** (2)
        BigInteger res;
        /* Do lots and lots of big integer calculations and get a final result in res */
        hashKey = res.hashCode();
        /* Store res and x1 in hashmap */
        hashVal[0] = res;
        hashVal[1] = BigInteger.valueOf(x1.intValue());
        map.put(hashKey, hashVal);
      }
      Integer x0;
      for(x0 = 0; x0 <= 1048576; x0++)
      {
        /* do lots of BigInteger calculations to generate res */ 
        hashKey = res.hashCode();
        **bigNum = map.get(hashKey); <--------------Never returns a match if (1) above is enabled instead of (2) !**
    }
}

}

【问题讨论】:

    标签: java reference hashmap pass-by-reference


    【解决方案1】:

    ...因为当 hashVal 在循环之外时,它只会被创建一次,因此在循环内你不断地将相同的 BigInteger[] 推入地图。换句话说,映射中的每个项目最终都是对同一个 BigInteger[] 的引用。这意味着您在最后一次循环中放入 BigInteger[] 的任何值都将是地图中的每个引用所看到的。

    当你在循环内创建一个新的 BigInteger[] 时,每次循环 hashVal 都是对不同 BigInteger[] 的引用。

    【讨论】:

    • 非常感谢。那么我该如何优化它,这样我就不必每次都在循环中创建新的 BigInteger[] 呢?在 C 中我没有这个问题吧?
    • @user2399453,你不能。您必须在每次传递时创建一个新的BigInteger[],否则您将覆盖以前的值。 Java 版本和 C/C++ 版本之间的区别在于,在 Java 中,您的调用函数在每次传递时都会创建新数组,而在 C/C++ 版本中,put 函数或 [] 运算符会复制数组,但结果是类似 - 为每个通道创建一个数组。
    • 所以在 Java 中,我不能创建一个数组一次(使用 new BigInteger[2])并从那时起继续为索引 0 和 1 分配不同的值?新的操作符像malloc吗?在 Java 中,我想这是可以的,因为垃圾收集..
    • @user2399453:我们看不到您的“C”代码,所以我们不知道您在做什么。 C 中没有内置的“hashmap”,在 C 和 C++ 中,普通的“数组”不能被分配或放入集合中。如果您在 C++ 中使用“向量”,那将是另一回事,因为分配向量会复制它。
    • 谢谢,实际上我没有使用 C 或 C++。那么 new 是否等同于 malloc?我的意思是在 C 中我可以这样做: int a[2];一个[0] = x; a[1] = y;等等,它会正确分配值。我可以在 Java 上为 BigInteger 数组做一些等效的事情,这样我就不必每次在循环中都做一个新的吗?无论如何,我在我的代码中发现了一些其他重大优化,将执行时间缩短了 10 倍,所以我实际上状态良好。
    【解决方案2】:

    那是因为,(2) 在循环内被声明和初始化。因此,无论经过多少次迭代,hashVal 都将始终包含上一次迭代的值。我的建议是在循环外声明 hashVal 并在循环内使用它。新代码如下所示。

    public class X
    {
       public static void foo()
       {
          Integer x1 = 0;
          HashMap<Integer, BigInteger[]> map = new HashMap<Integer, BigInteger[]>();
          int hashKey;
          BigInteger[] hashVal  = null; //changed here
          /* if I keep the hashVal declaration here. (1) */
          for(x1 = 0; x1 <= 1048576; x1++)
          {
            BigInteger bx1 = BigInteger.valueOf(x1.intValue());
            hashVal  = new BigInteger[2]; //changed here
            BigInteger res;
            /* Do lots and lots of big integer calculations and get a final result in res */
            hashKey = res.hashCode();
            /* Store res and x1 in hashmap */
            hashVal[0] = res;
            hashVal[1] = BigInteger.valueOf(x1.intValue());
            map.put(hashKey, hashVal);
          }
          Integer x0;
          for(x0 = 0; x0 <= 1048576; x0++)
          {
            /* do lots of BigInteger calculations to generate res */ 
            hashKey = res.hashCode();
           bigNum = map.get(hashKey); 
        }
    }
    

    我的更改已在代码中注释为“在此处更改”。 如果这种方法解决了您的问题,请告诉我。

    谢谢, 马杜。

    【讨论】:

      【解决方案3】:

      我的意思是在 C 中我可以做到这一点:

      int a[2]; a[0] =x; a[1] = y; 
      

      等重复,它会正确分配值。

      在你的 Java 程序中发生的同样的事情也发生在 C 中:

      int main()
      {
          int numbers[2];
          int* array_of_int_pointers[2];
      
      
          for (int i=0; i<2; ++i) {
              numbers[0] = i * 10;
              numbers[1] = i * 20;
      
              printf("%d : %d \n", numbers[0], numbers[1]);
      
              array_of_int_pointers[i] = numbers;
          }
      
          int* first_array = array_of_int_pointers[0];
          int* second_array = array_of_int_pointers[1];
      
          printf("%d \n", first_array[1] );
      
          return 0;
      }
      
      --output:--
      0 : 0 
      10 : 20 
      20 
      

      你的问题问为什么输出的最后一行不是 0。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-28
        • 1970-01-01
        • 2021-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-17
        相关资源
        最近更新 更多