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;
}
这可能看起来很奇怪而且没有必要,所以这里有一个更长的解释:
请注意 x
和 y
是 float
值,因此当您在 hash
计算中使用它们时,结果变为 float
作为出色地。您可以通过在 return 之前打印出 hash
的值来证明这一点。
Java 会抱怨这个。您可以通过简单地切换到 Java 模式并尝试 运行 您的程序来证明这一点。但是 JavaScript 对其类型没有那么严格,所以它会让你搬起石头砸自己的脚。我通常做的事情是在 Java 模式下编程以进行错误检查,然后使用 Java 脚本模式进行部署。
无论如何,在HashMap
class内部,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()
函数,所以 HashMap
和 Iterator
的实现是非常合理的。您只需要确保从 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
}
我在 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;
}
这可能看起来很奇怪而且没有必要,所以这里有一个更长的解释:
请注意 x
和 y
是 float
值,因此当您在 hash
计算中使用它们时,结果变为 float
作为出色地。您可以通过在 return 之前打印出 hash
的值来证明这一点。
Java 会抱怨这个。您可以通过简单地切换到 Java 模式并尝试 运行 您的程序来证明这一点。但是 JavaScript 对其类型没有那么严格,所以它会让你搬起石头砸自己的脚。我通常做的事情是在 Java 模式下编程以进行错误检查,然后使用 Java 脚本模式进行部署。
无论如何,在HashMap
class内部,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()
函数,所以 HashMap
和 Iterator
的实现是非常合理的。您只需要确保从 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
}