2011-07-27 137 views
25

Tengo una lista de objetos Java que quiero ordenar de acuerdo con más de un campo.Ordenar una lista de objetos por varios campos

public class graduationCeremony { 
    String campus; 
    String faculty; 
    String building; 
} 

¿Es posible utilizar una o la interfaz ComparatorComparable para ordenar la lista de acuerdo a múltiples campos? Todos los ejemplos que he visto se ordenan según un solo campo. En otras palabras, uno puede ordenar por 'campus' O 'facultad' O 'edificio'. Quiero ordenar por 'campus', luego 'facultad', luego 'compilación' (como existe en SQL: ORDER BY campus, faculty, building)

Creo que esta pregunta ha sido asked before, pero no entiendo la respuesta aceptada. ¿Alguien puede expandir o ilustrar esta respuesta?

+1

La segunda respuesta a esa pregunta es una buena ilustración. –

+0

¿Esto ayuda: http://stackoverflow.com/questions/1206073/sorting-a-collection-of-objects – jjnguy

+2

@sim, entonces ¿por qué no te tomas el tiempo para entender, en lugar de buscar el código? – Moonbeam

Respuesta

48

Su Comparador se vería así:

public class GraduationCeremonyComparator implements Comparator<GraduationCeremony> { 
    public int compare(GraduationCeremony o1, GraduationCeremony o2) { 
     int value1 = o1.campus.compareTo(o2.campus); 
     if (value1 == 0) { 
      int value2 = o1.faculty.compareTo(o2.faculty); 
      if (value2 == 0) { 
       return o1.building.compareTo(o2.building); 
      } else { 
       return value2; 
      } 
     } 
     return value1; 
    } 
} 

Básicamente se sigue comparando cada atributo sucesiva de su clase siempre que la comparación atributos hasta el momento son iguales (== 0) .

+0

Gracias. Tu explicación ayudó a que cayera el centavo. Tengo una comprensión más clara del uso del método compare() ahora que no tenía antes. – sim

+1

no olvide sus comprobaciones nulas. La línea 'int value1 = o1.campus.compareTo (o2.campus);' lanzará una NullPointerException si o1 es nula –

0

espero que esto ayude:

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.Comparator; 
import java.util.Iterator; 

class Person implements Comparable { 
    String firstName, lastName; 

    public Person(String f, String l) { 
    this.firstName = f; 
    this.lastName = l; 
    } 

    public String getFirstName() { 
    return firstName; 
    } 

    public String getLastName() { 
    return lastName; 
    } 

    public String toString() { 
    return "[ firstname=" + firstName + ",lastname=" + lastName + "]"; 
    } 

    public int compareTo(Object obj) { 
    Person emp = (Person) obj; 
    int deptComp = firstName.compareTo(emp.getFirstName()); 

    return ((deptComp == 0) ? lastName.compareTo(emp.getLastName()) : deptComp); 
    } 

    public boolean equals(Object obj) { 
    if (!(obj instanceof Person)) { 
     return false; 
    } 
    Person emp = (Person) obj; 
    return firstName.equals(emp.getFirstName()) && lastName.equals(emp.getLastName()); 
    } 
} 

class PersonComparator implements Comparator<Person> { 
    public int compare(Person emp1, Person emp2) { 
    int nameComp = emp1.getLastName().compareTo(emp2.getLastName()); 
    return ((nameComp == 0) ? emp1.getFirstName().compareTo(emp2.getFirstName()) : nameComp); 
    } 
} 

public class Main { 
    public static void main(String args[]) { 
    ArrayList<Person> names = new ArrayList<Person>(); 
    names.add(new Person("E", "T")); 
    names.add(new Person("A", "G")); 
    names.add(new Person("B", "H")); 
    names.add(new Person("C", "J")); 

    Iterator iter1 = names.iterator(); 
    while (iter1.hasNext()) { 
     System.out.println(iter1.next()); 
    } 
    Collections.sort(names, new PersonComparator()); 
    Iterator iter2 = names.iterator(); 
    while (iter2.hasNext()) { 
     System.out.println(iter2.next()); 
    } 
    } 
} 
29

Sí, absolutamente puede hacer esto. Por ejemplo:

public class PersonComparator implements Comparator<Person> 
{ 
    public int compare(Person p1, Person p2) 
    { 
     // Assume no nulls, and simple ordinal comparisons 

     // First by campus - stop if this gives a result. 
     int campusResult = p1.getCampus().compareTo(p2.getCampus()); 
     if (campusResult != 0) 
     { 
      return campusResult; 
     } 

     // Next by faculty 
     int facultyResult = p1.getFaculty().compareTo(p2.getFaculty()); 
     if (facultyResult != 0) 
     { 
      return facultyResult; 
     } 

     // Finally by building 
     return p1.getBuilding().compareTo(p2.getBuilding()); 
    } 
} 

Básicamente lo que está diciendo, "Si yo puedo decir cuál viene primero con sólo mirar el campus (antes de que provienen de diferentes campus y el campus es el campo más importante) entonces Simplemente devolveré ese resultado. De lo contrario, continuaré comparando facultades. De nuevo, deténgase si eso es suficiente para distinguirlos. De lo contrario, (si el campus y la facultad son iguales para ambas personas) simplemente use el resultado de comparar ellos construyendo ".

+0

Una implementación elegante. – Gravity

+2

legibilidad ... ¡esta debería ser la respuesta correcta! – BBaker

1

Solo necesita que su clase herede de Comparable.

luego implemente el método compareTo de la manera que desee.

0

Tiene que escribir su propio método compareTo() que tiene el código Java necesario para realizar la comparación.

Si quisiéramos, por ejemplo, para comparar dos campos públicos, campus, a continuación, la facultad, podríamos hacer algo como:

int compareTo(GraduationCeremony gc) 
{ 
    int c = this.campus.compareTo(gc.campus); 

    if(c != 0) 
    { 
     //sort by campus if we can 
     return c; 
    } 
    else 
    { 
     //campus equal, so sort by faculty 
     return this.faculty.compareTo(gc.faculty); 
    } 
} 

Esto se simplifica pero espero que le da una idea. Consulte los documentos Comparable y Comparator para obtener más información.

4

Si sabe de antemano qué campos usar para hacer la comparación, entonces otras personas dieron respuestas correctas.
Lo que le puede interesar es ordenar su colección en caso de que no sepa en tiempo de compilación qué criterios aplicar. Imagine que tiene un programa trata de ciudades:



    protected Set<City> cities; 
    (...) 
    Field temperatureField = City.class.getDeclaredField("temperature"); 
    Field numberOfInhabitantsField = City.class.getDeclaredField("numberOfInhabitants"); 
    Field rainfallField = City.class.getDeclaredField("rainfall"); 
    program.showCitiesSortBy(temperatureField, numberOfInhabitantsField, rainfallField); 
    (...) 
    public void showCitiesSortBy(Field... fields) { 
     List<City> sortedCities = new ArrayList<City>(cities); 
     Collections.sort(sortedCities, new City.CityMultiComparator(fields)); 
     for (City city : sortedCities) { 
      System.out.println(city.toString()); 
     } 
    } 

que permita cambiar los nombres de campos no modificables por nombres de campo deducidas a partir de una solicitud del usuario en su programa.

En este ejemplo, City.CityMultiComparator<City> es una clase anidada estática de la clase City implementar Comparator:



    public static class CityMultiComparator implements Comparator<City> { 
     protected List<Field> fields; 

     public CityMultiComparator(Field... orderedFields) { 
      fields = new ArrayList<Field>(); 
      for (Field field : orderedFields) { 
       fields.add(field); 
      } 
     } 

     @Override 
     public int compare(City cityA, City cityB) { 
      Integer score = 0; 
      Boolean continueComparison = true; 
      Iterator itFields = fields.iterator(); 

      while (itFields.hasNext() && continueComparison) { 
       Field field = itFields.next(); 
       Integer currentScore = 0; 
       if (field.getName().equalsIgnoreCase("temperature")) { 
        currentScore = cityA.getTemperature().compareTo(cityB.getTemperature()); 
       } else if (field.getName().equalsIgnoreCase("numberOfInhabitants")) { 
        currentScore = cityA.getNumberOfInhabitants().compareTo(cityB.getNumberOfInhabitants()); 
       } else if (field.getName().equalsIgnoreCase("rainfall")) { 
        currentScore = cityA.getRainfall().compareTo(cityB.getRainfall()); 
       } 
       if (currentScore != 0) { 
        continueComparison = false; 
       } 
       score = currentScore; 
      } 

      return score; 
     } 
    } 


Es posible que desee añadir una capa extra de precisión, para especificar, por cada campo, ya sea que la clasificación sea ascendente o descendente. Creo que una solución es reemplazar Field objetos por los objetos de una clase que se podría llamar SortedField, que contiene un objeto Field, más otro campo que significa ascendiente o descendiente .

Cuestiones relacionadas