TreeSet Sortierung

Steve222

Mitglied
Guten Tag,

hier eine weitere Frage und eine herzliches Danke für Beachtung oder sogar Beantwortung dieser Frage.

Wie kann ich ein TreeSet flexibel und nach verschiedenen Kriterien sortieren?

Sachverhalt:

TreeSet implementiert sortedSet und gilt (daher!?) als sortiert, bzw. sortiert automatisch.

Wenn nun Objekte des Typs Person in ein TreeSet ts sollen, dann muss
die class Person Comparable implementieren.

Sobald mehr als ein einzelnes Objekte des Typ Person in ts hinzugefügt werden soll,
wird die Überschreibung von compareTo(E o) gefordert.

public int compareTo(E o) ist einzige Methode in Comparable.

compareTo(E o) kann sich nur auf EIN Attribut von Person-Objekt beziehen, nämlich das was ich auswähle, denn über die Parameterliste von compareTo(...) ist keine Steuerung möglich.

Wenn ich meine Person Objekte nach jeweils einem anderen Attribut sortieren will
dann geht das in einem Programmlauf und ohne Abändern der compareTo(E o) -Überschreibung wohl nicht.

Wenn ich meine Person Objekte nach jeweils einem anderen Attribut sortieren will,
dann ist TreeSet anscheinend ungeeignet, oder?

Da muss ich vermutlich die Person-Objekte in eine andere Collection (z.B. List ) speichern und jeweils verschiedene Comparatoren
darauf anwenden, oder ?


Viele Grüße
Steve222
 
Nicht ganz, du kannst ja in dem Objekt Person einen SortKey angeben:


Java:
/* [...] */
pivate int sortKey = 0; // default sort by first field
/* [...] */
@Override
public int compareTo(Object o) {
    Person p = (Person) o;
    switch(sortKey ){
        case 0: { return this.getFirstName().compareTo(p.getFirstName()); }
        /* [...] */
       default: { return this.getFirstName().compareTo(p.getFirstName()); }
       }
}
/* [...] */

Musst dann nur noch den Objecten den Sortkey zuweisen, oder aber den Sortkey irgend wo anders her bekommen.

viele Grüße
f.
 
Hallo fassy und
vielen Dank für den schnellen Hinweis.

Dann können nur genau die Person-Instanzen miteinander verglichen und sortiert werden, die den gleichen sort-Key haben.

Aber was, wenn einige Person-Instanzen den einen .sortKey haben und andere Person-Instanzen einen anderen?
Spontan stelle ich es mir mühsam vor, jeder Person-Instanz einen sortkey zuzuweisen und den jedesmal abzuändern
wenn nach einem anderen Merkmal sortiert werden soll.


Java:
Person objPerson1 = new Person()
Person objPerson2 = new Person()
Person objPerson3 = new Person()
Person objPerson4 = new Person()
objPerson1.sortKey = 2;
objPerson2.sortKey = 1;
objPerson3.sortKey = 2;
objPerson4.sortKey = 1;

Oder denke ich verkehrt?

Viele Grüße

Steve222
 
Zuletzt bearbeitet von einem Moderator:
Nachtrag zu Nachtrag:

Ich wollte miteilen, dass meines Erachtens sortKey NICHT als private deklariert werden darf, sondern
als public oder ggf. protected.
 
Ich wollte miteilen, dass meines Erachtens sortKey NICHT als private deklariert werden darf, sondern
als public oder ggf. protected.

Warum solltest du das tun wollen? Ich würde in Java niemals eine Membervariable public deklarieren. Maximal protected und dann auch nur wenn sie in vererbten Klassen weiter genutzt werden soll. Public sollten nur Konstanten sein. 5 Zeilen für einen Getter und nochmal 5 für einen Setter hat man immer über. Wenn du irgendwann mal deine Meinung änderst was den Zugriff auf das Feld angeht musst du nur die Getter und Setter anpassen, nicht jede Klasse die auf das public deklarierte Feld zugreift. Aber das ist eine Glaubens und Design Frage - aber der Zugriff über public direkt auf Objektvariablen ist kein guter Stil und wiederspricht dem OOP Prinzip der DatenKapselung und Abstraktung

Dann können nur genau die Person-Instanzen miteinander verglichen und sortiert werden, die den gleichen sort-Key haben.

Aber was, wenn einige Person-Instanzen den einen .sortKey haben und andere Person-Instanzen einen anderen?
Spontan stelle ich es mir mühsam vor, jeder Person-Instanz einen sortkey zuzuweisen und den jedesmal abzuändern
wenn nach einem anderen Merkmal sortiert werden soll.

