2016-04-07 20 views
2

Ich habe Probleme beim Iterieren über eine HashMap in Processing.js. Beim Aufruf des Iterators while (it.hasNext()) wird nie eingegeben.Processing.js HashMap

Als Plausibilitätsprüfung habe ich versucht, den Iterator zu überspringen und stattdessen die Schlüssel in ein Array zu konvertieren und über diese nach Index zu iterieren. Das hat auch nicht funktioniert. Ich habe folgendes ausgedruckt:

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

Ich würde erwarten, dass die letzte Zeile auch 4 ausgibt, wie sie vorherige Aufrufe getan haben. Verstehe ich etwas falsch? Vielen Dank!


EDIT

Hier ist ein vollständiges Beispiel des Problems ich in lief. Es stellte sich heraus, dass es ein Problem bei der Verwendung eines Floats in meinem HashCode ist, obwohl ich immer noch nicht verstehe, warum dies bei der Konvertierung von keySet in ein Array zu einem Missverhältnis zwischen den Elementen führt.

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 
+0

Bitte senden Sie ein [MCVE], die wir zeigt genau den Code Sie ausführen. Beachten Sie, dass dies nur ein paar Zeilen sein sollte, gerade genug für uns zum Kopieren und Einfügen, um die gleichen Ergebnisse zu erhalten, aber nicht Ihre ganze Skizze! –

+0

Dieser Code funktioniert gut für mich: 'HashMap map = neue HashMap (); map.put ("eins", "A"); map.put ("zwei", "B"); map.put ("drei", "C"); println (map.size()); println (map.keySet(). ToArray(). Length); ' –

+0

Ah, danke Kevin! Entschuldigung für das unvollständige Beispiel. Deine Antwort hat mich dazu gebracht, auf niedrigerer Ebene zu debuggen. Mein Problem stellte sich heraus, dass die Schlüssel in meiner HashMap Objekte waren, und der HashCode, den ich für diese Objekte erstellt hatte, scheiterte aufgrund einer falschen Verwendung eines Floats. Ich verstehe immer noch nicht, warum die Aufrufe von keySet(). Size() und keySet() .toArray(). Length zu unterschiedlichen Werten führten. Ich poste ein aktuelles Codebeispiel mit dem, was mir wirklich in den Sinn gekommen ist, falls jemand interessiert ist. Danke noch einmal! – megabits

Antwort

1

Ich denke, ich habe es endlich herausgefunden. Das hat Kopfkratzen gekostet. Gute Frage.

Moral der Geschichte: Ich denke, Sie sind in ein bisschen Verrücktheit mit der hashCode() Funktion gestolpert. Die hashCode() Funktion muss einen int Wert zurückgeben, aber Sie geben einen float Wert zurück.

Um Ihr Problem zu beheben, werfen Sie einfach den hash Wert in eine int vor der Rückgabe!

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

Das mag seltsam und unnötig erscheinen, also hier eine längere Erklärung:

Beachten Sie, dass x und y sind float Werte, so dass, wenn Sie sie in Ihrer hash Berechnungen verwenden, wird das Ergebnis eine float als Gut. Sie können dies nachweisen, indem Sie den Wert hash vor der Rückgabe ausdrucken.

Java würde sich darüber beschweren. Sie können dies nachweisen, indem Sie einfach in den Java-Modus wechseln und versuchen, Ihr Programm auszuführen. Aber JavaScript ist nicht so streng mit seinen Typen, so dass Sie sich in den Fuß schießen können. Etwas, was ich normalerweise mache, ist im Java-Modus zu programmieren, um seine Fehlerprüfung zu erhalten, und dann im JavaScript-Modus zu implementieren.

Wie auch immer, innerhalb der HashMap Klasse wird das Ergebnis der hashCode() Funktion schließlich als ein Index in einem Array verwendet. Sie können in der Processing.js Quelldatei, die sie sehen:

//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 
} 

Und das könnte in Ordnung sein, denn seltsamerweise JavaScript mit Dezimalstellen in Array-Indizes in Ordnung ist. Aber das Problem ist die Implementierung von HashSet'sIterator:

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 

Check-out, dass findNext() Funktion. Es durchläuft die Indizes des Arrays, wird aber jedes Mal um eins erhöht. Daher werden alle Schlüssel, die in Dezimalstellenindizes eingefügt werden, übersprungen!

Aus diesem Grund überspringt Ihr Iterator eines Ihrer Objekte (das mit einer Dezimalstelle in dem von hashCode() zurückgegebenen Wert). Und deshalb schlägt auch toArray() fehl, da diese Funktion eine Iterator unter der Haube verwendet.

Ich würde dies nicht wirklich einen Fehler nennen, da das Problem durch die Rückgabe eines float von einer Funktion verursacht wird, die besagt, dass es eine int zurückgibt. JavaScript benötigt nicht wirklich hashCode() funktioniert auf die gleiche Weise wie Java, so dass diese Implementierung von HashMap und Iterator ziemlich vernünftig ist. Sie müssen nur sicherstellen, dass Sie eine int aus der hashCode() Funktion zurückgeben.

By the way, hier ist ein kleines Beispiel, wenn Sie wie das Spiel umhertasten:

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 
} 
+1

Wow, super! Das hat einiges nach unten gebracht :) Hätte nie gedacht, aber das Inkrementieren basierend auf einem Integer-Index macht natürlich Sinn. Danke für den Vorschlag zur Entwicklung im Java-Modus, definitiv dafür. Danke, dass du das Mysterium gelöst hast, und je mehr du weißt! – megabits