Processing.js HashMap

Processing.js HashMap

本文关键字:HashMap js Processing      更新时间:2024-03-09

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

作为健全性检查,我尝试跳过迭代器,而是将键转换为数组,并通过索引对其进行迭代。这也没用。我打印出以下内容:

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

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


编辑

这是我遇到的问题的一个完整的例子。在我的HashCode中使用float是个问题,尽管我仍然不明白为什么在将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()函数必须返回int值,但您返回的是float值。

若要解决问题,请将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。您可以在返回之前打印出hash的值来证明这一点。

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

无论如何,在HashMap类中,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
}

这可能很好,因为奇怪的是,JavaScript可以在数组索引中使用小数点。但问题在于HashSetIterator:的实现

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()的功能。它在数组的索引中循环,但每次递增一个。因此,任何放入小数点索引的键都将被跳过!

这就是迭代器跳过其中一个对象(从hashCode()返回的值中有一个小数位的对象)的原因。这就是toArray()也失败的原因,因为该函数在引擎盖下使用了Iterator

我不会真的称之为bug,因为问题是由一个函数返回float引起的,该函数表示它将返回int。JavaScript不像Java那样真正需要hashCode()函数,所以HashMapIterator的实现非常合理。但您必须确保从hashCode()函数返回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
}