Du solltest deine Requirements überdenken. Wann soll sortiert werden? Normalerweise wird beim einfügen in die Map sortiert, d.h. das neue Object wird an die richtige Position geschoben. Das Treeset sortiert nicht automatisch alle anderen Objekte mit um.

Natürlich macht es Sinn nur Objekte mit dem gleichen Sortkey in einem Set zu halten, das ist schließlich der Gedanke hinter einem SortedSet. Ich verstehe halt nicht so ganz was du vorhast, also die Frage ist was möchtest du machen? Wenn du das Set dynamisch umsortieren willst musst du so vorgehen:

1) Sortiere nach Vorname in Set1
2) ändern des sortkeys, also Set1 umsortieren
2.1)alle sortkeys der Object in set1 auf den neuen sortkey setzen
2.2)neue TreeSet anlegen
2.3)alle Objecte in das neue Set schieben

Java:
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class Test {
	public static final int SORT_BY_FIRSTNAME = 0;
	public static final int SORT_BY_LASTNAME = 1;

	public static void main(String[] args) {

		Person a = new Person("Klaus", "Heinze");
		Person b = new Person("Mara", "Schmidt");
		Person c = new Person("Werner", "Meier");
		Person d = new Person("Stephanie", "Maier");
		Person e = new Person("Ruth", "Mayer");
		Person z = new Person("Arno", "Zitter");
		z.setSortKey(SORT_BY_LASTNAME);

		Set<Person> persons = new TreeSet<Person>();
		persons.add(a);
		persons.add(b);
		persons.add(c);
		persons.add(d);
		persons.add(e);

		System.out.println("SORTED BY FIRSTNAME:");
         printSet(persons);
/* 
SORTED BY FIRSTNAME:
Klaus Heinze
Mara Schmidt
Ruth Mayer
Stephanie Maier
Werner Meier
*/

		persons.add(z);
		System.out.println("\nSORTED BY FIRSTNAME:");
        printSet(persons);
/*

SORTED BY FIRSTNAME:
Klaus Heinze
Mara Schmidt
Ruth Mayer
Stephanie Maier
Werner Meier
Arno Zitter              <-- am Ende eingefügt worden, das sortKey für den Kollegen 1 ist...  
*/
        
		persons = sortSet(persons, SORT_BY_LASTNAME);
		System.out.println("\nSORTED BY LASTNAME:");
		printSet(persons);
/*
SORTED BY LASTNAME:
Klaus Heinze
Stephanie Maier
Ruth Mayer
Werner Meier
Mara Schmidt
Arno Zitter
*/
		persons = sortSet(persons, SORT_BY_FIRSTNAME);
		System.out.println("\nSORTED BY FIRSTNAME:");
		printSet(persons);
/*
SORTED BY FIRSTNAME:
Arno Zitter
Klaus Heinze
Mara Schmidt
Ruth Mayer
Stephanie Maier
Werner Meier
*/


	}

	private static Set<Person> sortSet(Set<Person> set, int sortKey) {
		Set<Person> newSet = new TreeSet<Person>();
		for ( Iterator<Person> i = set.iterator(); i.hasNext();){
		         Person p = i.next();
		         p.setSortKey(sortKey);
		         newSet.add(p);
		}
		return newSet;
	}
	
	private static void printSet(Set<Person> set){
		for (Iterator<Person> i = set.iterator(); i.hasNext();) {
			Person p = i.next();
			System.out.println(p.getFirstName() + " " + p.getLastName());
		}
	}
}

class Person implements Comparable {
	private String firstName;
	private String lastName;
	private int sortKey = 0;

