# Mehrere Comparatoren



## Claudia92 (26. Jul 2012)

Hallo ihr Lieben! 

Angenommen eine Klasse sollte über viele Instanzvariablen sortierbar sein:

```
class World {
	int population;
	int surface;
	int mass;
	int ...
}
```

Muss ich dann tatsächlich für jede Variable eine eigene Comparator-Klasse schreiben?

```
class WorldPopulationComparator implements Comparator<World> {
		public int compare(World o1, World o2) {
			return o1.population - o2.population;
		}
	}
	
	class WorldSurfaceComparator implements Comparator<World> {
		public int compare(World o1, World o2) {
			return o1.surface - o2.surface;
		}
	}
	
	class WorldMassComparator implements Comparator<World> {
		public int compare(World o1, World o2) {
			return o1.mass - o2.mass;
		}
	}
```

Oder gibt es hier einen Trick, damit ich nur einmal eine Comparator-Klasse schreiben muss, aber dann über einen Parameter o. Ä. angeben kann, über welche Variable vergleichen bzw. sortiert werden soll?


----------



## Marco13 (26. Jul 2012)

Ich bin zwar nicht "lieb", aber sei's drum: Theoretisch kann man mit Reflection tricksen, aber praktisch macht man das nicht - außer wenn es vielleicht um hunderte von Standard-Beans-Klassen mit hunderten von Fields geht (was hier aber nicht der Fall zu sein scheint).


----------



## Gonzo17 (26. Jul 2012)

Ein alternativer Ansatz, der mir gerade in den Kopf kam. Du könntest in einer Variable in der Klasse World selbst festhalten, welches Feld das aktuelle Sortierkriterium Nummer 1 ist (theoretisch könntest du da auch ne Liste pflegen welches höher ist als andere). Dann schreibst du nur einen Comparator und checkst eben in der Methode compare(), welches Feld da gerade das Kriterium ist und verfährst entsprechend. Ob das jetzt eine "schönere" Lösung ist oder nicht sogar "schlechter Stil" kann ich jetzt schwer beurteilen, war nur so ne Idee.


----------



## Spacerat (26. Jul 2012)

Also ich weis nicht... was denn an mehreren Comparatoren auszusetzen? Am besten, die zu sortierende Klasse implementiert java.util.List und sortieren mit entsprechendem Komparator ist dann per Collections.sort(list, comparator) kein Thema mehr. Die ganzen Komparatoren wären dabei evtl. auch noch als öffentliche Konstanten der List angenehm.
Gonzo17 Idee mit der Prioritätenliste fine ich persönlich aber auch nicht verkehrt. Da setzt du zunächst die Sortierreihenfolge fest (falls ein Kriterium gleich, sortier nach dem nächsten) und rufst dann Collections.sort() auf. Dann vergiss aber blos die default Sortierreihenfolge nicht.


----------



## Wishmaster51 (26. Jul 2012)

Alternativ könnte man einen Comparator schreiben, und in diesem dann per Konstruktor festlegen, nach welchem Kriterium sortiert werden soll.
Prinzipiell spricht aber auch nicht gegen mehrere Comparatoren.


----------



## Claudia92 (26. Jul 2012)

@Marco13: Jeder, der mir hilft, ist lieb - also auch du! 

@Gonzo17: Interessanter Ansatz, aber wenn ich dich richtig verstanden habe, ergibt sich da ein Problem. Angenommen im Objekt earth ist population als Sortierkriterium eingetragen, im Objekt mars jedoch surface. Dann würde der Comparator ohne mit der Wimper zu zucken population und surface miteinander vergleichen, was auf keinen Fall passieren sollte.

@Spacerat: Was an mehreren Comparatoren auszusetzen ist? Nichts. 
Ich will nur wissen, ob ich mir die Tipparbeit für viele nahezu identische Klassen sparen kann. Zur Not schreibe ich mir ein Template in Eclipse ähnlich wie für automatisch generierte Getter/Setter-Methoden.
Der Vorteil deines Vorschlags, die zu sortierende Klasse solle java.util.List implementieren, erschließt sich mir jedoch nicht. Die einzelnen Objekte von World kommen in eine 
	
	
	
	





```
List<World> worlds
```
 und werden dann z. B. mit 
	
	
	
	





```
Collections.sort(worlds, new WorldPopulationComparator());
```
 sortiert.

@Wishmaster51: Und wie soll das gehen?! Ich kann einem Konstruktor ja nicht einfach einen Variablennamen mitgeben.


----------



## hüteüberhüte (26. Jul 2012)

Btw: 
	
	
	
	





```
o1.value - o2.value
```
 könnte, obwohl ein negativer Wert zu erwarten wäre, einen positiven Wert liefern, wenn ein integer underflow auftritt

Besser: 
	
	
	
	





```
o1.value < o2.value ? -1 : o1.value > o2.value ? 1 : 0
```


----------



## Claudia92 (26. Jul 2012)

@hüteüberhüte: Meine Instanzvariablen nehmen ausschließlich positive int-Werte an. Trotzdem Danke für die Warnung.


