Processing.js 哈希图

Processing.js HashMap

我在 Processing.js 中迭代 HashMap 时遇到问题。调用迭代器时,永远不会输入 while(it.hasNext()) 。

作为完整性检查,我尝试跳过迭代器,而是将键转换为数组并按索引迭代这些键。那也没用。我打印出以下内容:

myHashMap.size();          // outputs 4
myHashMap.keySet().size(); // outputs 4
myHashMap.keySet().toArray().length; // outputs 0

我希望最后一行也输出 4,就像他们之前的调用那样。我理解错了吗?谢谢!


编辑

这是我 运行 遇到的问题的完整示例。结果证明在我的 HashCode 中使用浮点数有问题,尽管我仍然不明白为什么在将 keySet 转换为数组时会导致元素不匹配。

class Vertex {
    float x;
    float y;

    public Vertex(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public int hashCode() {
        int hash = 17;
        hash = ((hash + x) << 5) - (hash + x);    
        hash = ((hash + y) << 5) - (hash + y);    
        return hash
    }

    public boolean equals(Object obj) {    
        Vertex other = (Vertex) obj;    
        return (x == obj.x && y == obj.y);    
    }

}

HashMap<Vertex, String> tmp = new HashMap<Vertex, String>();                
Vertex a = new Vertex(1.1, 2.2);    
Vertex b = new Vertex(1, 1);    
tmp.put(a, "A");                         
tmp.put(b, "B");                         

println("1, " + tmp.size());                      // outputs 2         
println("2, " + tmp.keySet().size());             // outputs 2
println("3, " + tmp.keySet().toArray().length);   // outputs 1

我想我终于明白了。这需要一些挠头。好问题。

这个故事的寓意:我认为您在使用 hashCode() 函数时遇到了一些奇怪的问题。 hashCode() 函数必须 return 一个 int 值,但你 return 一个 float 值。

要解决您的问题,只需在 return 之前将 hash 值转换为 int

  public int hashCode() {
    int hash = 17;
    hash = ((hash + x) << 5) - (hash + x);    
    hash = ((hash + y) << 5) - (hash + y);   
    return (int)hash;
  }

这可能看起来很奇怪而且没有必要,所以这里有一个更长的解释:

请注意 xyfloat 值,因此当您在 hash 计算中使用它们时,结果变为 float 作为出色地。您可以通过在 return 之前打印出 hash 的值来证明这一点。

Java 会抱怨这个。您可以通过简单地切换到 Java 模式并尝试 运行 您的程序来证明这一点。但是 JavaScript 对其类型没有那么严格,所以它会让你搬起石头砸自己的脚。我通常做的事情是在 Java 模式下编程以进行错误检查,然后使用 Java 脚本模式进行部署。

无论如何,在HashMapclass内部,hashCode()函数的结果最终被用作数组中的索引。您可以在 Processing.js 源文件中自行查看:

//this is in the HashMap class
function getBucketIndex(key) {
        var index = virtHashCode(key) % buckets.length;
        return index < 0 ? buckets.length + index : index;
}

function virtHashCode(obj) {
    if (obj.hashCode instanceof Function) {
      return obj.hashCode();
    }
   //other code omitted to keep this short
}

这可能没问题,因为奇怪的是 Java脚本可以在数组索引中使用小数位。但问题是 HashSet's Iterator:

的执行
function Iterator(conversion, removeItem) {
    var bucketIndex = 0;
    var itemIndex = -1;
    var endOfBuckets = false;
    var currentItem;

    function findNext() {
      while (!endOfBuckets) {
        ++itemIndex;
        if (bucketIndex >= buckets.length) {
          endOfBuckets = true;
        } else if (buckets[bucketIndex] === undef || itemIndex >= buckets[bucketIndex].length) {
          itemIndex = -1;
          ++bucketIndex;
        } else {
          return;
        }
      }
    }
    //more code

检查 findNext() 函数。它循环遍历数组的索引,但每次递增 1。因此,任何放入小数位索引的键都将被跳过!

这就是为什么您的迭代器会跳过您的对象之一(在 hashCode() 的值 return 中具有小数位的对象)。这就是 toArray() 也失败的原因,因为该函数在后台使用了 Iterator

我不会真的称这是一个错误,因为问题是由 return 从一个函数中调用 float 引起的,该函数表示它将 return 和 int. Java脚本并不需要像 Java 那样的 hashCode() 函数,所以 HashMapIterator 的实现是非常合理的。您只需要确保从 hashCode() 函数 returning 一个 int

顺便说一句,如果你喜欢玩,这里有一个更小的例子:

class Thing {
  int myThing;

  public Thing(int myThing) {
    this.myThing = myThing;
  }

  public int hashCode(){
    return myThing;
  }

  public boolean equals(Object obj) {    
    Thing other = (Thing) obj;    
    return (myThing == other.myThing);
  }
}

void setup() {

  HashMap<Thing, String> map = new HashMap<Thing, String>();
  map.put(new Thing(1), "A");
  map.put(new Thing(2.5), "B"); //uh oh!
  map.put(new Thing(3), "C");
  println("1, " + map.size());                      // outputs 3         
  println("2, " + map.keySet().size());             // outputs 3
  println("3, " + map.keySet().toArray().length);   // outputs 2
}