Mehrere Comparatoren

Claudia92

Mitglied
Hallo ihr Lieben! :)

Angenommen eine Klasse sollte über viele Instanzvariablen sortierbar sein:
Java:
class World {
	int population;
	int surface;
	int mass;
	int ...
}

Muss ich dann tatsächlich für jede Variable eine eigene Comparator-Klasse schreiben?
Java:
	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

Top Contributor
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).
 
G

Gonzo17

Gast
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.
 
S

Spacerat

Gast
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.
 
Zuletzt bearbeitet von einem Moderator:
W

Wishmaster51

Gast
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

Mitglied
@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
Code:
List<World> worlds
und werden dann z. B. mit
Code:
Collections.sort(worlds, new WorldPopulationComparator());
sortiert.

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

hüteüberhüte

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

Besser:
Code:
o1.value < o2.value ? -1 : o1.value > o2.value ? 1 : 0
 
H

hüteüberhüte

Gast
[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]
 
S

Spacerat

Gast
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
Code:
List<World> worlds
und werden dann z. B. mit
Code:
Collections.sort(worlds, new WorldPopulationComparator());
sortiert.
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: ;)
Java:
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]
 
Zuletzt bearbeitet von einem Moderator:

Claudia92

Mitglied
@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?
 
Zuletzt bearbeitet:

Landei

Top Contributor
@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
Code:
short
statt
Code:
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]
 
Zuletzt bearbeitet:
S

Spacerat

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

tagedieb

Top Contributor
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..
 
S

Spacerat

Gast
@Claudia: :oops: 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.
 
G

Gonzo17

Gast
@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. :)
 
W

Wishmaster51

Gast
@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.
 
B

bygones

Gast
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....
 
W

Wishmaster51

Gast
Ich dachte da an sowas wie:
Java:
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
                }
        }
    }
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
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")
 
W

Wishmaster51

Gast
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

Top Contributor
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
Java:
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.
 
Zuletzt bearbeitet:
W

Wishmaster51

Gast
- 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
- 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:
Java:
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

Gesperrter Benutzer
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.
Java:
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
 
W

Wishmaster51

Gast
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.
 
S

Spacerat

Gast
@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 :oops:). 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.
Java:
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;
		}
	}
}
 
Zuletzt bearbeitet von einem Moderator:

Mujahiddin

Top Contributor
Java:
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?
 
Zuletzt bearbeitet:

Mujahiddin

Top Contributor
@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?
 
S

Spacerat

Gast
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:
Java:
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;
		}
	}
}
 
Zuletzt bearbeitet von einem Moderator:

Marco13

Top Contributor
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...)
Java:
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

Top Contributor
Java:
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);
				}
			}
		}
	}
}

Ich würde aus dem letzten Block eher ein einfaches
Code:
order.addAll(DEFAULT);
machen.
 
S

Spacerat

Gast
@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

Top Contributor
Was mir grad in den Sinn kam:
Man will ja evtl nach Population ASC und Mass DESC sortieren.
Java:
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!
 
S

Spacerat

Gast
@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

Top Contributor
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
Java:
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

Top Contributor
@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:
Java:
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));
}
=)
 
S

Spacerat

Gast
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
Java:
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:
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. ;))
 
Zuletzt bearbeitet von einem Moderator:
B

bygones

Gast
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

Mitglied
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:
Java:
	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
Java:
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.
 

Marco13

Top Contributor
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

Top Contributor
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.

Java:
//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

Top Contributor
Hey, das kannte ich noch nicht. Extreme Proxy Magic :D 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.
 
Zuletzt bearbeitet:
B

bygones

Gast
Hey, das kannte ich noch nicht. Extreme Proxy Magic :D 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]
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
K Mehrere Werte in einem Switch Case parallel überprüfen Java Basics - Anfänger-Themen 23
S HashMap mehrere Keys zu einem Value Java Basics - Anfänger-Themen 3
T Mehrere if bedingungen ohne & Java Basics - Anfänger-Themen 2
I JPA Query für mehrere Klassen Java Basics - Anfänger-Themen 3
MiMa Java Doc mehrere Return Parameter Java Basics - Anfänger-Themen 11
javaBoon86 Array mehrere Dimensionen Java Basics - Anfänger-Themen 10
Buroto Klassen Mehrere .txt Datein verbienden und anschließend auslisten Java Basics - Anfänger-Themen 10
S mehrere TreeSets so speichern, dass man sie miteinander vergleichen kann Java Basics - Anfänger-Themen 1
P Zähler Variable für mehrere Objekte Java Basics - Anfänger-Themen 6
M Mehrere Daten/ Variablen Speichern Java Basics - Anfänger-Themen 9
C mehrere JPanel in ein JFrame bringen Java Basics - Anfänger-Themen 9
L Beim Java Programmstart, mehrere Parameter über die Kommandozeile übergeben Java Basics - Anfänger-Themen 9
D mehrere Berechnungen in einer Methode Java Basics - Anfänger-Themen 9
U Kann man bei Java gleich mehrere Bedingungen prüfen in der If, aber in einem "Satz"? Java Basics - Anfänger-Themen 1
Kotelettklopfer Mehrere Projekte in einem Git verwalten Java Basics - Anfänger-Themen 10
I JAX-RS Mehrere Parameter in Query Java Basics - Anfänger-Themen 3
M mehrere Rückgabenwerte aus Methode Java Basics - Anfänger-Themen 7
A Input/Output Mehrere Csv-Dateien einlesen Java Basics - Anfänger-Themen 2
R Mehrere Buchstaben aus einem String entfernen Java Basics - Anfänger-Themen 1
TimoN11 Java - Eine oder mehrere Eingaben möglich machen Java Basics - Anfänger-Themen 6
M Mehrere Datenbank zugriffe über tomee.xml regeln? Java Basics - Anfänger-Themen 1
S Mehrere Probleme im Code Java Basics - Anfänger-Themen 7
Ich lerne Java. Methoden Mehrere Methoden mit Punkt Java Basics - Anfänger-Themen 45
M Ausgabe einer Liste welche mehrere Stacks enthält Java Basics - Anfänger-Themen 3
D OOP- Eine Klasse in mehrere Klassen aufteilen Java Basics - Anfänger-Themen 7
F Mehrere Server Sockets in einer Anwendung Java Basics - Anfänger-Themen 9
T DoWhile Schleife über mehrere Mothoden Java Basics - Anfänger-Themen 5
B Methoden Mehrere ähnliche Methoden zusammenfassen Java Basics - Anfänger-Themen 24
E Mehrere Eingabezeilen gleichzeitig einlesen Java Basics - Anfänger-Themen 7
C Mehrere Zufallswerte Java Basics - Anfänger-Themen 4
M Dijkstra Algorithmus in Graphen auf mehrere verschiedene Knoten anwenden lassen Java Basics - Anfänger-Themen 11
R Error, wenn mehrere Clients gleichzeitig die Verbindung beenden Java Basics - Anfänger-Themen 16
F Mehrere Exceptions in einem Catch-Block abfangen Java Basics - Anfänger-Themen 12
O Datei in mehrere kleine Dateien umwandeln Java Basics - Anfänger-Themen 47
Henri Mehrere Ordner umbenennen Java Basics - Anfänger-Themen 11
E Mehrere Arrays addieren mit Übertrag Java Basics - Anfänger-Themen 13
B mehrere Werte mit scanner und while schleife einlesen, max berechnen bzw addieren Java Basics - Anfänger-Themen 2
J Mehrere paintComponenten in einem Programm Java Basics - Anfänger-Themen 0
F Mehrere Buttons mit einem ActionListener abdecken Java Basics - Anfänger-Themen 24
B Jeweils den Parent bekommen -> mehrere Ebenen Java Basics - Anfänger-Themen 2
javajoshi mehrere Threads: Methoden zentral unterbringen Java Basics - Anfänger-Themen 8
M Erste Schritte Mehrere eingaben in einer Line vergleichen (if equals...) Java Basics - Anfänger-Themen 6
L Mehrere Jars im Unterordner Java Basics - Anfänger-Themen 2
L LibGDX - mehrere Screens Java Basics - Anfänger-Themen 1
J Sudoku mehrere Lösungen Java Basics - Anfänger-Themen 29
F Mehrere Instanzen der Klasse A EINER Instanz der Klasse B übergeben Java Basics - Anfänger-Themen 3
T Mehrere JFrames gleichzeitig öffnen Java Basics - Anfänger-Themen 6
O Erste Schritte Scanner mehrere male benutzen (Konsole) Java Basics - Anfänger-Themen 7
S mehrere If Bedingungen Java Basics - Anfänger-Themen 5
F Mehrere Zeilen zu einer Zeile zusammenfügen und in eine Datei schreiben Java Basics - Anfänger-Themen 1
B Mehrere Zahlen speichern Java Basics - Anfänger-Themen 60
M mehrere extends? Java Basics - Anfänger-Themen 19
N Datei Zeilenweise einlesen, Ausgabe mehrere Arrays Java Basics - Anfänger-Themen 7
C ButtonController löst mehrere Methoden aus Java Basics - Anfänger-Themen 5
D JPanel mehrere Formen zeichnen Java Basics - Anfänger-Themen 5
B Timer mehrere Male ausführen Java Basics - Anfänger-Themen 4
kilopack15 Mehrere Threads in einer Klasse Java Basics - Anfänger-Themen 8
F mehrere eingegebene Zahlen mit Zahlen von 1-9 multiplizieren Java Basics - Anfänger-Themen 18
F Mehrere Konstruktoren? Wofür? Java Basics - Anfänger-Themen 21
J Mehrere Eingabefelder programmiert (Zeigt Fehler an) Java Basics - Anfänger-Themen 6
Jinnai4 Mehrere Textfelder überprüfen Java Basics - Anfänger-Themen 16
N Mehrere Forms auf einem Panel Java Basics - Anfänger-Themen 6
Tommy Nightmare Variable auf mehrere Ungleichheiten prüfen Java Basics - Anfänger-Themen 18
D Mehrere Objekte in ein Objekt zusammenfassen Java Basics - Anfänger-Themen 16
D Input/Output Mehrere Befehle nacheinander ausführen Java Basics - Anfänger-Themen 20
K Mehrere Objekte anlegen Java Basics - Anfänger-Themen 23
N Integers aus Textdatei auslesen und mehrere Arrays erstellen Java Basics - Anfänger-Themen 9
S Verständnis - Frage mehrere SQL Statements in While Schleife Java Basics - Anfänger-Themen 0
J MVC Pattern, mehrere Controller/Views/Models Java Basics - Anfänger-Themen 0
J Mehrere IF Anweisungen und dazugehörige ELSE Java Basics - Anfänger-Themen 6
I Klassen Mehrere Java Klassen in einer .java Datei Java Basics - Anfänger-Themen 7
V Mehrere Dateien aus JFileChooser in eine ArrayList speichern Java Basics - Anfänger-Themen 2
F Mehrere Konstruktoren Java Basics - Anfänger-Themen 10
A Mehrere Radiobuttons Java Basics - Anfänger-Themen 3
B Klassen Mehrere Objekte mit Schleife erstellen - How? Java Basics - Anfänger-Themen 1
T Mehrere Methoden in der main-Methode verknüpfen und aufeinander anwenden Java Basics - Anfänger-Themen 2
V OOP Aufnahme von Dreiecken in ein/mehrere Objekte Java Basics - Anfänger-Themen 0
Q OOP Mehrere Instanzen auf ein Feld Java Basics - Anfänger-Themen 13
C Klasse auf mehrere Objekte zugreifen lassen Java Basics - Anfänger-Themen 26
G Mehrere If-else-Sätze der Reihe nach durchlaufen lassen Java Basics - Anfänger-Themen 2
K Mehrere String.valueOf() kürzer schreiben / "packen"? Java Basics - Anfänger-Themen 2
F String mehrere male ausgeben? Java Basics - Anfänger-Themen 4
H wie mehrere variablen in einfacher for-schleife? Java Basics - Anfänger-Themen 2
H möglichkeiten für for-schleife? (mehrere ausgangsvariablen?) Java Basics - Anfänger-Themen 9
M PdfBox - mehrere Formularseiten Java Basics - Anfänger-Themen 2
Z Mehrere XML-Dateien zu einer zusammenfügen Java Basics - Anfänger-Themen 3
M GUI- mehrere Komponenten auf Container adden Java Basics - Anfänger-Themen 2
I Erste Schritte Resource Bundle - Alles in einem File oder mehrere? => Faktor Performance Java Basics - Anfänger-Themen 2
F Methoden split() - Mehrere Zeichen Java Basics - Anfänger-Themen 5
F Erste Schritte Mehrere nextInt() Eingaben nebeneinander ausgeben Java Basics - Anfänger-Themen 12
A mehrere Panels in eigenen Klasssen in einem Frame Java Basics - Anfänger-Themen 16
A Mehrere 100.000 Elemente verlgeichen Java Basics - Anfänger-Themen 8
T Compiler-Fehler Mit Array und "for" mehrere ImageIcon erstellen Java Basics - Anfänger-Themen 7
N Mehrere berechnungen auf einem Button legen? Java Basics - Anfänger-Themen 19
F Swing Applet: mehrere Komponenten hinzufügen Java Basics - Anfänger-Themen 1
F Speicherlast mehrere GB durch kleine png files? Java Basics - Anfänger-Themen 1
C Mehrere Tasten beim KeyListener gleichzeitig lesen Java Basics - Anfänger-Themen 2
M Mehrere Variabeln zusammenfassen Java Basics - Anfänger-Themen 4
M Erste Schritte mehrere Objekte aus einer Klasse Java Basics - Anfänger-Themen 4
L String extrahieren mit Regular Expression über mehrere Zeilen hinweg Java Basics - Anfänger-Themen 1

Ähnliche Java Themen

Neue Themen


Oben