tutorials.de Buch-Aktion 05/2012
Like Tree1Danke
  • 1 Beitrag von Anime-Otaku
ERLEDIGT
NEIN
ANTWORTEN
12
ZUGRIFFE
912
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Sebastian G Tutorials.de Gastzugang
    Hallo,

    ich muss mich gerade zum ersten Mal mit groesseren Listen, deren Inhalt sich staendig aendert (hinzufuegen von Objekten am Ende, entfernen von Objekten irgendwo in der Liste).

    Wenn ich das auf meiner bisherigen Suche mit Google richtig verstanden habe unterscheiden sich die LinkedList und ArrayList wie folgt:

    Taetigkeit LinkedList ArrayList
    einfuegen/loeschen O(n) O(n)
    anfuegen/loeschen hinten O(1) O(1)
    anfuegen/loeschen vorne O(1) O(n)
    Indexzugriff O(n) O(1)
    suchen O(n) O(n)

    Da ich (wie oben beschrieben) hinten einfuegen und irgendwo loeschen will (ersten beiden Punkte meiner obigen Tabelle) und sich LinkedList und ArrayList hier in der Performance nicht unterscheiden, habe ich mich auf Grund des Vorteils der ArrayList beim Indexzugriff fuer die ArrayList entschieden.

    Bisher "laufe" ich durch die Liste mit einer normalen for-Schleife [for(int index = 0, ...)].

    Nun zu meinen zwei Fragen.

    1. Stimmt die oben genannte Tabelle so?
    2. Kann man einen Performancegewinn erzielen, wenn man Iterator oder for(Object elem : list) verwendet? Also z.B. die neue for-Schleife in Kombination mit der LinkedList ist schneller als ... (oder so aehnlich).



    Habe bisher noch keine Erfahrungen mit Iterator und der neuen for-Schleife. Im Web habe leider nur gefunden, dass das eine besser als das andere ist, aber nicht warum (Performance, Stil, Sicherheit, ...).


    Vielen Dank fuer Eure Muehe.


    Gruss
    Sebastian
     

  2. #2
    Dunas Dunas ist offline Mitglied Gold
    Registriert seit
    Mar 2007
    Beiträge
    158
    ja die liste stimmt

    zu deiner for schleife:
    durch das ändern der for-schleife in eine for( object a : liste) erhälst du keinen geschwindigkeits gewinn.
    das selbe ist auch bei einem itterator.

    wenn du etwas mehr zu der nutzung und zu der größe sagen könntest, könnte man sich das noch mal genauer ansehen und dir weitere tips geben.

    lg dunas
     

  3. #3
    Sebastian G Tutorials.de Gastzugang
    Danke fuer die schnelle Antwort.

    Zu Groesse: 10.000 - einige 100.000
    Zur Verwendung: Die Liste enthaelt Objekte, die sich duplizieren und vernichten koennen (oder nix machen). Daher werden staendig hinten an die Liste neue Objekte angehaengt und irgendwo in der Mitte welche geloescht. Mehr macht das Programm nicht. Von Interesse ist die Groesse der List nach n Durchlaeufen (also wie hat sich die Anzahl der Objekte nach n Durchlaeufen vergroessert / verkleinert).

    Da die Reihenfolge der Objekte keine Rolle spielt, habe ich gerade ein HashSet ausprobiert. War im Testlauf um ein Faktor 60 schneller als die ArrayList.
    Ist das der richtige Ansatz?


    Ich habe da noch ein anderes Problem. Ich lasse den oben beschriebenen Prozess m mal ablaufen (also noch eine for-Loop eine Ebene hoeher). Hierbei ist mir aufgefallen, dass sehr viel mehr Arbeitsspeicher verbraucht wird, als wenn ich den oben beschrieben Prozess nur einmal ablaufen lasse (ca. 200 MB gegen 1.4 GB). Ich rufe vor jedem neuen Schleifendurchlauf System.gc() auf, aber das scheint nicht viel zu helfen.
    Vielleicht einen Tipp hierzu?


    Nochmal Danke fuer die Antwort.


    Gruss
    Sebastian
     

  4. #4
    Avatar von zeja
    zeja zeja ist offline Mitglied Diamant
    tutorials.de Premium-User
    Registriert seit
    Sep 2006
    Beiträge
    2.962
    Ohne zu wissen wie deine Schleife aussieht kann ich zu dem Speicherproblem nicht viel sagen.

    Ein for-each verwendet intern übrigens einen Iterator. Ein Iterator ist im falle von ArrayList aber langsamer als ein indizierter Zugriff (zur Zeit... muss nicht für immer und ewig so bleiben).

    Schau dir mal die Javolution Collection Klassen an. Die könnten für deine Zwecke noch ein wenig schneller sein: http://javolution.org/
     

  5. #5
    Anime-Otaku Anime-Otaku ist offline Mitglied Brillant
    Registriert seit
    Aug 2005
    Ort
    Karlsruhe (Baden-Württemberg)
    Beiträge
    905
    Die O Liste stimmt so nicht ganz.

    Methode|ArrayList | LinkedList
    get() | O(1)| O(n)| Da er durchiterieren muss um die Posi zu finden
    add() | O(1)O(1)| ein einfaches anhängen
    contains()|O(n)|O(n)| durch iterieren
    next()|O(1)|O(1)| einfaches durchiterieren
    remove(0)|O(n)|O(1)| Eine ArrayList muss neu erstellt werden(zumindest teilweise), bei einer LinkedList müssen nur die Objektnachbarn angepasst werden
    iterator.remove|O(n)|O(1)| Beim iteriren muss bei der LinkedList nur die Nachbarn geändert werden


    Also wäre in deinem Fall eine LinkedList klar besser.
    Geändert von Anime-Otaku (20.08.09 um 08:21 Uhr)
     
    Wäre super wenn ihr euren Code in dieser Form einfügt:
    Code java:
    1
    
    System.out.println("Hello World");
    [java]System.out.println("Hello World");[/java]
    Für erledigte Threads dürft ihr den "erledigt"-Button anklicken!
    Über Dank freut sich jeder, der euch geholfen hat - ein Klick auf "Danke" kostet ja nicht mal was
    Blog: http://javaeffective.wordpress.com/

  6. #6
    Dunas Dunas ist offline Mitglied Gold
    Registriert seit
    Mar 2007
    Beiträge
    158
    Zitat Zitat von Anime-Otaku Beitrag anzeigen
    Die O Liste stimmt so nicht ganz.

    Methode|ArrayList | LinkedList
    get() | O(1)| O(n)| Da er durchiterieren muss um die Posi zu finden
    add() | O(1)O(1)| ein einfaches anhängen
    contains()|O(n)|O(n)| durch iterieren
    next()|O(1)|O(1)| einfaches durchiterieren
    remove(0)|O(n)|O(1)| Eine ArrayList muss neu erstellt werden(zumindest teilweise), bei einer LinkedList müssen nur die Objektnachbarn angepasst werden
    iterator.remove|O(n)|O(1)| Beim iteriren muss bei der LinkedList nur die Nachbarn geändert werden


    Also wäre in deinem Fall eine LinkedList klar besser.
    Bei einer Arraylist wird beim removen des ersten elements nichts neu erstellt. um das erste element in der liste zu löschen werden sicher nur die internen zeiger umgebogen. start zeigt dann nicht mehr auf den index 0 sondern auf index 1.

    beim removen mit dem iterator hast du recht. da passt der aufwand nicht wirklich.
    das element wird entfernt und alle elemente die danach stehen müssen eine stelle nach vorne kopiert werden.



    Zitat Zitat von Sebastian Gude Beitrag anzeigen

    Da die Reihenfolge der Objekte keine Rolle spielt, habe ich gerade ein HashSet ausprobiert. War im Testlauf um ein Faktor 60 schneller als die ArrayList.
    Ist das der richtige Ansatz?
    Das kann ich jetzt nicht nachvollziehen.
    Wenn du mit einer for(int x = 0; x < liste.sice; x++) über die ArrayListe gehst, solltest du keinen unterschied zwischen der Geschwindigkeit haben.

    Zitat Zitat von Sebastian Gude Beitrag anzeigen
    Ich habe da noch ein anderes Problem. Ich lasse den oben beschriebenen Prozess m mal ablaufen (also noch eine for-Loop eine Ebene hoeher). Hierbei ist mir aufgefallen, dass sehr viel mehr Arbeitsspeicher verbraucht wird, als wenn ich den oben beschrieben Prozess nur einmal ablaufen lasse (ca. 200 MB gegen 1.4 GB). Ich rufe vor jedem neuen Schleifendurchlauf System.gc() auf, aber das scheint nicht viel zu helfen.
    Vielleicht einen Tipp hierzu?
    verstehe ich das richtig, dass du deine Testobjekte erstellst. deine forschleife drüber rennen lässt, dann alles zerstörst, die testobjekte neu erstellst und wieder die forschleife drüber laufen läst?
    sollte das richtig implementiert sein, dürfte kein so großer unterscheid entstehen.
    Geändert von Dunas (20.08.09 um 11:28 Uhr)
     

  7. #7
    Anime-Otaku Anime-Otaku ist offline Mitglied Brillant
    Registriert seit
    Aug 2005
    Ort
    Karlsruhe (Baden-Württemberg)
    Beiträge
    905
    Zitat Zitat von Dunas Beitrag anzeigen
    Bei einer Arraylist wird beim removen des ersten elements nichts neu erstellt. um das erste element in der liste zu löschen werden sicher nur die internen zeiger umgebogen. start zeigt dann nicht mehr auf den index 0 sondern auf index 1.

    beim removen mit dem iterator hast du recht. da passt der aufwand nicht wirklich.
    das element wird entfernt und alle elemente die danach stehen müssen eine stelle nach vorne kopiert werden.
    Auszug aus der API:
    * Removes the element at the specified position in this list.
    * Shifts any subsequent elements to the left (subtracts one from their
    * indices).
    Und das macht er immer. Sieht man auch wenn man sich den Quellcode anschaut. Er kopiert alles was hinter dem Element kommt nach 1 nach vorne, danach wird das letzte Element des Arrays entfernt --> removes(0) ist am langsamsten und removes(size-1) am schnellsten.

    (Das Performance Problem hatten wir schon festgestellt, als immer remove(0) auf einer ArrayList ausgeführt wurde)
    Geändert von Anime-Otaku (20.08.09 um 11:31 Uhr)
     
    Wäre super wenn ihr euren Code in dieser Form einfügt:
    Code java:
    1
    
    System.out.println("Hello World");
    [java]System.out.println("Hello World");[/java]
    Für erledigte Threads dürft ihr den "erledigt"-Button anklicken!
    Über Dank freut sich jeder, der euch geholfen hat - ein Klick auf "Danke" kostet ja nicht mal was
    Blog: http://javaeffective.wordpress.com/

  8. #8
    Dunas Dunas ist offline Mitglied Gold
    Registriert seit
    Mar 2007
    Beiträge
    158
    Zitat Zitat von Anime-Otaku Beitrag anzeigen
    Auszug aus der API:


    Und das macht er immer. Sieht man auch wenn man sich den Quellcode anschaut. Er kopiert alles was hinter dem Element kommt nach 1 nach vorne, danach wird das letzte Element des Arrays entfernt --> removes(0) ist am langsamsten und removes(size-1) am schnellsten.

    (Das Performance Problem hatten wir schon festgestellt, als immer remove(0) auf einer ArrayList ausgeführt wurde)
    sorry. dann war ich falsch informiert. ja ist klar das dann remove(0) am langsamsten ist.
    ich sehe eigentlich keinen nachteil bei meiner version mit den zeigern. dann würde ich mal sagen kacke programmiert
     

  9. #9
    Sebastian G Tutorials.de Gastzugang
    Das kann ich jetzt nicht nachvollziehen.
    Wenn du mit einer for(int x = 0; x < liste.sice; x++) über die ArrayListe gehst, solltest du keinen unterschied zwischen der Geschwindigkeit haben.
    Hier meine Beispiel-Implementierung:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    public static void main(String[] args) {
            
            HashSet<Integer> collection = new HashSet<Integer>();
        //  ArrayList<Integer> collection = new ArrayList<Integer>();
            
            for (int index = 0; index < 100000; index++) {
                collection.add(index);
            }
            
            double before = System.currentTimeMillis();
            for (int index = 0; index < 10000; index++) {
                collection.remove(new Integer(index*4));
            }
            double after = System.currentTimeMillis();
            
            System.out.println(after - before);
     
        }

    Wenn ich das Hash-Set benutze kriege ich Zeiten von 8 - 15 mill sec, wenn ich dagegen die ArrayList verwende kriege ich Zeiten von ca. 1800 mill sec. Also die ArrayList ist um einen Faktor 200 langsamer.


    verstehe ich das richtig, dass du deine Testobjekte erstellst. deine forschleife drüber rennen lässt, dann alles zerstörst, die testobjekte neu erstellst und wieder die forschleife drüber laufen läst?
    sollte das richtig implementiert sein, dürfte kein so großer unterscheid entstehen.
    Ja, das verstehst du richtig. Mein Code sieht wie folgt aus:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    for(int i = 0; i < m; i++) {
          System.gc();
         //population enthaelt als Klassenvarivable die Collection
         Population population = new Population();
         for (int j = 0; j < n; j++) {
              //in diesem Schritt werden Objekte an die Collection angehaengt bzw. geloescht
              poplation.simulateNextTimeStep();
          }
          //hier wird noch in eine Datei geschrieben (BufferedWriter, flush() wird gerufen)
    }


    Gruss
    Sebastian
     

  10. #10
    Anime-Otaku Anime-Otaku ist offline Mitglied Brillant
    Registriert seit
    Aug 2005
    Ort
    Karlsruhe (Baden-Württemberg)
    Beiträge
    905
    Das System.gc() sagt nicht explizit aus, dass der GC sofort alle alten Referenzen frei gibt. Viel mehr wird dem GC mitgeteilt, seine GC Anstrengen zu erhöhen. Aber genau bin ich da auch noch nicht schlau draus geworden.

    http://java.sun.com/javase/6/docs/ap...ystem.html#gc()

    Beim HashSet muss dir klar sein, dass der Hash-Wert deines Objektes als Key verwendet wird. Und das dieser Key eindeutig ist in deinem Set. Daher überschreibst du die Werte, wenn der Hash Wert gleich ist.

    Der Hash-Wert wird durch die hashCode Methode des Objektes erzeugt. Wenn das Objekt keine hashCode Methode hat (wird die von Object verwendet), wird die SpeicherAdresse der Instanz als Hash Wert verwendet.
    Geändert von Anime-Otaku (20.08.09 um 13:00 Uhr)
     
    Wäre super wenn ihr euren Code in dieser Form einfügt:
    Code java:
    1
    
    System.out.println("Hello World");
    [java]System.out.println("Hello World");[/java]
    Für erledigte Threads dürft ihr den "erledigt"-Button anklicken!
    Über Dank freut sich jeder, der euch geholfen hat - ein Klick auf "Danke" kostet ja nicht mal was
    Blog: http://javaeffective.wordpress.com/

  11. #11
    Dunas Dunas ist offline Mitglied Gold
    Registriert seit
    Mar 2007
    Beiträge
    158
    Zitat Zitat von Sebastian Gude Beitrag anzeigen
    Hier meine Beispiel-Implementierung:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    public static void main(String[] args) {
            
            HashSet<Integer> collection = new HashSet<Integer>();
        //  ArrayList<Integer> collection = new ArrayList<Integer>();
            
            for (int index = 0; index < 100000; index++) {
                collection.add(index);
            }
            
            double before = System.currentTimeMillis();
            for (int index = 0; index < 10000; index++) {
                collection.remove(new Integer(index*4));
            }
            double after = System.currentTimeMillis();
            
            System.out.println(after - before);
     
        }

    Wenn ich das Hash-Set benutze kriege ich Zeiten von 8 - 15 mill sec, wenn ich dagegen die ArrayList verwende kriege ich Zeiten von ca. 1800 mill sec. Also die ArrayList ist um einen Faktor 200 langsamer.
    ja das ist nach dem post 2 oben drüber klar. bei removen aus der array list wird ja noch alles was dahinter ist eine stelle nach vorne geschoben.


    Zitat Zitat von Sebastian Gude Beitrag anzeigen
    Ja, das verstehst du richtig. Mein Code sieht wie folgt aus:

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    for(int i = 0; i < m; i++) {
          System.gc();
         //population enthaelt als Klassenvarivable die Collection
         Population population = new Population();
         for (int j = 0; j < n; j++) {
              //in diesem Schritt werden Objekte an die Collection angehaengt bzw. geloescht
              poplation.simulateNextTimeStep();
          }
          //hier wird noch in eine Datei geschrieben (BufferedWriter, flush() wird gerufen)
    }
    hier kann ich jetzt nichts wildes entdecken.
    vieleicht solltest du nacher alle variablen mal auf null setzen. und nachdem du System.gc() aufrufst nen sleep von 1 sekunde einbaust. dann hat der gb etwas zeit.
    // edit: ok wird wohl nicht viel bringen den threat ne sekunde schlafen zu lassen. aber versuchen kannst du es ja mal.
    Geändert von Dunas (20.08.09 um 13:27 Uhr)
     

  12. #12
    Sebastian G Tutorials.de Gastzugang
    Zitat Zitat von Anime-Otaku Beitrag anzeigen
    Das System.gc() sagt nicht explizit aus, dass der GC sofort alle alten Referenzen frei gibt. Viel mehr wird dem GC mitgeteilt, seine GC Anstrengen zu erhöhen. Aber genau bin ich da auch noch nicht schlau draus geworden.

    http://java.sun.com/javase/6/docs/ap...ystem.html#gc()

    Beim HashSet muss dir klar sein, dass der Hash-Wert deines Objektes als Key verwendet wird. Und das dieser Key eindeutig ist in deinem Set. Daher überschreibst du die Werte, wenn der Hash Wert gleich ist.

    Der Hash-Wert wird durch die hashCode Methode des Objektes erzeugt. Wenn das Objekt keine hashCode Methode hat (wird die von Object verwendet), wird die SpeicherAdresse der Instanz als Hash Wert verwendet.

    Wenn mein Objekt keine eigene hashCode Methode hat, wird die Speicheradresse der Instanz verwendet. D.h. also wenn ich die Objekte nacheinander inintialisiere und dann dem HashSet hinzufuege, sind alle drin (keiner wird ueberschrieben). Nur wenn ich eine Instanz doppelt hinzufuege wird sie ueberschrieben. Hab ich das richtig verstanden? Also selbst wenn der Inhalt der Instanzen identisch ist, ist der HashCode (auf Grund der unterschiedlichen Speicheradresse der Instanzen) verschieden.



    Das Problem mit dem Speicherplatz (Einzeldurchlauf vs. for-Loop) hat sich geloest. Hat an einem anderen Programmteil gelegen. Sorry.


    Gruss
    Sebastian
     

  13. #13
    Anime-Otaku Anime-Otaku ist offline Mitglied Brillant
    Registriert seit
    Aug 2005
    Ort
    Karlsruhe (Baden-Württemberg)
    Beiträge
    905
    Zitat Zitat von Sebastian Gude Beitrag anzeigen
    Wenn mein Objekt keine eigene hashCode Methode hat, wird die Speicheradresse der Instanz verwendet. D.h. also wenn ich die Objekte nacheinander inintialisiere und dann dem HashSet hinzufuege, sind alle drin (keiner wird ueberschrieben). Nur wenn ich eine Instanz doppelt hinzufuege wird sie ueberschrieben. Hab ich das richtig verstanden? Also selbst wenn der Inhalt der Instanzen identisch ist, ist der HashCode (auf Grund der unterschiedlichen Speicheradresse der Instanzen) verschieden.
    Gruss
    Sebastian
    Ja hast du, weil dann die hashcode Methode von Object verwendet wird.
    Sebastian G bedankt sich. 
    Wäre super wenn ihr euren Code in dieser Form einfügt:
    Code java:
    1
    
    System.out.println("Hello World");
    [java]System.out.println("Hello World");[/java]
    Für erledigte Threads dürft ihr den "erledigt"-Button anklicken!
    Über Dank freut sich jeder, der euch geholfen hat - ein Klick auf "Danke" kostet ja nicht mal was
    Blog: http://javaeffective.wordpress.com/

Ähnliche Themen

  1. Iteration in c4d
    Von rown im Forum Cinema 4D
    Antworten: 6
    Letzter Beitrag: 17.09.10, 14:54
  2. Rekursion und Iteration
    Von DarkSean im Forum Delphi, Kylix, Pascal
    Antworten: 6
    Letzter Beitrag: 17.12.09, 12:42
  3. Xpresso > iteration
    Von digital art im Forum Cinema 4D
    Antworten: 2
    Letzter Beitrag: 03.02.09, 00:41
  4. HashMap-Iteration
    Von AvS im Forum Algorithmen & Datenstrukturen mit Java
    Antworten: 5
    Letzter Beitrag: 15.05.08, 23:01
  5. DLL Listen- Absturz, Listen übergeben
    Von haemmer im Forum C/C++
    Antworten: 0
    Letzter Beitrag: 05.02.04, 21:00

Stichworte