----------



## hüteüberhüte (26. Jul 2012)

[OT]Naja, man kann sich schlechten Programmierstil angewöhnen oder auch nicht. Sicher ist nur, dass wegen Überläufen auch schon Raketen abgestürzt sind[/OT]


----------



## Spacerat (26. Jul 2012)

Claudia92 hat gesagt.:


> Der Vorteil deines Vorschlags, die zu sortierende Klasse solle java.util.List implementieren, erschließt sich mir jedoch nicht. Die einzelnen Objekte von World kommen in eine
> 
> 
> 
> ...


Wie meinst du dass, dass sich dir dieser Vorschlag nicht erschliesst? Nichts anderes, als so wie du das beschreibst, hatte ich das gemeint, nur hattest du von einer Klasse gesprochen, die du sortieren wolltest.
Aber Interfaces sind gar nicht so schlecht, erst recht, wenn es um Gonzos Idee geht: 

```
import java.util.Comparator;
import java.util.Iterator;

public interface AttributeList extends Iterable<Comparable<Object>> {
  Comparator<AttributeList> COMPARATOR = new Comparator<AttributeList>() {
    @Override
    public int compare(AttributeList o1, AttributeList o2) {
      Iterator<Comparable<Object>> i1 = o1.iterator();
      Iterator<Comparable<Object>> i2 = o2.iterator();
      int rc = 0;
      while( rc == 0 && i1.hasNext() && i2.hasNext()) {
        rc = i1.next().compareTo(i2.next());
      }
      return rc;
    }
  };
}
```
Nur mal so 'ne allgemeine Idee von mir. Das implementierst du nun in World und gut. XD
[EDIT]Ich les' hier gerade von ausschliesslich int-Werten. Dann Object evtl. in Integer ändern, dann ists aber nicht mehr allgemein.[/EDIT]


----------



## Claudia92 (26. Jul 2012)

@hüteüberhüte: Hey, ich habe das so aus Galileo Computing :: Java ist auch eine Insel - 8 Besondere Klassen der Java SE ! Und deswegen gleich "schlechten Programmierstil" zu unterstellen, finde ich einem Anfänger gegenüber nicht fair. Aber gut, ist deine Meinung. Mir wäre es lieber, wenn du zu meiner eigentlichen Fragestellung etwas beitragen könntest. 

EDIT:
@Spacerat: Danke, ich sehe mir das gerade an...
Die Klasse Word soll dieses Interface AttributeList implementieren, oder?
Wie muss dann die iterator Methode dort aussehen?


----------



## Landei (26. Jul 2012)

Claudia92 hat gesagt.:


> @hüteüberhüte: Hey, ich habe das so aus Galileo Computing :: Java ist auch eine Insel - 8 Besondere Klassen der Java SE ! Und deswegen gleich "schlechten Programmierstil" zu unterstellen, finde ich einem Anfänger gegenüber nicht fair. Aber gut, ist deine Meinung. Mir wäre es lieber, wenn du zu meiner eigentlichen Fragestellung etwas beitragen könntest.



Nein, das war ein wirklich ein guter und berechtigter Hinweis. In 99,99% der Fälle wird es nicht zu einem Überlauf kommen, aber wenn doch, ist es genau die Sorte Fehler, die in einem größeren System extrem schwer dingfest zu machen ist, insbesondere wenn man sich den "schlechten Stil" angewöhnt hat und entsprechend betriebsblind geworden ist. Und wenn man - etwa zur Ansteuerung von Hardware - einmal 
	
	
	
	





```
short
```
 statt 
	
	
	
	





```
int
```
verwendet, ist die Wahrscheinlichkeit dieses Fehlers auf einmal um ein Vielfaches höher.

Ich empfehle übrigens die faule, aber narrensichere Variante (wenn es nicht auf das allerletzte Quentchen Performance ankommt): [c]Integer.valueOf(value1).compareTo(value2)[/c]


----------



## tagedieb (26. Jul 2012)

Spring und andere Frameworks bieten ein generischen PropertyComparator an.
PropertyComparator (Spring Framework)


----------



## Spacerat (26. Jul 2012)

Gleich ein komplettes Framework für so 'ne relativ simple Anforderung? Da bevorzuge ich lieber schlechten Programmierstil.


----------



## tagedieb (26. Jul 2012)

Ev. wird ja schon ein Framework mit dieser Funktionalität verwendet, oder man kann auch einfach die Klasse nachimplementieren.
Ich wollte nur darauf hinweisen, das so ein Comparator schon mehrfach implementiert wurde und man die ja wiederverwenden könnte..


----------



## Claudia92 (26. Jul 2012)

Ich habs nun mit Reflections hinbekommen. Danke an alle!


----------



## Spacerat (26. Jul 2012)

@Claudia:  Jetzt hab' ich deine Frage vorhin völlig übersehen... Ja, World sollte dieses Interface implementieren. Zu der Frage, wie so 'ne Iterator-Methode auszusehen hat, hab' ich mir noch nicht viele Gedanken gemacht bzw. machen können (evtl. 'ne PropertyMap und davon getValues().iterator() zurückgeben). Aber da sieht man, was spontane Ideen sind... eben ausbaufähig.
Aber du hast's ja hinbekommen.


----------



## Gonzo17 (27. Jul 2012)

Claudia92 hat gesagt.:


> @Gonzo17: Interessanter Ansatz, aber wenn ich dich richtig verstanden habe, ergibt sich da ein Problem. Angenommen im Objekt earth ist population als Sortierkriterium eingetragen, im Objekt mars jedoch surface. Dann würde der Comparator ohne mit der Wimper zu zucken population und surface miteinander vergleichen, was auf keinen Fall passieren sollte.



Jain. Das musst du natürlich beachten. Wenn zum Beispiel "earth" verglichen werden soll mit "mars", dann nimmst du den Comparator von "earth" bzw das dortige erste Kriterium. Oder, wenn es ohnehin für alle gleich sein soll, du machst eine statische Variable in der Klasse. Aber das kommt jetzt auch ganz auf den Anwendungsfall ein, was du genau mit dem Vergleich bezwecken willst. Wenn du deine Listen einfach nur unterschiedlich sortieren möchtest nach bestimmten Kriterien und dem World-Objekt ist das vollkommen egal, dann kannst du auch mehrere Comparatoren schreiben oder zB auch einen, der dann eine compare()-Methode enthält, der du das Kriterium mit übergibst. Irgendwie sowas. Wenn es bei dir mit Reflections funktioniert, auch gut.


----------



## Wishmaster51 (27. Jul 2012)

Claudia92 hat gesagt.:


> @Wishmaster51: Und wie soll das gehen?! Ich kann einem Konstruktor ja nicht einfach einen Variablennamen mitgeben.


Warum nicht? Du schreibst dir einen Konstruktor, der einen int-Parameter erwartet, und dieser legt intern die Sortierung fest. Hat auch den Vorteil, dass du, wenn du die Sortierreihenfolge mal ändern möchtest, kein neuen Objekt erzeugen musst.


----------



## bygones (27. Jul 2012)

Wishmaster51 hat gesagt.:


> Warum nicht? Du schreibst dir einen Konstruktor, der einen int-Parameter erwartet, und dieser legt intern die Sortierung fest. Hat auch den Vorteil, dass du, wenn du die Sortierreihenfolge mal ändern möchtest, kein neuen Objekt erzeugen musst.


wie bitte ? du kannst deinem Sortierer dann den Wert des aktuellen ints geben, der hilft dir aber bei spaeteren sortieren gar nix....


----------



## Wishmaster51 (27. Jul 2012)

Ich dachte da an sowas wie:

```
class DynamicComparator implements Comparator<World>
{
        int sort;
        public DynamicComparator(int sort)
        {
                this.sort=sort;
        }
        public int compare(World o1, World o2)
        {
            switch(this.sort)
                {
                        //hier in Abhängigkeit von this.sort sortieren
                }
        }
    }
```


----------



## Marco13 (27. Jul 2012)

Ich bin nicht sicher, ob ich alle Vorschläge hier nachvollziehen konnte, aber unabhängig davon klingen einige schon SEHR abenteuerlich. Einzelne Klassen, oder vielleicht Reflection, ja, aber ... irgendwelche int-Parameter für den Comparator, die angeben, das wievielte Field verwendet werden soll (!?) klingt schräg...

EDIT: Ah ja, hab gerade gesehen, dass das wirklich so gemeint war... Ähm.. davon würde ich abraten... (So ein schönes Objektorientiertes Konzept wie ein "Comparator" verkommt damit zu einem "lästigen Umweg auf dem Weg zu einem C-Konstrukt")


----------



## Wishmaster51 (27. Jul 2012)

Marco13 hat gesagt.:


> Ich bin nicht sicher, ob ich alle Vorschläge hier nachvollziehen konnte, aber unabhängig davon klingen einige schon SEHR abenteuerlich. Einzelne Klassen, oder vielleicht Reflection, ja, aber ... irgendwelche int-Parameter für den Comparator, die angeben, das wievielte Field verwendet werden soll (!?) klingt schräg...


Weshalb schräg? Der Vorteil wäre in meinen Augen, dass du, in dem Falle dass du während der Laufzeit das Kriterium schnell, d.h. ohne Erzeugung eines neuen Objektes, ändern kannst. Das wäre IMHO ein Vorteil gegenüber den einzelnen Klassen, bei denen du jedesmal ein neues Objekt erzeugen müsstest.


----------



## Marco13 (27. Jul 2012)

Ich glaube, das EDIT hat sich mit der Antwort überschnitten, hatte das Beispiel zu spät gesehen. 

Abgesehen davon, dass die Objektorientierung verloren geht, hat das einige GANZ üble Seiteneffekte: 
- Was passiert bei ungültigen Indizes übergeben werden?
- Was passiert wenn ein neues Field in die Klasse kommt? (Jeder der vorher nach "Kriterium 3" sortiert hat, sortiert dann nach etwas anderem :autsch: )
- In bezug auf Erweiterbarkeit und Trennung von Aufgaben ist es damit auch nicht weit her...
- Fragen, die weiter gehen, gibt es viele (was passiert wenn jemand in einem anderen Thread _während der Sortierung_ das Kriterium ändert?)
- ...

