ArrayList sortieren (Collections.sort) Problem

Valas

Grünschnabel
Hallo zusammen,

ich hoffe, ich bin hier im richtigen Unterforum für diese Art von Frage. Und zwar beschäftige ich mich seit relativ kurzer Zeit mit Java und bin gerade auf ein Problem mit Generics gestoßen. Es geht darum, eine bestehende ArrayList anhand von mir gewaehlter Kriterien zu sortieren. Dafür habe ich (grob betrachtet) folgenden Code verwendet. Dummerweise sagt er mir dann immer bei zuVergleichenderWertA > zuVergleichenderWertB , dass der Vergleichsoperator hierfür nicht definiert ist. Wenn ich stattdessen zuVergleichenderWertA.getGewicht() > zuVergleichenderWertB.getGewicht() verwende, funktioniert es. Da ich die compare-Methode aber gerne für verschiedene übergebene Attribute verwenden würde, gestaltet sich die Festlegung auf bestimmte Getter an dieser Stelle etwas ungünstig, da es nicht flexibel zu handhaben ist. Vor allem warum funktioniert der Vergleichsoperator == aber > dagegen nicht? Er müsste doch in beiden Fällen das gleiche Problem haben: Nämlich dass er nicht weiß, welche Attribute er überhaupt vergleichen soll. Daher wollte ich da irgendwie Platzhalter einbauen, aber es scheint nicht zu funktionieren... oder ist es gar nicht möglich, dass anders zu gestalten?

Ich habe schon zig Seiten im Internet besucht (unter anderem die JavaInsel), aber ich komme bei der OO-Gestaltung dieser Methode nicht vorwärts. Ich wäre euch sehr dankbar, wenn ihr ein paar (mehr) hilfreiche Tipps auf Lager habt, damit ich das Problem irgendwie lösen kann. Generics sind irgendwie nicht wirklich anfängertauglich, aber dummerweise scheine ich da nicht drum rum zu kommen. Vielen Dank schonmal für eure Antworten.

Mario


Java:
public class CollectionsTest implements Comparator<Test>  { 

public void listeAnzeigen() {
		java.util.Collections.sort(liste, new CollectionsTest());
            ...
}

@override
public int compare(Test zuVergleichenderWertA, Test zuVergleichenderWertB) {
		int i = 0;
		if (zuVergleichenderWertA == zuVergleichenderWertB) {
			i = 0;
		}	else if (zuVergleichenderWertA > zuVergleichenderWertB) {
			i =  -1;
		} else {
			i = 1;
		}
		return i;
	}
}
 
Das ist normal, denn Objekte können nicht mit größer oder kleiner verglichen werden, das geht nur mit primitiven Datentypen, also int, short, long, double oder float (wenn ich nichts vergessen habe). Damit wäre Deine Implementierung immer bezogen auf die Attribute, d.h. Du vergleichst z.B. Strings mit compateTo() und primitive Datentypen mit ==, > oder < und gibst entsprechend das zurück, was Du für kleiner oder größer hälst.
 
Hallo, erstmal danke für deine schnelle Antwort. Ok, das mit den Objekten vergleichen ist mir nun klar. Es könnte ja theoretisch auch ein String an die Methode übergeben werden, so dass ein < oder > nicht funktionieren würde. Bei dem compareTo müsste ich doch dann aber auch mit nem Getter ein bestimmtes Attribut aus der Klasse Test ansprechen, damit ich das vergleichen kann oder? Wenn ich aber z.B. in der Klasse Test 10 verschiedene Attribute habe, wovon ich z.B. immer mal nach verschiedenen Attributen sortieren möchte, komme ich mit nem expliziten Getter auf ein bestimmtes Attribut nicht weiter, denn dann würden mir auch die übergebenen Parameter (ein bestimmtes Attribut zweier Objekte) nicht weiterhelfen. Wenn ich als z.B. nach Größe sortieren möchte, dann brauch ich den Getter für Größe, möchte ich aber wie in dem Beispiel-Code nach Gewicht sortieren, brauche ich den Getter für Gewicht. Ich weiß nicht, ob verständlich geworden ist, worauf ich hinaus will, aber das Problem lässt sich irgendwie schlecht beschreiben (besonderns wenn man keine Ahnung von Generics hat).
 
