Clasificación de una ArrayList de objetos mediante un orden de clasificación personalizado


119

Estoy buscando implementar una función de clasificación para mi aplicación de libreta de direcciones.

Quiero ordenar un ArrayList<Contact> contactArray. Contactes una clase que contiene cuatro campos: nombre, número de casa, número de móvil y dirección. Quiero ordenar name.

¿Cómo puedo escribir una función de clasificación personalizada para hacer esto?

Respuestas:


270

Aquí hay un tutorial sobre cómo ordenar objetos:

Aunque daré algunos ejemplos, recomendaría leerlo de todos modos.


Hay varias formas de ordenar un ArrayList. Si desea definir un naturales (por defecto) de pedidos , a continuación, es necesario dejar que Contactponer en práctica Comparable. Suponiendo que desea ordenar de forma predeterminada name, haga (se omiten las comprobaciones nulas para simplificar):

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

para que puedas hacer

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

Si desea definir un orden controlable externo (que anula el orden natural), entonces necesita crear un Comparator:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

Incluso puede definir los Comparators en Contactsí mismo para poder reutilizarlos en lugar de volver a crearlos cada vez:

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

que se puede utilizar de la siguiente manera:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

Y para aclarar la parte superior, podría considerar usar un comparador de Java genérico :

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

que puede utilizar de la siguiente manera:

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

(como puede ver en el código, posiblemente los campos nulos ya estén cubiertos para evitar NPE durante la clasificación)



2
De hecho, acabo de hacerlo. Más fácil que tratar de explicarme.
Stobor

@BalusC: No hay problemas. No puedo atribuirme el mérito de la idea, la obtuve de mis String.CASE_INSENSITIVE_ORDERamigos, pero me gusta. Hace que el código resultante sea más fácil de leer.
Stobor

1
Esas definiciones de Comparador probablemente también deberían ser staticy quizás finaltambién ... O algo así ...
Stobor

Jejeje ... ¡BeanComparator es como Awesome On A Stick! :-) (No recuerdo la comparación lógica exacta de valores nulos, pero ¿quiere un (o1 == null && o2 == null) ? 0 :al comienzo de esa línea de retorno?)
Stobor

28

Además de lo que ya se publicó debes saber que desde Java 8 podemos acortar nuestro código y escribirlo así:

Collection.sort(yourList, Comparator.comparing(YourClass::getFieldToSortOn));

o desde List ahora tiene sortmétodo

yourList.sort(Comparator.comparing(YourClass::getFieldToSortOn));

Explicación:

Desde Java 8, las interfaces funcionales (interfaces con un solo método abstracto; pueden tener más métodos predeterminados o estáticos) se pueden implementar fácilmente usando:

Dado que Comparator<T>solo tiene un método abstracto int compare(T o1, T o2), es una interfaz funcional.

Entonces, en lugar de (ejemplo de la respuesta de @BalusC )

Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

podemos reducir este código a:

Collections.sort(contacts, (Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress());
});

Podemos simplificar esta (o cualquier) lambda saltando

  • tipos de argumentos (Java los inferirá en función de la firma del método)
  • o {return...}

Entonces en lugar de

(Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress();
}

podemos escribir

(one, other) -> one.getAddress().compareTo(other.getAddress())

También ahora Comparatortiene métodos estáticos como comparing(FunctionToComparableValue)ocomparing(FunctionToValue, ValueComparator) que podríamos usar para crear fácilmente comparadores que deberían comparar algunos valores específicos de objetos.

En otras palabras, podemos reescribir el código anterior como

Collections.sort(contacts, Comparator.comparing(Contact::getAddress)); 
//assuming that Address implements Comparable (provides default order).

8

Esta página le dice todo lo que necesita saber sobre la clasificación de colecciones, como ArrayList.

Básicamente necesitas

  • haz que tu Contactclase implemente elComparable interfaz
    • creando un método public int compareTo(Contact anotherContact)dentro de él.
  • Una vez que hagas esto, puedes llamar Collections.sort(myContactList); ,
    • donde myContactListestá ArrayList<Contact>(o cualquier otra colección de Contact).

También hay otra forma, que implica la creación de una clase Comparator, y también puede leer sobre eso en la página vinculada.

Ejemplo:

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}

5

BalusC y bguiz ya han dado respuestas muy completas sobre cómo utilizar los comparadores integrados de Java.

Solo quiero agregar que google-collections tiene una clase Ordering que es más "poderosa" que los comparadores estándar. Podría valer la pena echarle un vistazo. Puede hacer cosas interesantes, como componer ordenaciones, invertirlas, ordenar según el resultado de una función para sus objetos ...