EDIT: Also ... ich würde immernoch zu einzelnen Klassen tendieren (ggf. auch gleich ihren Instanzen) und Methoden a la

```
class WorldComparators
{
    public static Comparator<World> byFoo() { return /* new comparator that compares by foo */ }
    public static Comparator<World> byBar() { return /* new comparator that compares by bar */ }
    ...
}
```
Und wenn schon abzusehen ist, dass es solche Dinger für 50 Kriterien gibt, die sich ggf. auch oft ändern, kann man Reflection in Betracht ziehen.

Dass die Comparatoren IN der Klasse liegen müssen, ist IMHO auch nicht so schön.


----------



## Wishmaster51 (27. Jul 2012)

Marco13 hat gesagt.:


> - Was passiert bei ungültigen Indizes übergeben werden?


Da gibts mehrere Möglichkeiten:
1) Man legt ein "default sort kriterium" fest, nach dem dann sortiert wird
2) Man schmeißt eine Exception


Marco13 hat gesagt.:


> - Was passiert wenn ein neues Field in die Klasse kommt? (Jeder der vorher nach "Kriterium 3" sortiert hat, sortiert dann nach etwas anderem :autsch: )


´Nein, warum?

Hier mal das vollständige Codebeispiel:

```
class DynamicComparator implements Comparator<World>
{
        int sort;
        public DynamicComparator(int sort)
        {
                this.sort=sort;
        }
        public int compare(World o1, World o2)
        {
            switch(this.sort)
                {
                       case 1: return o1.population - o2.population;
                       case 2:  return o1.surface - o2.surface;
                       default: return o1.mass - o2.mass;
                }
        }
    }
```
Wenn ich nun in der Klasse World noch ein neues Attribut hinzufüge, dann ändert sich der Comparator erstmal gar nicht, man könnte ihn aber sehr einfach erweitern.


----------



## TheDarkRose (27. Jul 2012)

Wishmaster, dann aber bitte mit Konstanten (static final int) arbeiten, damit IMHO egal ist welcher int Wert zugrunde liegt. Liest sich im Code auch schöner.

```
class WorldComparator implements Comparator<World>
{
        static final int POPULATION = 0;
        static final int SURFACE = 1;
        static final int MASS = 2;

        int sort;
        public WorldComparator(int sort)
        {
                this.sort=sort;
        }

        public int compare(World o1, World o2)
        {
            switch(this.sort)
                {
                       case WorldComparator.POPULATION:
                               return o1.population.compareTo(o2.population);
                       case WorldComparator.SURFACE: 
                               return o1.surface.compareTo(o2.surface);
                       case WorldComparator.MASS:
                               return o1.mass.compareTo(o2.mass);
                       default:
                               throw new RuntimeException("false sort type");
                }
        }
}
```

Noch schöner wäre ein WorldComparatorEnum, statt static final int


----------



## Wishmaster51 (27. Jul 2012)

TheDarkRose hat gesagt.:


> Wishmaster, dann aber bitte mit Konstanten (static final int) arbeiten, damit IMHO egal ist welcher int Wert zugrunde liegt. Liest sich im Code auch schöner.
> (...)
> Noch schöner wäre ein WorldComparatorEnum, statt static final int


Ja, das ist klar. Ich wollte auch nur das Prinzip demonstrieren.

Es fehlen in meinen Beispiel auch noch Setter und Getter, um das während der laufzeit zu ändern.


----------



## Spacerat (27. Jul 2012)

@TheDarkRose: Diese ints sind doch veraltet... Das bringt mich aber auf eine weitere spontane Idee für meine Version (obwohl... Comparable<Object> funktioniert ja wohl überhaupt nicht ). ENUMS als SortOrder. Davon bekäme man ja ein Array, dessen Elemente man austauschen kann und dann an ein ähnliches Konstrukt wie das von WishMaster übergeben. Könnte funktionieren und hat obendrein noch sehr viel OOP jedoch kein Reflection.

```
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.Map;

public class World {
	public enum Attribute {
		POPULATION,
		SURFACE,
		MASS,
	}
	public static final WorldComparator WORLD_COMPARATOR = new WorldComparator();

	private final Map<Attribute, Integer> attributes = new IdentityHashMap<Attribute, Integer>();

	public int get(Attribute a) {
		return (attributes.containsKey(a))? attributes.get(a) : 0;
	}

	public void set(Attribute a, int value) {
		attributes.put(a, value);
	}
}

public class WorldComparator implements Comparator<World> {
	private static final int numAttributes = World.Attribute.values().length;

	private final World.Attribute[] order = World.Attribute.values();
	private boolean descending;

	WorldComparator() {
		// Sollte nur die Klasse Welt instanzieren duerfen, evtl. sogar ein Singleton-Kandidat.
		// Die Klasse WorldComparator selbst muss aber oeffentlich sein.
	}

	@Override
	public int compare(World o1, World o2) {
		int rc = 0;
		synchronized(order) {
			for(int n = 0; rc != 0 && n < numAttributes; n++) {
				Integer a = o1.get(order[n]);
				Integer b = o2.get(order[n]);
				rc = (descending)? b.compareTo(a) : a.compareTo(b);
			}
		}
		return rc;
	}

	public void setSortPriority(World.Attribute[] prio) {
		synchronized(order) {
			// this order setzen und validieren...
		}
	}

	public void setSortOrder(boolean descending) {
		synchronized(order) {
			this.descending = descending;
		}
	}
}
```


----------



## Mujahiddin (27. Jul 2012)

```
enum Sorts{
	POPULATION, SURFACE, MASS
}

public static class WorldComparator {
	public static int compare(World o1, World o2, String first, String... others) {
		EnumSet<Sorts> selectedSorts = EnumSet.of(Sorts.valueOf(first.toUpperCase()));
		for(String s : others)
			selectedSorts.add(Sorts.valueOf(s.toUpperCase()));
		return compare(o1, o2, selectedSorts);
	}
	
	public static int compare(World o1, World o2, Sorts first, Sorts... others) {
		EnumSet<Sorts> selectedSorts = EnumSet.of(first, others);
		return compare(o1, o2, selectedSorts);
	}
	
	public static int compare(World o1, World o2, EnumSet<Sorts> selectedSorts) {
		Iterator<Sorts> sortOrder = selectedSorts.iterator();
		int comparison = 0;
		while(sortOrder.hasNext() && comparison == 0)
			switch(sortOrder.next()){
				case POPULATION:
					//blabla
					break;
				case SURFACE:
					//blablae
					break;
				case MASS:
					//blabla
					break;
			}
		return comparison;
	}
	
	public static int compare(World o1, World o2) {
		return compare(o1, o2, getDefaultSortOrder());
	}
	
	private static EnumSet<Sorts> getDefaultSortOrder(){
		return EnumSet.allOf(Sorts.class);
	}
}
```

So was in der Art?


----------



## Mujahiddin (27. Jul 2012)

@Spacerat:
Ich sehe den Sinn von Attribute.values(); nicht. Deine order bleibt doch immer gleich? Oder greift dein Konstrukt von IdentityHashMap direkt auf das Enum zu? Würde mich aber sehr wundern.

Gäbe es an meiner Variante irgendwas auszusetzen?


----------



## Spacerat (27. Jul 2012)

Ich habe mir schlicht nicht die Mühe gemacht, "setSortPriority" fertig zu implementieren. Dort soll das Array "order" gemäss dem übergebenen "prio" gesetzt werden. Danach muss "order" validiert werden, so dass sich darin weder ein doppeltes noch ein Null-Attribut befindet.
Von meiner Seite her, wäre auch deine Version okay, ist mir aber ein bissl' zu viel Code.  Aber ich hab' da etwas entdeckt, woran ich noch gar nicht gedacht habe --> EnumSets. Das Setzen und Validieren dürfte damit einfacher sein, danke für den Tipp.
Das macht aus meinem WorldComparator nun das:

```
public lass WorldComparator implements Comparator<World> {
	private static final Set<World.Attribute> DEFAULT = Collections.unmodifiableSet(EnumSet.allOf(World.Attribute.class));
	private final Set<World.Attribute> order = EnumSet.allOf(World.Attribute.class);
	private boolean descending;

	WorldComparator() {
	}

	@Override
	public int compare(World o1, World o2) {
		int rc = 0;
		synchronized(order) {
			for(World.Attribute att : order) {
				Integer a = o1.get(att);
				Integer b = o2.get(att);
				rc = (descending)? b.compareTo(a) : a.compareTo(b);
				if(rc != 0) {
					return rc;
				}
			}
		}
		return 0;
	}

	public void setSortPriority(Set<World.Attribute> prio) {
		synchronized(order) {
			order.clear();
			for(World.Attribute att : prio) {
				if(att != null) {
					order.add(att);
				}
			}
			if(order.size() < DEFAULT.size()) {
				for(World.Attribute att : DEFAULT) {
					if(!order.contains(att)) {
						order.add(att);
					}
					order.add(att);
				}
			}
		}
	}

	public void setSortOrder(boolean descending) {
		synchronized(order) {
			this.descending = descending;
		}
	}
}
```


----------



## Marco13 (27. Jul 2012)

Bin ich der einzige, der es häßlich und fragwürdig findet, wenn die Tatsache, dass Objekte nach verschiedenen Kriterien sortiert werden können, nicht nur _Einfluß_ auf die Schnittstelle hat (was schon schlimm genug wäre), sondern die Schnittstelle dieser Objekte _komplett bestimmt_? Comparatoren sind ja eigentlich gerade dazu da, DAS zu vermeiden, oder nicht?

Reflection kann man in Beracht ziehen, aber Generizität hin oder her: Ich finde, einzelne Comparatoren wären OK. Das sind Einzeiler. Mit Java 8 und Closures sogar noch weniger  

Noch ein kleines Experiment...: (Nicht als "Lösungsvorschlag" anzusehen, nur ... eine Alternative, wertfrei...)

```
public class SortingTest
{
    public static void main(String[] args)
    {
        Comparator<World> cx = AttributeComparator.create(WorldAttributes.getAttributeX());
        Comparator<World> cy = AttributeComparator.create(WorldAttributes.getAttributeY());
    }
}


class World
{
    public float getX()
    {
        return 0;
    }
    public int getY()
    {
        return 0;
    }
}

interface Attribute<T, A>
{
    A get(T t);
}

class AttributeComparator 
{
    public static <T, A extends Comparable<? super A>> Comparator<T> create(final Attribute<T, A> attribute)
    {
        return new Comparator<T>()
        {
            @Override
            public int compare(T t0, T t1) 
            {
                A c0 = attribute.get(t0);
                A c1 = attribute.get(t1);
                return c0.compareTo(c1);
            }
            
        };
    }
}

class WorldAttributes
{
    public static Attribute<World, Float> getAttributeX()
    {
        return new Attribute<World, Float>() 
        {
            @Override
            public Float get(World t)
            {
                return t.getX();
            }
        }; 
    }
    
    public static Attribute<World, Integer> getAttributeY()
    {
        return new Attribute<World, Integer>() 
        {
            @Override
            public Integer get(World t)
            {
                return t.getY();
            }
        }; 
    }
    
}
```


----------



## Mujahiddin (27. Jul 2012)

Spacerat hat gesagt.:


> ```
> public void setSortPriority(Set<World.Attribute> prio) {
> synchronized(order) {
> order.clear();
> ...



Ich würde aus dem letzten Block eher ein einfaches 
	
	
	
	





```
order.addAll(DEFAULT);
```
 machen.


----------



## Spacerat (27. Jul 2012)

@Marco13: Reflections zieht man immer als letztes in Betracht, es ist die Brechstange, die man benutzen kann, wenn einem die Ideen ausgehen.
Worüber untehalten wir uns hier? Ich denke, darüber, ob man in Klassen mit X Sortierkriterien, vermeiden kann X*2 (also ASC und DESC) Comparatoren schreiben zu müssen. Natürlich geht das. Am einfachsten siehe meinen Code, etwas aufwendiger, dafür aber auch ohne veränderte Schnittstelle des Objekts, bei Mujahiddin.
Fragwürdig ist nun mehr; Wenn man so etwas vor hat und die Schnittstelle der zu sortierenden Objekte noch gar nicht fest steht (also noch umdesignt werden kann), warum implementiert man es dann nicht passend? Okay, wenn das Objekt "World" in dem Beispiel nun unveränderlich, weil Teil einer anderen API wäre, dann bliebe einem noch Mujahiddins Lösung, jedoch gäbe es noch lange noch keinen Grund für mehrere verschiedene Comparatoren, andere Verrenkungen mit Generics oder gar Reflections.


----------



## Mujahiddin (27. Jul 2012)

Was mir grad in den Sinn kam:
Man will ja evtl nach Population ASC und Mass DESC sortieren.

```
enum Attributes{
    POPULATION_ASC, POPULATION_DESC,
    MASS_ASC, MASS_DESC,
    SURFACE_ASC, SURFACE_DESC
}
```
Woran mich das erinnert... Man könnte doch theoretisch auch Bits setzen.
Allerdings kenne ich mich in dem Gebiet fast gar nicht aus.
Also einen Integer und jede Stell im Bit steht für etwas (entweder für ein ASC/DESC oder für die Sortierung generell..) Wie gesagt, kenne mich da nicht aus, aber meine Vorstellung wäre ungefähr:
4 Bits pro Attribut:
1. Bit sagt aus, ob ASC oder DESC
letzten 3 Bits (also insgesamt 2^3 = 8 Möglichkeiten, wenn man davon ausgeht, dass man nicht mehr als 8 Attribute verwenden wird) stehen für die Priorität in der Sortierreihenfolge. Mit 32 Bits hätte man sogar 32/4 = 8 Möglichkeiten. Das nenn ich Kalkulation. Sprich:
1001|0010|0000|1011|1101|1111|1110|1100
1.P  |2.P  |0.P  |3.P  |5. P | 7.P | 6.P  | 4. P
Man könnte ja vereinbaren, dass die ersten 4 Bits für POPULATION, die zweiten 4 Bits für MASS, dann für SURFACE, und der Rest für eventuelle zukünftige Attribute belegt sind.
Hat da vielleicht jemand Ahnung im Gebiet und weiß, wie man sowas implementiert? Das wäre sicher ziemlich interessant!


----------



## Wishmaster51 (27. Jul 2012)

Mujahiddin hat gesagt.:


> Woran mich das erinnert... Man könnte doch theoretisch auch Bits setzen.


Könnte man, aber wiso sollte man das? :noe:


----------



## Spacerat (27. Jul 2012)

@Mujahiddin: Du kennst dich ja aus...  Den Beitrag mid dem "addAll" hatte ich komplett übersehen. Der Tipp funktioniert sogar. Hatte irrtümlich angenommen, "addAll" würde alle anderen überschreiben aber das tut es nicht. Was ein (Hash)Set allerdings nicht tut, ist die Sortierreihenfolge behalten.  Von daher muss aus den Set "order" nun halt eine List werden und dein Tipp ist damit obsolet, sorry. Zu deinen zusätzlichen ENUMS für ASC und DESC... wie soll' ich denn jetzt blos meine Pirioritätenliste im Komparator validieren? Etwa mit BitSets? :lol:


----------



## Marco13 (27. Jul 2012)

Spacerat hat gesagt.:


> Fragwürdig ist nun mehr; Wenn man so etwas vor hat und die Schnittstelle der zu sortierenden Objekte noch gar nicht fest steht (also noch umdesignt werden kann), warum implementiert man es dann nicht passend? Okay, wenn das Objekt "World" in dem Beispiel nun unveränderlich, weil Teil einer anderen API wäre, dann bliebe einem noch Mujahiddins Lösung, jedoch gäbe es noch lange noch keinen Grund für mehrere verschiedene Comparatoren, andere Verrenkungen mit Generics oder gar Reflections.



Da kommen wohl auch subjektive Einflüsse dazu. Aber eine Klasse wie die angedeutete "World", die eigentlich nur eine Map wrappt und deren Fields dann über Enums definiert sind klingt IMHO bedenklich. Es mag sicher Fälle geben, wo so etwas sinnvoll ist, aber doch nicht für ein "normales Datenmodell"? Dann fehlt na nur noch eine

```
public class World {
    public enum Action {
        ROTATE,
        MOVE,
        EXPLODE,
    }
    private Map<Action, Function> map =... 