	public Person(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	@Override
	public int compareTo(Object o) {
		Person p = (Person) o;
		switch (sortKey) {
		case 0: {
			return this.getFirstName().compareTo(p.getFirstName());
		}
		case 1: {
			return this.getLastName().compareTo(p.getLastName());
		}
		default: {
			return this.getFirstName().compareTo(p.getFirstName());
		}
		}
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public int getSortKey() {
		return sortKey;
	}

	public void setSortKey(int sortKey) {
		this.sortKey = sortKey;
	}

}

Die Frage ist und bleibt was du eigentlich vorhast. Du kannst ja auch ein Event/Listener Konzept fahren, d.h. wenn ein sortKey geändert wird triggert das eine Neusortierung. Oder du überschreibst die add Methode des Set und sortierst neu beim Einfügen wenn der sortKey nicht passt. Aber in einem Set nach verschiedenen Keys zu sortieren macht keinen Sinn. Das Set verschieden sortiert ausgeben schon, das kannst du machen wie in meinem Beispiel indem du die sortSet Methode aufrust und dann mit dem nach deinen Wünschen Sortierten Set arbeitest.

Viele Grüße
f.
 
Zuletzt bearbeitet:
Du kannst dem Konstruktor des TreeSet einen Comparator mitgeben. Der funktioniert im Prinzip genauso wie Comparable nur dass er eben zwei zu vergleichende Objekte bekommt. Dadurch kannst du je nach Situation einen anderen Comparator mitgeben und bist nicht darauf angewiesen dass deine Person Comparable ist oder wie Comparable dort implementiert ist.
 
Für dein Problem hast du dir leider die falsche Programmiersprache ausgesucht; in C# würde ich das Problem folgendermaßen lösen. Ich verwalte die Daten in einem DataSet an, in dem ich die notwendigen Tabellen definiere, füge unter Umständen die notwendigen Indices hinzu, und hole die gewünschten Daten mit Hilfe eines geeigneten SQL-Statements. Als Ergebnis bekomme ich dann ein DataSet, das ich entweder direkt als DataSource einem Anzeigeelement zuweise oder in die benötigten Objekte und Verwaltungsstrukturen umwandle. Dies alles kann man ohne jegliche Datenbankverbindung machen, die Daten bleiben also lokal im Speicher deines Programms.
In Java ist das nicht möglich. Man kann solche Daten in speziellen Bäumen verwalten (B+ Bäume), die man dann allerdings selber implementieren muss. Eine solche Datenstruktur wird bei der Bereichssuche verwendet, die beispielsweise bei graphischen Algorithmen genutzt wird. Die Vorgehensweise ist dann folgendermaßen.
Nehmen wir an, deine Daten sollen nach zwei Kriterien geordent sein, beispielsweise Geburtstag und Name. Auf Ebene 1 (dem Wurzelknoten) unterscheidest du dann nach Geburtstag, ob der Datensatz links oder rechts gespeichert werden soll. Auf Ebene 2 wird dann anhand des Namens unterschieden, ob der Datensatz nach links oder rechts soll. Auf Ebene 3 wird dann wieder per Geburtstag als Kriterium unterschieden etc. Der Comparator müsste also wissen, auf welcher Ebene des Baumes er aufgerufen wird, um das richtige Kriterium zu verwenden, was meines Erachtens nicht möglich ist. Ein TreeSet ist dafür ungeeignet.
Du kannst aber, wie oben erwähnt, einem TreeSet über den Konstruktor einen speziellen Comparator zuweisen. der dann eine spezielle Compare-Methode in deinen Objekten aufruft (z.B. CompareBirthdayAndName(..) ), welche automatisch die richtigen Kriterien verwendet. Die TreeSets fungieren dann quasi als eigene Tabelle mit einem Comparator als einzigem Index. Für jedes TreeSet musst du dann einen eigenen Comparator schreiben und ein spezielle Compare-Funktion in deiner Objektklasse, welche dann von dem jeweiligen Comparator aufgerufen werden kann. Damit das auch sauber funktioniert, musst du zusätzlich für jeden Comparator (welcher jeweils einer eigenen Klasse angehören muss) ein eigenes Interface definieren, das dann von deinen zu sortierenden Objekten implementiert wird, damit diese dann auch von dem Comparator verwendet werden kann. Außerdem musst du als Datentyp für die TreeSets das Interface angeben das verwendet werden soll, nicht die Klasse der Objekte, die darin zu speichern sind. Zudem müssen alle betroffenen TreeSets neu sortiert werden, wenn die Objekte Änderungen in den Daten haben, die für ihre Sortierung relevant sind; eventuell solltest du dafür einen eigenen Event-Mechanismus implementieren. Welche Objekte nach welchem Kriterium sortiert werden sollen und welche nicht, entscheidest du dadurch, welchem TreeSet du sie zuweist.
Alles in allem ist das ziemlich umständlich und aufwändig, aber ich sehe keine andere 'brauchbare' Möglichkeit, dein Problem in Java zu lösen.
 
Zuletzt bearbeitet:
Hi,

Du kannst einen Customcomparator bauen, der den jeweiligen Vergleich beherrscht. Umsortieren kannst Du dann mittels Collections.sort().

Beispiel
Java:
	public static class MyComparator implements Comparator<SetType>, Serializable {
		public int compare(final SetType o1, final SetType o2) {

			return null;
		}
	}

Gerufen wird der dann mittels

Collections.sort(meinSet, new MyComparator());

Wenn ich Dich richtig verstanden habe, geht es nur um die Klasse Person, die mehrfach auf unterschiedliche Weise verglichen werden soll, oder? Afaik ist das TreeSet ein sortiertes Set, dass die Reihenfolge nicht mehr ändert. Ein Collections.sort() sollte daher auch im Treeset greifen.



Grüße
gore
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück