Java Wie Code mit mehrdimensionalen Arrays optimieren?

MrOhlson

Cadet 3rd Year
Registriert
Okt. 2015
Beiträge
49
Hallo,

nehmen wir an ich hätte folgenden Code:

Code:
public class Wald{

    private Baum[][] wald;
    ...
  
    public void sucheBaum(Baum baum){
        for(int i=0; i<wald.length; i++){
            for(int j=0; j<wald[i].length; j++){
                if(...){
                    ...
                }
            }
        }
    }
  
    public void pflanzeNeueBaeume(){
        for(int i=0; i<wald.length; i++){
            for(int j=0; j<wald[i].length; j++){
                wald[i][j] = new Baum ("Ahorn");
                ...
            }
        }
    }
  
    ...
}

Gehen wir nun davon aus, dass ich noch weiter Methoden nach diesem Schema hätte. Was mich nun allerdings stört ist der duplizierte Code. Jede Methode beginnt mit den for-Schleifen, die das Array durchlaufen. Meine Frage ist nun, ob ich das durchlaufen der for-Schleifen sinnvoll auslagern kann, so dass ich in der ganzen Klasse nur insgesamt zweimal das Wort "for" benutzen müsste?
 
Zuletzt bearbeitet:
Was immer geht, ist pro for und if eine eigene Methode mit Namen die beschreibt was da gemacht wird. Wenn das ganze einmal in kleine Stücke aufgespalten wurde, lässt sich die Methode recyceln.

Alternativ schreibst die for Schleifen in eine eigene Klasse (vielleicht sogar einen Iterator für Typ Array?). In der Methode getTree(String tree) kommt das for rein. Am Ende benutzt es wie wald.getTree("Ahorn").

Wie man das auf der Arbeit macht...einfach Libs einbinden die solchen basic Kram erledigen: https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/ArrayUtils.html Musst halt mal Googlen was es da für 2D Arrays gibt. Normal versucht man die Dinger zu vermeiden wo es geht, da den Code irgendwann keiner mehr versteht.

Datenbank Style:
Da ein 2d Array eine Tabelle ist die irgendwann mal befüllt werden muss, kannst du parallel dazu gleich eine Art Index aufbauen, um ohne Iteration zugreifen zu können, ein Hash z.B.
 
Zuletzt bearbeitet von einem Moderator:
  • Gefällt mir
Reaktionen: MrOhlson
Du könntest den Code in der Schleife in Lambdas auslagern und dann Eine Funktion schreiben, die das Lambda entgegennimmt und bei jedem Schleifendurchlauf ausführt.
Ergänzung ()

rob- schrieb:
Was immer geht, ist pro for eine eigene Methode mit Namen die beschreibt was da gemacht wird.
Da ein 2d Array eine Tabelle ist die irgendwann mal befüllt werden muss, kannst du parallel dazu gleich eine Art Index aufbauen um ohne Iteration zugreifen zu können, ein Hash z.B.
Ist aber nur sinnvoll, wenn nur auf kleine Teile zugegriffen wird, und die gesuchte Eigenschaft konstant ist, bis der Schleifendurchlauf durchgeführt wird (was bei der ersten Methode vermutlich tatsächlich gegeben sein dürfte).
Das ist jedoch nur Eine Performanzoptimieren, welche wahrscheinlich nicht benötigt wird ;)
 
  • Gefällt mir
Reaktionen: MrOhlson
Jup, zu viele Einzelteile sollte man auch nicht haben die an etlichen Stellen verwendet werden. Späteres umbauen hat dann massenweise unerwünschte Seiteneffekte. Am besten Code pro Feature des Programms, darin dann auch gerne doppelten Code.
 
new Account() schrieb:
Du könntest den Code in der Schleife in Lambdas auslagern und dann Eine Funktion schreiben, die das Lambda entgegennimmt und bei jedem Schleifendurchlauf ausführt.

Das scheint eventuell genau das zu sein, dass ich Suche, allerdings kenne ich mich gar nicht mit Lambdas aus. Ich hätte halt gerne eine universal Methode die immer die Schleifen durchläuft und dort drinnen ausführt, was ich der Methode übergebe. Aber ich weiß nicht, ob dies Möglich ist. In Code ausgedrückt hätte ich gerne so etwas ähnliches, wenn dies irgendwie möglich ist:

Java:
private class Wald{

    ...
    public void baumFinden(pBaum){
        durchlaufeArray(sucheBaum(pBaum))
    }
    
    private void sucheBaum(pBaum){
        if(wald[i][j] = baum){
            System.out.println("Gefunden")
        }
    }
    
    private neueBaeumeErstellen(){
        wald[i][j] = new Baum("Ahorn");
    }
    
    public void erstelleNeueBaeume(){
        durchlaufeArray(neueBaeumeErstellen())
    }
    
    private void durchlaufeArray(Methode fuehreAktionAus){
        for(int i=0; i<wald.length; i++){
            for(int j=0; j<wald[i].length; j++){
                fuehreAktionAus;
            }
        }
    }

}
 
Zuletzt bearbeitet:
Du kannst es auch mittels eines interface erledigen.
Erstellst ein Interface mit einer oder mehrerer Methoden, erzeugst dir dann fuer jede deiner "Aktion" eine passende Klasse.

Und uebergibst deiner Free oder privaten Funktion diese Interface instance.
Diese ruft dann fuer jedes Objekt die aktion auf.

Um einen durchlauf dann abzubrechen kannst du entweder nen Bool zurueckgeben und abfragen (langsam da if) oder mit einer Dummy Exception reagieren und dadurch auch die schleife abbrechen.


Alternativ kannst du auch das 2D array durch einen Vector ersetzen und dann die forEach funktion verwenden.
Bspw. siehe hier: Link
 
  • Gefällt mir
Reaktionen: MrOhlson
Aaskarioth schrieb:
Erstellst ein Interface mit einer oder mehrerer Methoden, erzeugst dir dann fuer jede deiner "Aktion" eine passende Klasse.

Ich verstehe zwar was du meinst, weiß jetzt allerdings nicht, wie ich das konkret umsetzen muss. Danke für den Link ich schau es mir mal an.
 
https://stackoverflow.com/questions...which-takes-a-lambda-as-a-parameter-in-java-8

Hier, wie das mit den Lambdas funktionieren würde. Die Parameter des Lambdas/Interfaces müsste man entsprechend wählen. Wenn du das nicht verstehst, kannst du einfach mal ein Tutorial dazu durchgehen.

Der Ansatz mit einer Klasse ist irgendwie nett gedacht, aber irgendwie unnötig kompliziert, weil man die Methode nur hinter einer Klasse versteckt?
 
  • Gefällt mir
Reaktionen: MrOhlson
MrOhlson schrieb:
Ich verstehe zwar was du meinst, weiß jetzt allerdings nicht, wie ich das konkret umsetzen muss. Danke für den Link ich schau es mir mal an.

Eigentlich recht simple, aber sehr aufwendig und nicht zu empfehlen, lambdas sind deutlisch schneller und einfacher.
Du bildest im ansatz das nach was der Compiler/Jvm/Jit sonst fuer dich erledigt.


Mal als pseudo code:
Java:
interface FunctionObj
{
  public void execute( Baum b );
}

class SearchBaum implements FunctionObj
{
  Baum baum = null;
  // Irgendwas SearchCrit
 
  SearchBaum( /*irgendwas*/ )
  {
     // SearchCrit = ....
  }
 
  public Baum get_baum()
  {
    return ( baum );
  }
 
  void execute( Baum b )
  {
    if( b == SearchCrit )
    {
      baum = b;
    }
  }
}

// in deinem wald
private void execute( FunctionObj obj )
{
  for( Baum[] tree_list : Wald )
  {
    for( Baum tree : tree_list )
    {
      obj(tree);
    }
  }
}

public Baum suche_baum( /*crit*/ )
{
  SearchBaum instance(/*crit*/);
  execute( instance );
 
  if( instance.get_baum() != null )
  {
    // gefunden
   // baum zurueck geben
  }
}
 
Ab wann lohnt es sich denn für solche Probleme anzufangen Code zu deduplizieren und dem Compiler / JVM und deren verfügbaren Optimierungen entgegenzuwirken? Irgendwann hat man durch Interfaces, implementierende Klassen und am Ende gar Factories mehr Unübersichtlichkeit als Nutzen geschaffen.

Wenn es wirklich nur darum geht, Wege zu finden Code "eleganter" zu verpacken mögen solche esoterischen Problemstellungen helfen. Ansonsten kann das schnell in so etwas enden https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition :).
 
JP-M schrieb:
Ab wann lohnt es sich denn für solche Probleme anzufangen Code zu deduplizieren und dem Compiler / JVM und deren verfügbaren Optimierungen entgegenzuwirken? Irgendwann hat man durch Interfaces, implementierende Klassen und am Ende gar Factories mehr Unübersichtlichkeit als Nutzen geschaffen.

Wenn es wirklich nur darum geht, Wege zu finden Code "eleganter" zu verpacken mögen solche esoterischen Problemstellungen helfen. Ansonsten kann das schnell in so etwas enden https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition:).
Eigentlich lohnt es sich je komplexer es wird umso mehr. Natürlich musst du es richtig machen. Zum Beispiel durch die Verwendung von Design Patterns.

greetz
hroessler
 
Java:
public class Example {

    public interface TreeFunction<A, B> {
        public void execute(A a, B b);
    }

    private Tree[][] forest = new Tree[10][10];

    public void plantForest() {

        iterate((Integer x, Integer y) -> {
            forest[x][y] = new Tree("Ahorn");
        });
    }


    public void searchTree(Tree tree) {

        iterate((Integer x, Integer y) -> {
            if (forest[x][y].equals(tree)) {
                // do something with the Tree you just found...
            }
        });
    }


    private void iterate(TreeFunction<Integer, Integer> function) {

        for (int x = 0; x < forest.length; x++) {
            for (int y = 0; y < forest[x].length; y++) {
                function.execute(x, y);
            }
        }
    }
}

Meinst du sowas?
 
  • Gefällt mir
Reaktionen: new Account()
Ich glaub ihr seht den Wald vor lauter Bäumen nicht mehr. :evillol:
Man muss doch nicht dem Baum im Wald suchen wenn man das Objekt schon hat, die Funktionen sind ja wohl am Baum (oder Mutterobjekt(en)) angebracht, wenn man den Index in dem 2D-Array sucht, warum nicht einfach die Koordinaten im Konstruktor mitgeben und in Entsprechenden Variablen im Baum speichern? :cool_alt:

Edit: Kleines Beispiel:
Code:
// Wald erstellen
for(int i=0; i<wald.length; i++)
    for(int j=0; j<wald[i].length; j++)
        wald[i][j] = new Baum("Ahorn", i, j);
 
Zurück
Oben