Wenn ich aber z.B. in der Klasse Test 10 verschiedene Attribute habe, wovon ich z.B. immer mal nach verschiedenen Attributen sortieren möchte, komme ich mit nem expliziten Getter auf ein bestimmtes Attribut nicht weiter, denn dann würden mir auch die übergebenen Parameter (ein bestimmtes Attribut zweier Objekte) nicht weiterhelfen. Wenn ich als z.B. nach Größe sortieren möchte, dann brauch ich den Getter für Größe, möchte ich aber wie in dem Beispiel-Code nach Gewicht sortieren, brauche ich den Getter für Gewicht.
Ich weiß nicht, ob verständlich geworden ist, worauf ich hinaus will, aber das Problem lässt sich irgendwie schlecht beschreiben (besonderns wenn man keine Ahnung von Generics hat)

Moin,

ich muss zugeben, dass sich mir Dein konkretes Problem nicht so ganz erschlossen hat.
Wenn Du in Deiner Klasse zehn verschiedene Attribute hast, brauchst Du auch entsprechend 10 Get- und Set-Methoden, um drauf zugreifen zu können.
Falls Du mit "Parameter" diejenigen meinst, die Du dem Konstruktor der Klasse mitgibst, brauchst Du entweder EINEN Konstruktor mit allen denkbaren Attributen oder halt mehrere spezifische Konstruktoren mit den jeweils gewünschen Attributen (gleiches gilt dann ggf. auch für 'normale' Methodenaufrufe) !

Vlt. würde ja mit einen Code-Beispiel deutlicher,m was Du meinst ....

Gruß
Klaus
 
Eigentlich klingt das für mich wie "Reflection". Du könntest bei jedem beliebigen Objekt z.B. alle Getter ermitteln und entsprechend aufrufen und vergleichen:

Code:
for (Method m : irgendeinObject.getMethods()) {
if (m.getName().startsWith("get")) {
if (m.getReturnType().isPrimitive()) {
  // prüfe mit "<" oder "==" oder ">"
}
}
// u.s.w.
}
 
Hallöchen ihr 2, danke für eure Bemühungen. Ich habe mir schon gedacht, dass das Problem eventuell nicht hinreichend von mir erklärt wurde, da es ein wenig schwer ist, etwas zu erklären, von dem man wenig Ahnung hat. ;) Schnuffies Antwort könnte wohl schon in die Richtung gehen, aber ich konnte es bisher noch nicht ausprobieren.

@VFL_freak

Das mit den Gettern und Settern für jedes Attribut ist mir bewusst. Das stellt auch kein Problem dar, da ich für die 10 Attribute die jeweiligen Getter und Setter schon erstellt habe. Ich versuche nochmal, das Problem näher zu veranschaulichen. Nehmen wir mal an, ich habe eine ArrayList mit den Objekten mit je 10 Attributen (z.B. Stärke, Gewicht, etc.) auf der Konsole ausgegeben und möchte die je nach Belieben nach einem anderen Attribut sortieren (collections.sort). Um jetzt z.B. nach Gewicht zu sortieren, habe ich mit dem folgenden Comparator gearbeitet:

Java:
public class CollectionsTest implements Comparator<Test>  { 

public void listeAnzeigen() {
		java.util.Collections.sort(liste, new CollectionsTest());
            ...
}

@override
public int compare(Test zuVergleichenderWertA, Test zuVergleichenderWertB) {
		int i = 0;
		if (zuVergleichenderWertA == zuVergleichenderWertB) {
			i = 0;
		}	else if (zuVergleichenderWertA.getGewicht() > zuVergleichenderWertB.getGewicht()) {
			i =  -1;
		} else {
			i = 1;
		}
		return i;
	}
}

