Am'm de punere în aplicare compareTo () metoda pentru o clasă simplă, cum ar fi acest lucru (pentru a fi capabil de a utiliza Colecțiile.sort()
și alte bunătăți oferite de platforma Java):
public class Metadata implements Comparable<Metadata> {
private String name;
private String value;
// Imagine basic constructor and accessors here
// Irrelevant parts omitted
}
Vreau ordonare naturală pentru aceste obiecte să fie: 1) sortate în funcție de numele și 2) clasificate în funcție de valoare dacă numele este același; ambele comparații ar trebui să fie case-insensitive. Pentru ambele domenii valorile nule sunt perfect acceptabil, deci compareTo
nu trebuie să se rupă în aceste cazuri.
Soluția care vine în minte este de-a lungul liniilor de următoarele (I'm, folosind "garda de clauze" aici, în timp ce alții s-ar putea prefera un singur punct de întoarcere, dar care'e pe lângă subiect):
// 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);
}
Acest lucru are loc de muncă, dar nu'm nu foarte fericit cu acest cod. Desigur, e't foarte complex, dar este destul de detaliată și obositoare.
Întrebarea este, cum ați face acest lucru mai puțin detaliat (păstrând în același timp funcționalitatea)? Simțiți-vă liber pentru a se referi la Java standard biblioteci sau Apache Commons dacă ele ajuta. Ar fi singura opțiune pentru a face acest lucru (un pic) mai simplu de a pune în aplicare propria mea "NullSafeStringComparator", și se aplică pentru a compara cele două domenii?
Modificări 1-3: Eddie's de dreapta; fix "ambele denumiri sunt nule de" cazul de mai sus
Am pus această întrebare în 2009, pe Java 1.6, desigur, și la timp pur JDK soluție de Eddie mea preferată răspunsul acceptat. N-am reușit să schimb asta până acum (2017).
Există, de asemenea, 3rd party biblioteca soluții—2009 Apache Commons Colecțiile și un 2013 Guava, atât postat de mine care am facut—o prefer la un moment dat în timp.
Acum am facut curat Java 8 soluție de Lukasz Wiktor acceptat răspunsul. Care ar fi cu siguranta de preferat, în cazul în Java 8, și în aceste zile Java 8 ar trebui să fie disponibile pentru aproape toate proiectele.
Puteți folosi pur și simplu Apache Commons Lang:
result = ObjectUtils.compare(firstComparable, secondComparable)
Folosind Java 8:
private static Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareToIgnoreCase);
private static Comparator<metadata> metadataComparator = Comparator
.comparing(Metadata::getName, nullSafeStringComparator)
.thenComparing(Metadata::getValue, nullSafeStringComparator);
public int compareTo(Metadata that) {
return metadataComparator.compare(this, that);
}
Mi-ar implementa un nul în condiții de siguranță comparator. Acolo poate fi o punere în aplicare acolo, dar acest lucru este atât de simplu să pună în aplicare pe care am'am întotdeauna laminate mea.
Notă: comparator de mai sus, dacă ambele denumiri sunt nule, a câștigat't chiar compara valoarea domenii. Eu nu't cred că acest lucru este ceea ce vrei.
Mi-ar implementa asta cu ceva de genul următor:
// primarily by name, secondarily by value; null-safe; case-insensitive
public int compareTo(final Metadata other) {
if (other == null) {
throw new NullPointerException();
}
int result = nullSafeStringComparator(this.name, other.name);
if (result != 0) {
return result;
}
return nullSafeStringComparator(this.value, other.value);
}
public static int nullSafeStringComparator(final String one, final String two) {
if (one == null ^ two == null) {
return (one == null) ? -1 : 1;
}
if (one == null && two == null) {
return 0;
}
return one.compareToIgnoreCase(two);
}
EDIT: typos Fixe în codul de probă. Ca's ceea ce primesc pentru a nu testează primul!
EDIT: Promovat nullSafeStringComparator statice.
A se vedea partea de jos a acest răspuns pentru actualizată (2013), utilizând soluția de Guava.
Aceasta este ceea ce am în cele din urmă s-a dus cu. S-a dovedit că a avut deja o utilitate metodă pentru null-în condiții de siguranță comparație Șir, așa că cea mai simplă soluție a fost să se folosească de asta. (It's un mare codebase; ușor să dor de acest tip de lucru :)
public int compareTo(Metadata other) {
int result = StringUtils.compare(this.getName(), other.getName(), true);
if (result != 0) {
return result;
}
return StringUtils.compare(this.getValue(), other.getValue(), true);
}
Acesta este modul în care helper este definit (it's supraîncărcat, astfel încât să puteți defini, de asemenea, dacă null venit, primul sau ultimul, dacă vrei):
public static int compare(String s1, String s2, boolean ignoreCase) { ... }
Deci, aceasta este, în esență, la fel ca Eddie's answer (deși mi-ar't apela un statice helper metoda a comparator) și care de uzhin prea.
Oricum, în general, mi-ar fi fost favorizat Patrick's solution, după cum cred că-l's-o bună practică de a utiliza stabilit biblioteci ori de câte ori este posibil. (Cunoaște și de a folosi biblioteci ca Josh Bloch spune.) Dar, în acest caz, care nu ar fi cedat cel mai curat, mai simplu cod.
De fapt, aici's o modalitate de a face soluții bazate pe Apache Commons NullComparator
mai simplu. Combina-l cu [case-insensitive "Comparator"] (http://java.sun.com/javase/6/docs/api/java/lang/String.html#CASE_INSENSITIVE_ORDER) prevăzute în "String" clasa:
public static final Comparator<String> NULL_SAFE_COMPARATOR
= new NullComparator(String.CASE_INSENSITIVE_ORDER);
@Override
public int compareTo(Metadata other) {
int result = NULL_SAFE_COMPARATOR.compare(this.name, other.name);
if (result != 0) {
return result;
}
return NULL_SAFE_COMPARATOR.compare(this.value, other.value);
}
Acum, acest lucru este destul de elegant, cred. (Doar o mica problema rămâne: Commons NullComparator
nu't de suport generice, deci nu's o necontrolate de atribuire.)
De aproape 5 ani mai târziu, aici's cum am'd aborda întrebarea mea inițială. Dacă codificare în Java, aș (desigur), fie folosind Guava. (Și destul de sigur nu Apache Commons.)
Pune această constantă pe undeva, de exemplu, în "StringUtils" clasa:
public static final Ordering<String> CASE_INSENSITIVE_NULL_SAFE_ORDER =
Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast(); // or nullsFirst()
Apoi, în public class Metadate implementează Comparabile<Metadate>
:
@Override
public int compareTo(Metadata other) {
int result = CASE_INSENSITIVE_NULL_SAFE_ORDER.compare(this.name, other.name);
if (result != 0) {
return result;
}
return CASE_INSENSITIVE_NULL_SAFE_ORDER.compare(this.value, other.value);
}
Desigur, acest lucru este aproape identic cu Apache Commons versiune (ambele folosesc JDK's CASE_INSENSITIVE_ORDER), utilizarea de nullsLast()` fiind singurul Guava-lucru specific. Această versiune este de preferat pur și simplu pentru că Guava este de preferat, ca o dependență, la Commons Colecții. (Ca toată lumea este de acord.)
Dacă ați fost întrebați despre Comanda
, rețineți că acesta pune în aplicare "Comparator". L's destul de la îndemână, mai ales pentru mai multe complexe de sortare are nevoie, permițându-vă pentru un exemplu de lanț de mai multe Ordonări folosind compuse()`. Citit Comanda Explicat pentru mai multe!
Intotdeauna mi-am recomanda utilizarea Apache commons, deoarece acesta va fi cel mai probabil mai bine decât o poți scrie pe cont propriu. În Plus, puteți face apoi 'real' de lucru, mai degrabă apoi reinventarea.
Clasa sunteti interesati este Nul Comparator]1. Acesta vă permite să facă null-uri mari sau mici. Ai, de asemenea, dă-ți singur comparator pentru a utiliza atunci când cele două valori nu sunt nule.
În cazul tău poți fi un membru static variabile care face comparație și apoi compareTo
metoda doar referințe asta.
Ceva de genul
class Metadata implements Comparable<metadata> {
private String name;
private String value;
static NullComparator nullAndCaseInsensitveComparator = new NullComparator(
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// inputs can't be null
return o1.compareToIgnoreCase(o2);
}
});
@Override
public int compareTo(Metadata other) {
if (other == null) {
return 1;
}
int res = nullAndCaseInsensitveComparator.compare(name, other.name);
if (res != 0)
return res;
return nullAndCaseInsensitveComparator.compare(value, other.value);
}
}
Chiar dacă vă decideți să se rostogolească, să păstrezi această clasă în minte, deoarece este foarte util atunci când comanda liste thatcontain null elemente.
Știu că poate fi nu a răspuns direct la întrebarea ta, pentru că ai spus că valorile nule trebuie să fie acceptate.
Dar eu vreau doar să rețineți că sprijinirea null în compareTo nu este în conformitate cu compareTo contract descrise în oficial javadocs Comparabile:
Rețineți că nul nu este o instanță de clasă, și e.compareTo(null) ar trebui să arunce o NullPointerException chiar dacă e.egal(null) întoarce fals.
Deci mi-ar arunca, fie NullPointerException în mod explicit sau doar sa-l fi aruncat prima dată când null este dereferenced.
Puteți extract metoda:
public int cmp(String txt, String otherTxt)
{
if ( txt == null )
return otjerTxt == null ? 0 : 1;
if ( otherTxt == null )
return 1;
return txt.compareToIgnoreCase(otherTxt);
}
public int compareTo(Metadata other) {
int result = cmp( name, other.name);
if ( result != 0 ) return result;
return cmp( value, other.value);
}
Ai putea proiecta clasa ta de a fi imuabile (Java 2-a Ed. are o secțiune mare pe acest, Articol 15: Minimiza mutabilitatea) și asigurați-vă că la construcție care nu valori nule sunt posibile (și de a folosi null object model dacă este necesar). Atunci puteți sări peste toate aceste verificări și în condiții de siguranță presupune că valorile nu sunt nule.
Am fost în căutarea pentru ceva similar și acest lucru pare un pic complicat, așa că am făcut acest lucru. Cred că's un pic mai ușor de înțeles. Îl puteți folosi ca un Comparator sau ca o singură linie. Pentru această întrebare le-ar schimba la compareToIgnoreCase(). Cum este, null pluti în sus. Puteți flip 1, -1, dacă vrei ca ei să se scufunde.
StringUtil.NULL_SAFE_COMPARATOR.compare(getName(), o.getName());
.
public class StringUtil {
public static final Comparator<String> NULL_SAFE_COMPARATOR = new Comparator<String>() {
@Override
public int compare(final String s1, final String s2) {
if (s1 == s2) {
//Nulls or exact equality
return 0;
} else if (s1 == null) {
//s1 null and s2 not null, so s1 less
return -1;
} else if (s2 == null) {
//s2 null and s1 not null, so s1 greater
return 1;
} else {
return s1.compareTo(s2);
}
}
};
public static void main(String args[]) {
final ArrayList<String> list = new ArrayList<String>(Arrays.asList(new String[]{"qad", "bad", "sad", null, "had"}));
Collections.sort(list, NULL_SAFE_COMPARATOR);
System.out.println(list);
}
}
putem folosi java 8-a face cu un null-friendly comparație între obiect. trebuia să petrec un Băiat de clasa cu 2 câmpuri: String nume și număr Întreg de vârstă și vreau să compara întâi numele și apoi vârstele dacă ambele sunt egale.
static void test2() {
List<Boy> list = new ArrayList<>();
list.add(new Boy("Peter", null));
list.add(new Boy("Tom", 24));
list.add(new Boy("Peter", 20));
list.add(new Boy("Peter", 23));
list.add(new Boy("Peter", 18));
list.add(new Boy(null, 19));
list.add(new Boy(null, 12));
list.add(new Boy(null, 24));
list.add(new Boy("Peter", null));
list.add(new Boy(null, 21));
list.add(new Boy("John", 30));
List<Boy> list2 = list.stream()
.sorted(comparing(Boy::getName,
nullsLast(naturalOrder()))
.thenComparing(Boy::getAge,
nullsLast(naturalOrder())))
.collect(toList());
list2.stream().forEach(System.out::println);
}
private static class Boy {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boy(String name, Integer age) {
this.name = name;
this.age = age;
}
public String toString() {
return "name: " + name + " age: " + age;
}
}
și rezultatul:
name: John age: 30
name: Peter age: 18
name: Peter age: 20
name: Peter age: 23
name: Peter age: null
name: Peter age: null
name: Tom age: 24
name: null age: 12
name: null age: 19
name: null age: 21
name: null age: 24
În cazul în care cineva folosind Primăvară, există o clasă org.springframework.util.comparator.NullSafeComparator
noi NullSafeComparator<YourObject>(nou YourComparable(), true)
Unul din simplu mod de a folosind NullSafe Comparator este de a utiliza Primăvară punerea în aplicare a, de mai jos este unul dintre cele simple, de exemplu să consultați :
public int compare(Object o1, Object o2) {
ValidationMessage m1 = (ValidationMessage) o1;
ValidationMessage m2 = (ValidationMessage) o2;
int c;
if (m1.getTimestamp() == m2.getTimestamp()) {
c = NullSafeComparator.NULLS_HIGH.compare(m1.getProperty(), m2.getProperty());
if (c == 0) {
c = m1.getSeverity().compareTo(m2.getSeverity());
if (c == 0) {
c = m1.getMessage().compareTo(m2.getMessage());
}
}
}
else {
c = (m1.getTimestamp() > m2.getTimestamp()) ? -1 : 1;
}
return c;
}
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Comparator;
public class TestClass {
public static void main(String[] args) {
Student s1 = new Student("1","Nikhil");
Student s2 = new Student("1","*");
Student s3 = new Student("1",null);
Student s11 = new Student("2","Nikhil");
Student s12 = new Student("2","*");
Student s13 = new Student("2",null);
List<Student> list = new ArrayList<Student>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s11);
list.add(s12);
list.add(s13);
list.sort(Comparator.comparing(Student::getName,Comparator.nullsLast(Comparator.naturalOrder())));
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Student student = (Student) iterator.next();
System.out.println(student);
}
}
}
de ieșire este
Student [name=*, id=1]
Student [name=*, id=2]
Student [name=Nikhil, id=1]
Student [name=Nikhil, id=2]
Student [name=null, id=1]
Student [name=null, id=2]
Aceasta este punerea în aplicare a mea pe care am folosi pentru a sorta mea ArrayList. null clasele sunt sortate de la ultimul.
pentru cazul meu, EntityPhone se extinde EntityAbstract și recipient meu este Lista < EntityAbstract>.
"compareIfNull()" metodă este folosită pentru nulă în condiții de siguranță de sortare. Alte metode sunt pentru completitudine, arată cum compareIfNull poate fi folosit.
@Nullable
private static Integer compareIfNull(EntityPhone ep1, EntityPhone ep2) {
if (ep1 == null || ep2 == null) {
if (ep1 == ep2) {
return 0;
}
return ep1 == null ? -1 : 1;
}
return null;
}
private static final Comparator<EntityAbstract> AbsComparatorByName = = new Comparator<EntityAbstract>() {
@Override
public int compare(EntityAbstract ea1, EntityAbstract ea2) {
//sort type Phone first.
EntityPhone ep1 = getEntityPhone(ea1);
EntityPhone ep2 = getEntityPhone(ea2);
//null compare
Integer x = compareIfNull(ep1, ep2);
if (x != null) return x;
String name1 = ep1.getName().toUpperCase();
String name2 = ep2.getName().toUpperCase();
return name1.compareTo(name2);
}
}
private static EntityPhone getEntityPhone(EntityAbstract ea) {
return (ea != null && ea.getClass() == EntityPhone.class) ?
(EntityPhone) ea : null;
}
Pentru cazul specific în care știi că datele nu vor fi null (întotdeauna o idee bună pentru siruri de caractere) și de date este foarte mare, sunt încă trei comparații de fapt, înainte de compararea valorilor, dacă ai ști sigur că este cazul tău, puteți optimiza un pic. YMMV ca poate fi citit codul de atuuri minore de optimizare:
if(o1.name != null && o2.name != null){
return o1.name.compareToIgnoreCase(o2.name);
}
// at least one is null
return (o1.name == o2.name) ? 0 : (o1.name != null ? 1 : -1);
Un alt Apache ObjectUtils exemplu. Posibilitatea de a sorta alte tipuri de obiecte.
@Override
public int compare(Object o1, Object o2) {
String s1 = ObjectUtils.toString(o1);
String s2 = ObjectUtils.toString(o2);
return s1.toLowerCase().compareTo(s2.toLowerCase());
}