# TreeSet ändert sich bei Änderungen nicht!



## gpxricky (22. Mai 2005)

Hallo,

in meiner Klasse benutze ich einen TreeSet, der nach einem bestimmten Comparator sortiert.
Nehmen wir zur Vereinfach einmal folgendes Beispiel:


```
class Test {
    public TreeSet trees = new TreeSet();
    ...
}

class TreeSetElement implements Comparable {
    private GregorianCalendar date = null;

    public TreeSetElement() {
        date = new GregorianCalendar();
    }

    public changeDate() {
        date = new GregorianCalendar(2005, 12, 12, 12, 12, 12);
    }

    public int compareTo(Object o) {
        // Sortiert korrekt nach TreeSetElement.date
    }
}
```

Bis hierher ist alles schön und gut.
Wenn ich meine Klasse Test habe und dann dem TreeSet immer wieder ein neues TreeSetElement "draufhaue", sortiert er das Element richtig ein!

Das Problem tritt nur auf, wenn ich nun ein bereits im TreeSet vorhandenes TreeSetElement mit changeDate() ändere: Anstatt dieses Element neu einzusortieren, bleibt es an der selben Stelle :-(((!!
Das kann aber nicht sein, es muss (wenn changeDate wirklich wie oben implementiert ist), an das Ende des TreeSets rutschen!

Gibt es irgendwie ne Möglichkeit, dem TreeSet zu sagen, er solle sich neu sortieren, ohne, dass ich das geänderte Element zunächst aus dem TreeSet lösche und dann wieder einfüge?

Vielen Dank für Eure Hilfe

gpxricky


----------



## gpxricky (22. Mai 2005)

Wobei witzigerweise TreeSet.contains() auch false zurückgibt, wenn man durch alle Elemente durchiteriert und an das geändert kommt. Komisch, komisch....


----------



## bygones (22. Mai 2005)

nicht komisch - das Objekt hat sich geändert - die contains beruht auf der equals Methode und wenn die bei der änderung dann false zurückgibt.

dir wird afaik nichts anderes übrige bleiben als

```
Set s = new TreeSet(oldSet);
```


----------



## gpxricky (22. Mai 2005)

Nein, das funktioniert auch nicht :-(.
Das komische ist, dass ich den TreeSet durchlese (mit Iterator), dann jedes Element nehme und schaue, ob es in TreeSet drin ist (eigentlich logisch, dass dann alles drin sein sollte), aber er bringt, dass das Element nicht drin sei.


----------



## Wildcard (22. Mai 2005)

Dann hast du equals oder compareTo falsch überschrieben...


----------



## gpxricky (22. Mai 2005)

Also jetzt wirds noch komischer: Wenn nicht viele Elemente im TreeSet drin sind, dann funktioniert das removen und dann wieder adden! Ich schau mal, ab wie viel Elementen es nicht mehr funktioniert und meld mich gleich wieder...


----------



## mic_checker (22. Mai 2005)

Ansonsten musst du halt mal deinen kompletten Source posten

Hab nämlich keine Lust mir die Extension von JProphet runterzuladen....*g*


----------



## gpxricky (22. Mai 2005)

*g* naja, den ganzen Quellcode möchte ich nicht posten. Denke mal, ihr habt keine Lust 1500 Zeilen Code durchzulesen, oder?

Also: Folgendermaßen hats jetzt funktioniert, allerdings ist das mächtig mächtig aufwendig, denn ich kopiere jetzt manuell alles vom TreeSet (und die Elemente können ja auch wieder nen TreeSet haben und die dann auch wieder, und und und) neu in einen anderen TreeSet rüber. Das ist meiner Meinung irgendwie ziemlich absurd, aber ich habe keine Ahnung, warum das andere nicht funktioniert.


----------



## Wildcard (22. Mai 2005)

Wenn du ein Date ändern willst, und das TreeSet danach noch sortiert sein soll musst du das von aussen machen:

```
treeSet.remove(treeSetElement);
treeSetElement.machwas();
treeSet.add(treeSetElement);
```
Wenn du jetzt noch equals und compareTo richtig überschrieben hast passt das...


----------



## gpxricky (22. Mai 2005)

Ok, also ich hab jetzt mal ein Beispiel ;-):

Ich habe den Code nicht kommentiert und hoffe, dass er selbsterklärend ist (Kommentare mussten aus Zeitgründen ausbleiben - will schließlich auch irgendwann ins Bett):


```
package forum;
import java.util.*;

public class Testklasse implements Comparable {
  public TreeSet tasks = null;
  private GregorianCalendar date = null;
  private String name = null;
  
  public Testklasse(String text) {
    date = new GregorianCalendar();
    tasks = new TreeSet();
    name = text;
  }
  
  public GregorianCalendar getDate() {
    return date;
  }
  
  public String getName() {
    return name;
  }
  
  public void setDate(GregorianCalendar datum) {
    date = datum;
  }
  
  public Testklasse addNew(String text) {
    Testklasse test = new Testklasse(text);
    this.tasks.add(test);
    return test;
  }

  /**
   * compareTo
   *
   * @param o Object
   * @return int
   */
  public int compareTo(Object o) {
    Testklasse test = (Testklasse)o;
    if (date.after(test.getDate())) {
      return 1;
    } else if (date.before(test.getDate())) {
      return -1;
    } else {
      return name.compareTo(test.getName());
    }
  }
}

****************************************************

package forum;
import java.util.*;

public class Forum {

  public static void main(String args[]) {
    Testklasse changer = null;
    int id = 0;
    Testklasse go = new Testklasse("A" + Integer.toString(id));
    Testklasse sicherung = null;
    while (id < 20) {
      id++;
      Testklasse neu = go.addNew("A" + Integer.toString(id));
      if (id == 2) {
        sicherung = neu;
        id++;
        changer = neu.addNew("A" + Integer.toString(id));
        while (id < 10) {
          id++;
          neu.addNew("A" + Integer.toString(id));
        }
      }
    }
    changer.setDate(new GregorianCalendar(2005, 12, 12, 12, 12,12));
    System.out.println("Aus " + sicherung.getName() + " " + changer.getName() + " löschen und neu einfügen");  
    System.out.println(changer.getName() + " gefunden?" + sicherung.tasks.contains(changer));
    System.out.println(changer.getName() + " löschen?" + sicherung.tasks.remove(changer));
    System.out.println(changer.getName() + " neu einfügen?" + sicherung.tasks.add(changer));
    Forum test = new Forum();
    test.OutputTestklasse(go);
  }
  
  public void OutputTestklasse(Testklasse main) {
    Iterator itrTasks = main.tasks.iterator();
    while (itrTasks.hasNext()) {
      Testklasse p = (Testklasse)itrTasks.next();
      System.out.println(p.getName() + " ist in dem Unterbaum von " + main.getName() + "? " + main.tasks.contains(p));
      OutputTestklasse(p);
    }
  }
}
```

Und jetzt probierts einmal aus, indem ihr z.B. in der main-Methode den Befehl changer.setDate(new GregorianCalendar(2005, 12, 12, 12, 12, 12)) ausklammert oder probierts mal, wenn ihr diese Zeile drin lasst, aber die anderen 4 Zeilen drunter ausklammert (also die System.out.println-Zeilen).

Na? Ich hoffe, ihr bekommt auch die Ergebnisse, die ich rausbekommen hab ;-)

Gruß

gpxricky


----------



## Wildcard (23. Mai 2005)

Was willst du denn jetzt hören? Macht doch genau das was zu erwarten war.
Du musst das so machen wie ich schon weiter oben geschrieben hab:
entfernen -> verändern -> hinzufügen


----------



## gpxricky (23. Mai 2005)

Naja, prinzipiell schön und gut, aber ich kann nicht zuerst den Eintrag aus dem TreeSet entfernen und dann wieder hinzufügen, da mein Programm etwas anders aussieht als das obige. z.B. bekommt die Testklasse, die im TreeSet changer hat, eine Änderung von changer nur mittles Observer-update-Methode mit und dann ist es ja leider schon zu spät :-(.
Oder gibt es eine Möglichkeit, direkt von (im Beispiel jetzt) changer zu sagen:


```
TreeSet reload = (TreeSet)changer.parent;
reload.remove(this);
changer.setDate(..)
reload.add(this)
```


----------



## Bleiglanz (23. Mai 2005)

nur zu deiner Info:

das ganze Collection API basiert darauf, dass Objektreferenzen gespeichert werden

ändert sich ein bereits in irgendeiner Collection vorhandenes Element, so hat das überhaupt keine Auswirkungen, ggf. führt das aber zu leichten Inkonsistenzen



> aber ich kann nicht zuerst den Eintrag aus dem TreeSet entfernen und dann wieder hinzufügen, da mein Programm etwas anders aussieht


musst du aber, wenn du die Sortierung erhalten willst


----------



## Wildcard (23. Mai 2005)

gpxricky hat gesagt.:
			
		

> Naja, prinzipiell schön und gut, aber ich kann nicht zuerst den Eintrag aus dem TreeSet entfernen und dann wieder hinzufügen, da mein Programm etwas anders aussieht als das obige.


Zeit das Klassendesign zu überdenken  :wink:


----------



## gpxricky (23. Mai 2005)

*lach* ne ne, das ist jetzt schon hundertmal über den Haufen geworfen worden ;-).
Ok, ich habs mittlerweile so hinbekommen, dass ich einigermaßen zufrieden bin (auch von der Performance her).

Vielen lieben Dank für eure Hilfe

gpxricky


----------



## Wildcard (23. Mai 2005)

gpxricky hat gesagt.:
			
		

> *lach* ne ne, das ist jetzt schon hundertmal über den Haufen geworfen worden ;-).
> 
> gpxricky



Also hundert Fehler die du nächstes mal nicht mehr machst. Nur dank dieser Fehler gibt es überhaupt Design-Patterns


----------