Collections.sort nimmt dann automatisch die Methode compare (die von mir ja nach meinen Vorgaben überschrieben wurde). Möchte ich jetzt aber z.B. nach Stärke sortieren, kann ich diese überschriebene Methode nicht verwenden, da ich dort ja explizit den Getter von Gewicht angegeben habe (zuVergleichenderWertA.getGewicht()). Für eine Sortierung nach Stärke müsste dann ja zuVergleichenderWertA.getStaerke() stehen. Für jedes Attribut steht dort also eine andere Programmzeile. Die Frage ist also, wie ich den Part mit den Gettern so gestalten kann, dass ich immer die gleiche compare Methode verwenden kann, egal nach welchen Attributen ich sortieren möchte. Es werden ja immer 2 Werte an die Methode übergeben (zuVergleichenderWertA und zuVergleichenderWertB). Es würde sich bei den zu vergleichenden Attributen auch immer um primitive Datentypen handeln. Nur der Part mit dem Getter müsste dynamisch sein. Ich hoffe, ich konnte das Problem jetzt etwas deutlicher darstellen. Eine bessere Umschreibung fällt mir leider dazu nicht ein... :(
 
Hi.

Es gibt eine zweite Collections.sort Methode wo du den zu verwendenden Comparator angeben kannst. Definiere für jedes deiner Attribute einen Comparator und rufe dann die sort Methode entsprechend auf.

Gruß
 
Hallo deepthroat,

gelesen hab ich das mit dem wählbaren Comparator schon und ich bin auch der Meinung, dass ich oben beim Aufruf von Collections.sort eben diese Variante genutzt habe (java.util.Collections.sort(liste, new CollectionsTest());). Allerdings rufe ich dort ja sozusagen die Klasse CollectionsTest auf (die die compare Methode enthält). Ich kann mir allerdings im Moment nicht so richtig vorstellen, wie ich dort einen anderen Comparator (Namen) angeben kann (außer noch mehr Klassen erstellen, was aber wohl nicht so der beste Weg ist). Ich kann ja nicht mal den Namen der Methode compare ändern, da dieser Name so vorgeschrieben ist.

Hast du zufällig in ner groben Übersicht irgendwo nen Codeschnipsel rumliegen, der mir das Ganze veranschaulichen könnte?
 
gelesen hab ich das mit dem wählbaren Comparator schon und ich bin auch der Meinung, dass ich oben beim Aufruf von Collections.sort eben diese Variante genutzt habe (java.util.Collections.sort(liste, new CollectionsTest());). Allerdings rufe ich dort ja sozusagen die Klasse CollectionsTest auf (die die compare Methode enthält). Ich kann mir allerdings im Moment nicht so richtig vorstellen, wie ich dort einen anderen Comparator (Namen) angeben kann (außer noch mehr Klassen erstellen
Eben.
was aber wohl nicht so der beste Weg ist
Warum? Den Code für die unterschiedlichen Vergleiche mußt du ohnehin schreiben.

Java:
class Foo {
  Comparator<Test> sortBy;

  class SortByName implements Comparator<Test> {
     @override
     public int compare(Test zuVergleichenderWertA, Test zuVergleichenderWertB) {
        ...
     }
  }

  class SortByWeight implements Comparator<Test> {
     @override
     public int compare(Test zuVergleichenderWertA, Test zuVergleichenderWertB) {
        ...
     }
  }

  ... 
     sortBy = new SortByWeight();
// oder
    sortBy = new SortByName();

  ...
     Collections.sort(xyz, sortBy);
Gruß

PS: Wenn der Vergleich nicht "teuer" ist, kannst du übrigens die Funktion vereinfachen:
Java:
// nach Gewicht sortieren.
public int compare(Test a, Test b) {
  return (a.getGewicht() - b.getGewicht());
}
 
Zuletzt bearbeitet:
Danke für deine schnelle Antwort. Ich dachte mir, dass das mit den zig verschiedenen Klassen nur zum Vergleichen vielleicht nicht der eleganteste Weg ist. Aber da habe ich wohl falsch gedacht. Ich werde das einfach mal so ausprobieren, wie du das dargestellt hast und meine Ergebnisse dann hier posten. Aber ich denke mal, dass ich so schon gut weiterkomme...

Merci

EDITH meint: Konnte das Problem mit eurer Hilfe loesen. Insbesondere danke an deepthroat. Konnte anhand deines Code-Beispiels eine Loesung zusammenbasteln und es funktioniert nun alles wie gewollt.
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück