Ich implementiere eine compareTo()
Methode für eine einfache Klasse wie diese (um Collections.sort()
andere von der Java-Plattform angebotene Extras verwenden zu können ):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
Ich möchte, dass die natürliche Reihenfolge für diese Objekte wie folgt lautet: 1) sortiert nach Namen und 2) sortiert nach Wert, wenn der Name derselbe ist; Bei beiden Vergleichen sollte die Groß- und Kleinschreibung nicht berücksichtigt werden. Für beide Felder sind Nullwerte durchaus akzeptabel und compareTo
dürfen in diesen Fällen nicht unterbrochen werden.
Die Lösung, die mir in den Sinn kommt, sieht wie folgt aus (ich verwende hier "Schutzklauseln", während andere vielleicht einen einzelnen Rückgabepunkt bevorzugen, aber das ist nebensächlich):
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(Metadata other) {
if (this.name == null && other.name != null){
return -1;
}
else if (this.name != null && other.name == null){
return 1;
}
else if (this.name != null && other.name != null) {
int result = this.name.compareToIgnoreCase(other.name);
if (result != 0){
return result;
}
}
if (this.value == null) {
return other.value == null ? 0 : -1;
}
if (other.value == null){
return 1;
}
return this.value.compareToIgnoreCase(other.value);
}
Das macht den Job, aber ich bin mit diesem Code nicht ganz zufrieden. Zugegeben, es ist nicht sehr komplex, aber ziemlich ausführlich und langweilig.
Die Frage ist, wie würden Sie dies weniger ausführlich machen (unter Beibehaltung der Funktionalität)? Sie können sich gerne auf Java-Standardbibliotheken oder Apache Commons beziehen, wenn diese helfen. Wäre die einzige Möglichkeit, dies (ein wenig) einfacher zu machen, die Implementierung meines eigenen "NullSafeStringComparator" und dessen Anwendung zum Vergleich beider Felder?
Änderungen 1-3 : Eddie hat recht; Der obige Fall "beide Namen sind null" wurde behoben
Über die akzeptierte Antwort
Ich habe diese Frage bereits 2009 gestellt, natürlich auf Java 1.6, und zu der Zeit war die reine JDK-Lösung von Eddie meine bevorzugte akzeptierte Antwort. Ich bin bis jetzt (2017) nie dazu gekommen, das zu ändern.
Es gibt auch Bibliothekslösungen von Drittanbietern - eine Apache Commons Collections 2009 und eine Guava 2013, die beide von mir veröffentlicht wurden -, die ich zu einem bestimmten Zeitpunkt bevorzugt habe.
Ich habe jetzt die saubere Java 8-Lösung von Lukasz Wiktor zur akzeptierten Antwort gemacht. Dies sollte auf jeden Fall unter Java 8 bevorzugt werden, und heutzutage sollte Java 8 für fast alle Projekte verfügbar sein.
quelle
Antworten:
Verwenden von Java 8 :
quelle
Collections.sort(List)
funktioniert dies nicht, wenn die Liste Nullen enthält. Der Kommentar ist jedoch für die Frage nicht relevant.Sie können einfach Apache Commons Lang verwenden :
quelle
nullsFirst()
/nullsLast()
.org.apache.commons.lang3
) "Legacy / schlecht gewartet / von geringer Qualität" ist, ist falsch oder bestenfalls unbegründet. Commons Lang3 ist leicht zu verstehen und zu verwenden und wird aktiv gewartet. Es ist wahrscheinlich meine am häufigsten verwendete Bibliothek (abgesehen von Spring Framework und Spring Security) - die StringUtils- Klasse mit ihren null-sicheren Methoden macht beispielsweise die Normalisierung von Eingaben trivial.Ich würde einen null sicheren Komparator implementieren. Es mag eine Implementierung geben, aber diese ist so einfach zu implementieren, dass ich immer meine eigene gerollt habe.
Hinweis: Wenn beide Namen null sind, vergleicht Ihr Komparator oben nicht einmal die Wertefelder. Ich glaube nicht, dass du das willst.
Ich würde dies mit so etwas wie dem folgenden implementieren:
BEARBEITEN: Tippfehler im Codebeispiel behoben. Das bekomme ich, wenn ich es nicht zuerst teste!
BEARBEITEN: nullSafeStringComparator wurde auf statisch hochgestuft.
quelle
final
Schlüsselwort ist nicht wirklich notwendig (Java-Code ist bereits so ausführlich wie er ist). Es verhindert jedoch die Wiederverwendung von Parametern als lokale Variablen (eine schreckliche Codierungspraxis.) As Unser kollektives Verständnis von Software wird mit der Zeit besser. Wir wissen, dass die Dinge standardmäßig endgültig / const / unveränderlich sein sollten. Daher bevorzuge ich ein bisschen mehr Ausführlichkeit bei der Verwendungfinal
in Parameterdeklarationen (wie trivial die Funktion auch sein mag), um sie zu erhalteninmutability-by-quasi-default
.) Der Aufwand für Verständlichkeit / Wartbarkeit ist im großen Schema der Dinge vernachlässigbar.Unten in dieser Antwort finden Sie eine aktualisierte (2013) Lösung mit Guava.
Damit bin ich letztendlich gegangen. Es stellte sich heraus, dass wir bereits eine Dienstprogrammmethode für den nullsicheren String-Vergleich hatten. Die einfachste Lösung bestand darin, diese zu nutzen. (Es ist eine große Codebasis; leicht zu übersehen :)
So wird der Helfer definiert (er ist überladen, sodass Sie auch definieren können, ob Nullen zuerst oder zuletzt kommen, wenn Sie möchten):
Dies ist also im Wesentlichen dasselbe wie Eddies Antwort (obwohl ich eine statische Hilfsmethode nicht als Komparator bezeichnen würde ) und die von Uzhin .
Im Allgemeinen hätte ich Patricks Lösung nachdrücklich favorisiert , da ich denke, dass es eine gute Praxis ist, etablierte Bibliotheken zu verwenden, wann immer dies möglich ist. ( Kennen und verwenden Sie die Bibliotheken, wie Josh Bloch sagt.) In diesem Fall hätte dies jedoch nicht den saubersten und einfachsten Code ergeben.
Edit (2009): Apache Commons Collections-Version
Hier ist eine Möglichkeit, die auf Apache Commons basierende Lösung zu
NullComparator
vereinfachen. Kombinieren Sie es mit der in der Klasse angegebenen Groß- und KleinschreibungComparator
String
:Das ist ziemlich elegant, denke ich. (Es bleibt nur ein kleines Problem: Die Commons
NullComparator
unterstützen keine Generika, daher gibt es eine ungeprüfte Zuordnung.)Update (2013): Guava-Version
Fast 5 Jahre später würde ich meine ursprüngliche Frage folgendermaßen angehen. Wenn ich in Java codiere, würde ich (natürlich) Guava verwenden . (Und ganz sicher nicht Apache Commons.)
Setzen Sie diese Konstante irgendwo ein, z. B. in die Klasse "StringUtils":
Dann in
public class Metadata implements Comparable<Metadata>
:Dies ist natürlich fast identisch mit der Apache Commons-Version (beide verwenden JDKs CASE_INSENSITIVE_ORDER ), wobei die Verwendung
nullsLast()
die einzige Guava-spezifische Sache ist. Diese Version ist einfach deshalb vorzuziehen, weil Guava als Abhängigkeit den Commons-Sammlungen vorzuziehen ist. (Wie alle zustimmen .)Wenn Sie sich gefragt haben
Ordering
, beachten Sie, dass es implementiertComparator
. Dies ist besonders für komplexere Sortieranforderungen sehr praktisch, da Sie beispielsweise mehrere Bestellungen mit verketten könnencompound()
. Lesen Sie die Bestellung erklärt für mehr!quelle
ComparatorChain
sodass Sie keine eigenecompareTo
Methode benötigen .Ich empfehle immer, Apache Commons zu verwenden, da es höchstwahrscheinlich besser ist als eines, das Sie selbst schreiben können. Außerdem können Sie dann "echte" Arbeit leisten, anstatt sie neu zu erfinden.
Die Klasse, an der Sie interessiert sind, ist der Nullkomparator . Sie können damit Nullen hoch oder niedrig setzen. Sie geben ihm auch einen eigenen Komparator, den Sie verwenden können, wenn die beiden Werte nicht null sind.
In Ihrem Fall können Sie eine statische Elementvariable haben, die den Vergleich durchführt, und dann
compareTo
verweist Ihre Methode nur darauf.So etwas wie
}}
Auch wenn Sie sich dazu entschließen, Ihre eigene zu würfeln, sollten Sie diese Klasse berücksichtigen, da sie sehr nützlich ist, wenn Sie Listen bestellen, die Nullelemente enthalten.
quelle
Ich weiß, dass Ihre Frage möglicherweise nicht direkt beantwortet wird, da Sie gesagt haben, dass Nullwerte unterstützt werden müssen.
Ich möchte jedoch nur darauf hinweisen, dass die Unterstützung von Nullen in compareTo nicht mit dem in den offiziellen Javadocs für Comparable beschriebenen compareTo-Vertrag übereinstimmt :
Also würde ich entweder NullPointerException explizit auslösen oder es einfach beim ersten Mal auslösen lassen, wenn das Null-Argument dereferenziert wird.
quelle
Sie können Methode extrahieren:
}}
quelle
Sie können Ihre Klasse so gestalten, dass sie unveränderlich ist (Effective Java 2nd Ed. Enthält einen großartigen Abschnitt dazu, Punkt 15: Minimieren der Veränderlichkeit) und bei der Erstellung sicherstellen, dass keine Nullen möglich sind (und bei Bedarf das Nullobjektmuster verwenden ). Dann können Sie alle diese Überprüfungen überspringen und sicher annehmen, dass die Werte nicht null sind.
quelle
Ich suchte nach etwas Ähnlichem und das schien ein bisschen kompliziert zu sein, also tat ich das. Ich denke, es ist etwas einfacher zu verstehen. Sie können es als Komparator oder als Einzeiler verwenden. Für diese Frage würden Sie zu compareToIgnoreCase () wechseln. Wie es ist, schweben Nullen auf. Sie können die 1, -1 umdrehen, wenn Sie möchten, dass sie sinken.
.
quelle
Wir können Java 8 verwenden, um einen nullfreundlichen Vergleich zwischen Objekten durchzuführen. Angenommen, ich habe eine Boy-Klasse mit 2 Feldern: String-Name und Integer-Alter und ich möchte zuerst Namen und dann Alter vergleichen, wenn beide gleich sind.
und das Ergebnis:
quelle
Für den Fall, dass jemand Spring verwendet, gibt es eine Klasse org.springframework.util.comparator.NullSafeComparator, die dies auch für Sie erledigt. Dekorieren Sie einfach Ihre eigenen vergleichbar damit
new NullSafeComparator<YourObject>(new YourComparable(), true)
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/comparator/NullSafeComparator.html
quelle
Für den speziellen Fall, in dem Sie wissen, dass die Daten keine Nullen haben (immer eine gute Idee für Zeichenfolgen) und die Daten sehr groß sind, führen Sie noch drei Vergleiche durch, bevor Sie die Werte tatsächlich vergleichen. Wenn Sie sicher sind, dass dies Ihr Fall ist , Sie können ein bisschen optimieren. YMMV als lesbarer Code übertrifft geringfügige Optimierungen:
quelle
Ausgabe ist
quelle
Eine der einfachen Möglichkeiten zur Verwendung von NullSafe Comparator ist die Verwendung der Spring-Implementierung. Im Folgenden finden Sie eines der einfachen Beispiele, auf die verwiesen wird:
quelle
Ein weiteres Beispiel für Apache ObjectUtils. Kann andere Arten von Objekten sortieren.
quelle
Dies ist meine Implementierung, mit der ich meine ArrayList sortiere. Die Nullklassen werden bis zum letzten sortiert.
In meinem Fall erweitert EntityPhone EntityAbstract und mein Container ist List <EntityAbstract>.
Die Methode "compareIfNull ()" wird für die nullsichere Sortierung verwendet. Die anderen Methoden dienen der Vollständigkeit und zeigen, wie compareIfNull verwendet werden kann.
quelle
Wenn Sie einen einfachen Hack wollen:
Wenn Sie Nullen an das Ende der Liste setzen möchten, ändern Sie dies einfach in der obigen Metod
quelle