BestPractice - über mehrdimensionale arrays iterieren

Hi.

Vielleicht ist es ziemlich subjektiv - vielleicht aber auch nicht… ich frage einfach Mal.

Ich habe hier etwas ganz übliches, eine Welt und ein 2d Raster (welches irgendwann theoretisch n-dimensional werden könnte).

Nun muss ich an verschiedenen Stellen im Code über jedes Feld iterieren - üblicherweise macht man da eben n - verschachtelte Schleifen und gut ist. Aber irgendwie… sieht das doof aus. Ich habe nun überlegt ob man das nicht irgendwie schöner machen könnte… Hab nen Iterator zu diesem Zweck geschrieben, aber moment Mal - teilweise brauche ich ja die Koordinaten, an denen ich mich gerade befinde… was haltet ihr denn von Iteratorn, die extra methdoden wie getCurrentXyz oder so bereitstellen?

Man könnte ja auch eine Methode schreiben, die eine Interface Implementierung ala doSth(Field f) erwartet und dann durchs Array iteriert und die Methode aufruft? Aber ich denke da gibt es dann sofort Probleme mit Sichtbarkeit und was weiß ich nicht was…

Um geschwindigkeit geht es mir momentan nicht. Also falls ihr irgendwelchen schönen Vorschläge habt, raus damit :wink:

Schönen Abend noch ^^

Eindimensionale Arrays mit der Größe heightwidth machen, das ist zumindest bei sonst zweidimensionalen Arrays noch recht einfach:
Ein Element in einem solchen Array mit gegebenen x- und y-Werten wird per x
height+y adressiert und aus einem Index bekommt man x mit x=index/height und y mit y=index%height.

Und was wenn ich dann die Beziehungen links rechts oben unten brauche? Den einzelnen Objekten verweise verpassen? Oder ständig umrechnen?

Die Umrechnung kann transparent sein. Es gibt ein Interface wie

interface IntArray2D {
    int get(int x, int y);
}

und ob man die get-Methode nun als

return array[x][y];

implementiert, oder als

return array[x+y*width];

ist egal.

Oder… als

return array[x*height+y];

Das ist nämlich dann gleich die Frage, die sich dabei stellt :smiley: Row-major oder column-major?

Wie man das allgemeiner, für mehrere Dimensionen anbieten kann … kommt ein bißchen darauf an, was genau damit gemacht werden soll. Ich hatte ja mal irgendwann GitHub - javagl/ND: Multidimensional primitive data structures gebastelt, wo sich bei der Iteration eine ähnliche Frage stellt (genagenommen liegt das in ND/nd-iteration at master · javagl/ND · GitHub ). Dort wird ein N-Dimensionaler Array jetzt eben über ein IntTuple addressiert. Wenn man es etwas rudimentärer will, könnte man auch einen int[]-Array verwenden (viele ND-Matrix-Libs machen das). Man könnte also sowas machen wie

interface ArrayND<T> {
    T get(int ... coordinates);
}

Man könnte es auch kombinieren, wie du angedeutet hast - ganz grob im Sinne von

interface IteratorND<T> extends Iterator<T> {
    int[] getCurrentCoordinates();
}

Aber ich denke, dass es dann trotzdem noch eine Methode geben müßte, mit der man mit so einem int[] auf den passenden Array-Eintrag zugreifen kann, deswegen würde das nicht sooo viel bringen.

BTW: Ich bin den umgekehrten Weg gegangen: Es gibt einen Iterator bzw. Stream über alle Koordinaten (also eine Menge von IntTuple Instanzen), und die Iteration über die Werte ist dann ziemlich trivial:

/**
 * Returns a sequential {@code DoubleStream} with this array as its source.
 * 
 * @return The stream
 */
default DoubleStream stream()
{
    return coordinates().mapToDouble(t -> get(t));

}

Landei hatte doch mal einen Blog, in dem er alle Möglichkeiten der Iteration aufzählte.

Einerseits gilt das Subsidiaritätsprinzip, anderseits hat es ein Elem nicht zu interessieren, was in seiner Umgebung liegt.

( Andererseits wollte ich doch gar nicht in fremde Themen schreiben. :wink: Bitte nicht hauen :wink: schade, kein OT-Tag mehr )

Okay danke für eure Anregungen. ich überlege mal, wie ich das umsetze… das Problem ist nur, dass ich noch nicht ganz genau weiss was ich eventuell alles brauchen könnte (innerhalb von iterationen) und es wäre doof alles umbauen zu müssen weil ich merke ahh mist mit der einschränkung komm ich hier nicht weiter.

Aber mal sehen. danke euch :slight_smile:

Ich verweise mal auf Map aus dem JDK, dass hier ein vergleichbares Problem hat.

Map hat verschiedene Möglichkeiten Iterables zurückzuliefern um eine Iteration via Iterator zu ermöglichen.

  1. values(), allerdings gehen dann die Keys verloren.
  2. keySet(), liefert die Keys, womit man dann wieder die values aus der map holen muss map.get(key);
  3. entrySet(), welches ein Set<Map.Entry> zurückliefert, auf dem man dann getKey() und getValue() aufrufen kann.

Auch wenn jetzt Steine fliegen, ein mehrdimensionales Array kann man auch als Map darstellen und somit z.B. auch auf die Variante entrySet() gehen. Man könnte auch auf array bleiben und einen Iterator erstellen, der Entry zurückgibt.

Damit könnte man also einen schnöden Iterator<Entry<C, T>> erstellen mit

interface Entry<C extends Coordinates,T> {
  T getValue();
  C getCoordinates();
}

Und Coordinates für 1D, 2D, 3D, 4D, etc.

Also meine Steine sind’s sicher nicht.
Man KANN es halt auch so machen… stimmt.
Danke für den Ansatz.

Edit: Wenn wir schon mal dabei sind, wäre es verwerflich an verschiedenen Stellen
verschiedene Iterationsmethoden zu verwenden? Ich denke nicht, oder?
Macht man doch auch sonst wo mit Array-Lists zum Beispiel