Aquí hay una publicación de blog que menciona algunos de sus beneficios.


Tenga en cuenta que google-collections ahora es parte de Guava (las bibliotecas java comunes de Google), por lo que es posible que desee depender de Guava (o del módulo de colección de Guava) si desea utilizar la clase Ordering.
Etienne Neveu

4

Necesita hacer que sus clases de contacto implementen Comparable y luego implementar el compareTo(Contact)método. De esa manera, Collections.sort podrá ordenarlos por usted. Según la página a la que me vinculé, compareTo 'devuelve un número entero negativo, cero o un número entero positivo ya que este objeto es menor, igual o mayor que el objeto especificado'.

Por ejemplo, si quisiera ordenar por nombre (de la A a la Z), su clase se vería así:

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}

Funcionó bien conmigo, gracias! También utilicé compareToIgnoreCase para ignorar el caso.
Rani Kheir

3

Al usar lambdaj , puede ordenar una colección de sus contactos (por ejemplo, por su nombre) de la siguiente manera

sort(contacts, on(Contact.class).getName());

o por su dirección:

sort(contacts, on(Contacts.class).getAddress());

y así. Más en general, ofrece un DSL para acceder y manipular sus colecciones de muchas formas, como filtrar o agrupar sus contactos en función de algunas condiciones, agregar algunos de sus valores de propiedad, etc.


0

Collections.sort es una buena implementación de ordenación. Si no tiene implementado el comparable para el contacto, deberá pasar una implementación del comparador

De nota:

El algoritmo de clasificación es un mergesort modificado (en el que la combinación se omite si el elemento más alto de la sublista baja es menor que el elemento más bajo de la sublista alta). Este algoritmo ofrece un rendimiento n log (n) garantizado. La lista especificada debe ser modificable, pero no necesita ser redimensionable. Esta implementación vuelca la lista especificada en una matriz, ordena la matriz e itera sobre la lista restableciendo cada elemento desde la posición correspondiente en la matriz. Esto evita el rendimiento n2 log (n) que resultaría de intentar ordenar una lista vinculada en su lugar.

La clasificación por combinación es probablemente mejor que la mayoría de los algoritmos de búsqueda que puede hacer.


0

Lo hice de la siguiente manera. número y nombre son dos listas de arrays. Tengo que ordenar el nombre. Si se produce algún cambio en el orden de la lista de matrices de nombres, la lista de matrices de números también cambiará su orden.

public void sortval(){

        String tempname="",tempnum="";

         if (name.size()>1) // check if the number of orders is larger than 1
            {
                for (int x=0; x<name.size(); x++) // bubble sort outer loop
                {
                    for (int i=0; i < name.size()-x-1; i++) {
                        if (name.get(i).compareTo(name.get(i+1)) > 0)
                        {

                            tempname = name.get(i);

                            tempnum=number.get(i);


                           name.set(i,name.get(i+1) );
                           name.set(i+1, tempname);

                            number.set(i,number.get(i+1) );
                            number.set(i+1, tempnum);


                        }
                    }
                }
            }



}

Vas a tardar más en escribir esto, obtendrás un rendimiento de clasificación menos óptimo, escribirás más errores (y con suerte más pruebas), y el código será más difícil de transferir a otras personas. Entonces no está bien. Puede funcionar, pero eso no lo hace correcto.
Eric

0

usa este método:

private ArrayList<myClass> sortList(ArrayList<myClass> list) {
    if (list != null && list.size() > 1) {
        Collections.sort(list, new Comparator<myClass>() {
            public int compare(myClass o1, myClass o2) {
                if (o1.getsortnumber() == o2.getsortnumber()) return 0;
                return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
            }
        });
    }
    return list;
}

'

y uso: mySortedlist = sortList(myList); No es necesario implementar comparador en su clase. Si desea un intercambio de orden inverso 1y-1


0

Ok, sé que esto fue respondido hace mucho tiempo ... pero, aquí hay información nueva:

Supongamos que la clase de contacto en cuestión ya tiene un orden natural definido mediante la implementación de Comparable, pero desea anular ese orden, por ejemplo, por nombre. Esta es la forma moderna de hacerlo:

List<Contact> contacts = ...;

contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());

De esta manera, primero ordenará por nombre (en orden inverso), y luego, para las colisiones de nombres, recurrirá al orden "natural" implementado por la propia clase Contact.


-1

Debe utilizar la función Arrays.sort. Las clases contenedoras deben implementar Comparable.


Por eso dije Arrays.
monksy

El problema es que OP está usando ArrayList, no array.
Pshemo
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.