    public void perform(Action e, Object args...) { map.get(e).execute(args); }

...
```
dann ist mal _vollkommen_ allgemein. Strukturfrei, aber allgemein... :bahnhof:


----------



## Mujahiddin (27. Jul 2012)

@Marco13, das was du suchst sind Predicates. Könnte dir FunctionalJava empfehlen.

@Spacerat: Das mit den Sets wusste ich nicht so genau.


AUF JEDEN FALL!
Ich habe mich mal in Bitoperatoren eingelesen und eine Menge dazugelernt...
Hab auch schon ein wenig angefangen und glaubt mir, das ist ein megaharter Brocken...
Ich geh jetzt allerdings pennen. Falls jemand daran weiterarbeiten will, kann er sich ja mein Fragment ansehen:

```
static int sortValue = 0x0; //TODO: set to a chosen default value!

public static final int[] PRIORITY = {
	0x1C, 0x18, 0x14, 0x10, // 28, 24, 20, 16
	0x0C, 0x08, 0x04, 0x00  // 12, 08, 04, 00 - Position of the bits ordered by priority.
};

public static final int POPULATION 	= 0b1110;
public static final int SURFACE		= 0b1100; // placeholder for each attribute in the bit-"array" of sortValue.
public static final int MASS		= 0b1010;

public static final int ASC = 0b1; // placeholders for the last bit of attribute
public static final int DESC = 0b0;

public static void setPriorityOrder(int attribute, int priority, int ascending) {
	int oldPrio = -1; // retrieve current attribute position
	for(int i=0; i<PRIORITY.length && oldPrio == -1; i++)
		if((sortValue & priority << PRIORITY[i]) == 0)
			oldPrio = i;
	int currentOrder = sortValue & 0b1111 << priority; // save the current attribute that is about to be replaced.
	
	//TODO: if (priority < oldPrio), shift to left, else shift to right
	//shift only until oldPrio and no further. Otherwise meaningful data is overwritten!
	
	
	sortValue = sortValue & ~(0b1111 << priority) | ((ascending | attribute) << priority); //update the 4 bits with parameters.
}

public static void main(String... args) throws Throwable {
	System.out.println(sortValue); // test if it worked... sortValue == 0!!!
	setPriorityOrder(POPULATION, PRIORITY[7], ASC);
	setPriorityOrder(SURFACE, PRIORITY[7], ASC);
	System.out.println(Integer.toBinaryString(sortValue));
}
```
=)


----------



## Spacerat (27. Jul 2012)

Marco13 hat gesagt.:


> Da kommen wohl auch subjektive Einflüsse dazu. Aber eine Klasse wie die angedeutete "World", die eigentlich nur eine Map wrappt und deren Fields dann über Enums definiert sind klingt IMHO bedenklich. Es mag sicher Fälle geben, wo so etwas sinnvoll ist, aber doch nicht für ein "normales Datenmodell"? Dann fehlt na nur noch eine
> 
> ```
> public class World {
> ...


Viel mehr hab' ich ja auch nicht behauptet und es müssen ja nicht immer Enums sein, das passte nur gerade als Antwort auf die Idee mit den (veralteten) ints. Es können z.B. auch Stringkonstanten oder besser unmodifizierbare Stringsets sein. Wie macht man's denn in Datenbanken mit den Feldern einer Tabelle? Soweit ich weis, ungefähr oder genau so.

@Mujahiddin: Ach hör doch auf... so hart können Bitoperationen doch gar nicht sein... hast doch nur zwei Ziffern... 0 und 1... OMG... was ist das? :lol: (Nur nicht drauf eingehen, ist OT. Glücklicherweise muss man sich in Java nur sehr selten bis gar nicht mit solchen Dingen befassen. )


----------



## bygones (28. Jul 2012)

Marco13 hat gesagt.:


> Bin ich der einzige, der es häßlich und fragwürdig findet, wenn die Tatsache, dass Objekte nach verschiedenen Kriterien sortiert werden können, nicht nur _Einfluß_ auf die Schnittstelle hat (was schon schlimm genug wäre), sondern die Schnittstelle dieser Objekte _komplett bestimmt_? Comparatoren sind ja eigentlich gerade dazu da, DAS zu vermeiden, oder nicht?



nein ueberhaupt nicht der einzige.... *schauder*


----------



## Claudia92 (28. Jul 2012)

Da hakt man den Thread als "erledigt" ab und nach zwei Tagen ist er voll mit interessanten Antworten. 

Mit Reflections habe ich es so gelöst:

```
class WorldComparator implements Comparator<World> {
		private String comparator;
		
		public WorldComparator(String comparator) {
			this.comparator = comparator;
		}
		
		public int compare(World o1, World o2) {
			try {
				return Integer.compare(o1.getClass().getDeclaredField(comparator).getInt(o1), o2.getClass().getDeclaredField(comparator).getInt(o2));
			} catch (IllegalAccessException | NoSuchFieldException e) {
				throw new IllegalArgumentException("no access to " + comparator + " in " + o1.getClass());
			}
		}
	}
```

Dass das kein guter Stil ist, ist mir bewusst, da bei

```
Collections.sort(worlds, new WorldComparator("surface"));
```
neben den Instanzvariablen population/surface/mass alles Mögliche eingegeben werden kann und die Exception erst zur Laufzeit fliegt. Insofern werde ich das so lieber nicht verwenden und bleibe dabei, für jede Instanzvariable eine eigene Comparator-Klasse zu schreiben. Aber interessant, dass so etwas überhaupt möglich ist.


----------



## bygones (28. Jul 2012)

Claudia92 hat gesagt.:


> Insofern werde ich das so lieber nicht verwenden und bleibe dabei, für jede Instanzvariable eine eigene Comparator-Klasse zu schreiben. Aber interessant, dass so etwas überhaupt möglich ist.


gute Wahl


----------



## Marco13 (29. Jul 2012)

Als ich die Frage gestellt hatte, habe ich schon auf eine Antwort von dir gehofft, weil ich vermutet hatte, dass sie so aussehen wird :toll: 
Das soll die geposteten und diskutierten Ansätze nicht diskreditieren. Es ist interessant, solche Ansätze zu sehen, einige davon bieten interessante Möglichkeiten, und es gibt sicher Fälle, wo man das eine oder andere, vielleicht in ähnlicher Form, in Betracht ziehen kann. Aber für den konkreten Fall erschienen sie mir eher unpassend. :bahnhof: (Wirklich beurteilen könnte das aber ohnehin nur der TO)


----------



## tagedieb (29. Jul 2012)

Hier hab ich noch eine Lösung ohne Comparator. Das LambdaJ Projekt bietet neben Sortierung auch andere mächtige Features. Mir gefällt die kompakte schreibweise ohne für jedes Sortierkriterium eine eigene Klasse zu schreiben.


```
//iterative version:
List<Sale> sortedSales = new ArrayList<Sale>(sales);
Collections.sort(sortedSales, new Comparator<Sale>() {
    public int compare(Sale s1, Sale s2) {
        return Double.valueOf(s1.getCost()).compareTo(s2.getCost());
    }
});

//lambdaj version:
List<Sale> sortedSales = LambdaJ.sort(sales, on(Sale.class).getCost());
```


----------



## Marco13 (29. Jul 2012)

Hey, das kannte ich noch nicht. Extreme Proxy Magic  Sieht ganz interessant aus.

Weil ich gerade sehe, dass "Proxy" einen Tooltip-Text hat, der irreführend sein könnte: Dort werden massivst Dynamic Proxy Classes verwendet. Ein ziemlich cooles feature, mit dem man eine Menge trickreicher Sachen machen kann.


----------



## bygones (30. Jul 2012)

Marco13 hat gesagt.:


> Hey, das kannte ich noch nicht. Extreme Proxy Magic  Sieht ganz interessant aus.


[OT]lambdaj ist ein cooler Ansatz, auch wenn sie ein paar restriktionen haben ala final klassen koennen nicht geproxied (?) werden etc. Ausserdem sollte man sich bewusst sein, dass die Performanz ein bisschen einbuesst (nachdem man anscheinend hier allgemein darauf immer Wert legt)[/OT]


----------

