¿Cómo utilizo Comparator para definir un orden de clasificación personalizado?

91

Quiero desarrollar una demostración de clasificación para la lista de autos. Estoy usando la tabla de datos para mostrar la lista de autos. Ahora, en realidad, quiero ordenar la lista por color de automóvil. Aquí no está ordenado por orden alfabético. Quiero usar mi orden de clasificación personalizado, como el automóvil rojo viene primero, luego el azul, etc.

Para eso trato de usar Java Comparatory Comparablesolo permite ordenar en orden alfabético.

Entonces, ¿alguien puede guiarme en la forma de implementar la técnica a usar para que la clasificación sea más rápida?

class Car implements Comparable<Car>
{
    private String name;
    private String color;

    public Car(String name, String color){
        this.name = name;
        this.color = color;
    }

    //Implement the natural order for this class
    public int compareTo(Car c) {
        return name.compareTo(c.name);
    }

    static class ColorComparator implements Comparator<Car> {
        public int compare(Car c1, Car c2) {
            String a1 = c1.color;
            String a2 = c2.color;
            return a1.compareTo(a2);
        }
    }

    public static void main(String[] args) {
        List<Car> carList = new ArrayList<>();
        List<String> sortOrder = new ArrayList<>();

        carList.add(new Car("Ford","Silver"));
        carList.add(new Car("Tes","Blue"));
        carList.add(new Car("Honda","Magenta"));

        sortOrder.add("Silver");
        sortOrder.add("Magenta");
        sortOrder.add("Blue");

        // Now here I am confuse how to implement my custom sort             
    }
}
akhtar
fuente

Respuestas:

101

Te recomiendo que crees una enumeración para los colores de tu coche en lugar de usar Strings y el orden natural de la enumeración será el orden en el que declares las constantes.

public enum PaintColors {
    SILVER, BLUE, MAGENTA, RED
}

y

 static class ColorComparator implements Comparator<CarSort>
 {
     public int compare(CarSort c1, CarSort c2)
     {
         return c1.getColor().compareTo(c2.getColor());
     }
 }

Cambia String a PaintColor y luego en la lista principal de su automóvil se convierte en:

carList.add(new CarSort("Ford Figo",PaintColor.SILVER));

...

Collections.sort(carList, new ColorComparator());
z7sg Ѫ
fuente
¿Cómo ejecutaría este ejemplo? No se accede a PaintColors en ColorComparator. ¿Podría ilustrar cómo se vería el método principal?
Deepak
cual es la salida? ¿Siempre es PLATA primero?
Deepak
@Deepak: sí, el orden natural de los enumvalores es el orden en que se definen sus valores.
Joachim Sauer
1
@SeanPatrickFloyd No creo que depender del orden natural de las enumeraciones sea una buena práctica.
Mike
1
@ Mike No estoy de acuerdo, pero hay argumentos válidos para ambos puntos de vista
Sean Patrick Floyd
57

Qué tal esto:

List<String> definedOrder = // define your custom order
    Arrays.asList("Red", "Green", "Magenta", "Silver");

Comparator<Car> comparator = new Comparator<Car>(){

    @Override
    public int compare(final Car o1, final Car o2){
        // let your comparator look up your car's color in the custom order
        return Integer.valueOf(
            definedOrder.indexOf(o1.getColor()))
            .compareTo(
                Integer.valueOf(
                    definedOrder.indexOf(o2.getColor())));
    }
};

En principio, estoy de acuerdo en que usar un enumes un enfoque aún mejor, pero esta versión es más flexible ya que le permite definir diferentes órdenes de clasificación.

Actualizar

Guayaba tiene esta funcionalidad incorporada en su Orderingclase:

List<String> colorOrder = ImmutableList.of("red","green","blue","yellow");
final Ordering<String> colorOrdering = Ordering.explicit(colorOrder);
Comparator<Car> comp = new Comparator<Car>() {
    @Override
    public int compare(Car o1, Car o2) {
        return colorOrdering.compare(o1.getColor(),o2.getColor());
    }
}; 

Esta versión es un poco menos detallada.


Actualizar de nuevo

Java 8 hace que el Comparador sea aún menos detallado:

Comparator<Car> carComparator = Comparator.comparing(
        c -> definedOrder.indexOf(c.getColor()));
Sean Patrick Floyd
fuente
¿Puedes por favor investigar esta pregunta también? Gracias. stackoverflow.com/questions/61934313/…
user2048204
25

Comparador en línea ...

List<Object> objList = findObj(name);
Collections.sort(objList, new Comparator<Object>() {
    @Override
    public int compare(Object a1, Object a2) {
        return a1.getType().compareToIgnoreCase(a2.getType());
    }
});
Silvio Troia
fuente
11

Creo que esto se puede hacer de la siguiente manera:

class ColorComparator implements Comparator<CarSort>
{
    private List<String> sortOrder;
    public ColorComparator (List<String> sortOrder){
        this.sortOrder = sortOrder;
    }

    public int compare(CarSort c1, CarSort c2)
    {
        String a1 = c1.getColor();
        String a2 = c2.getColor();
        return sortOrder.indexOf(a1) - sortOrder.indexOf(a2);
     }
 }

Para clasificar use esto:

Collections.sort(carList, new ColorComparator(sortOrder));
ilalex
fuente
7

Tuve que hacer algo similar a la respuesta de Sean e ilalex.
Pero tenía demasiadas opciones para definir explícitamente el orden de clasificación y solo necesitaba hacer flotar ciertas entradas al frente de la lista ... en el orden especificado (no natural).
Con suerte, esto será útil para otra persona.

public class CarComparator implements Comparator<Car> {

    //sort these items in this order to the front of the list 
    private static List<String> ORDER = Arrays.asList("dd", "aa", "cc", "bb");

    public int compare(final Car o1, final Car o2) {
        int result = 0;
        int o1Index = ORDER.indexOf(o1.getName());
        int o2Index = ORDER.indexOf(o2.getName());
        //if neither are found in the order list, then do natural sort
        //if only one is found in the order list, float it above the other
        //if both are found in the order list, then do the index compare
        if (o1Index < 0 && o2Index < 0) result = o1.getName().compareTo(o2.getName());
        else if (o1Index < 0) result = 1;
        else if (o2Index < 0) result = -1;
        else result = o1Index - o2Index;
        return result;
    }

//Testing output: dd,aa,aa,cc,bb,bb,bb,a,aaa,ac,ac,ba,bd,ca,cb,cb,cd,da,db,dc,zz
}
matt1616
fuente
4

Haré algo como esto:

List<String> order = List.of("Red", "Green", "Magenta", "Silver");

Comparator.comparing(Car::getColor(), Comparator.comparingInt(c -> order.indexOf(c)))

Todos los créditos van a @Sean Patrick Floyd :)

MariuszS
fuente
3

En Java 8 puedes hacer algo como esto:

Primero necesitas un Enum:

public enum Color {
    BLUE, YELLOW, RED
}

Clase de auto:

public class Car {

    Color color;

    ....

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }
}

Y luego, usando su lista de autos, simplemente puede hacer:

Collections.sort(carList, Comparator:comparing(CarSort::getColor));
Thermech
fuente
Este no es un tipo personalizado.
zygimantus
Además, no es una forma "funcional" de hacerlo ... ¡¡esto tiene efectos secundarios !!
TriCore
2

Defina un tipo de enumeración como

public enum Colors {
     BLUE, SILVER, MAGENTA, RED
}

Cambiar el tipo de datos de colordesde Stringa Colors Cambiar el tipo de retorno y el tipo de argumento del getter y el método de setter de colorColors

Defina el tipo de comparador de la siguiente manera

static class ColorComparator implements Comparator<CarSort>
{
    public int compare(CarSort c1, CarSort c2)
    {
        return c1.getColor().compareTo(c2.getColor());
    }
}

después de agregar elementos a la Lista, llame al método de clasificación de Colección pasando la lista y los objetos comparadores como argumentos

es decir, Collections.sort(carList, new ColorComparator()); luego imprima usando ListIterator.

La implementación de clase completa es la siguiente:

package test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;    
import java.util.ListIterator;

public class CarSort implements Comparable<CarSort>{

    String name;
    Colors color;

    public CarSort(String name, Colors color){
        this.name = name;
        this.color = color;
    } 

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Colors getColor() {
        return color;
    }
    public void setColor(Colors color) {
        this.color = color;
    }

    //Implement the natural order for this class
    public int compareTo(CarSort c)
    {
        return getName().compareTo(c.getName());
    }

    static class ColorComparator implements Comparator<CarSort>
    {
        public int compare(CarSort c1, CarSort c2)
        {
            return c1.getColor().compareTo(c2.getColor());
        }
    }

    public enum Colors {
         BLUE, SILVER, MAGENTA, RED
    }

     public static void main(String[] args)
     {
         List<CarSort> carList = new ArrayList<CarSort>();
         List<String> sortOrder = new ArrayList<String>();

         carList.add(new CarSort("Ford Figo",Colors.SILVER));
         carList.add(new CarSort("Santro",Colors.BLUE));
         carList.add(new CarSort("Honda Jazz",Colors.MAGENTA));
         carList.add(new CarSort("Indigo V2",Colors.RED));
         Collections.sort(carList, new ColorComparator());

         ListIterator<CarSort> itr=carList.listIterator();
         while (itr.hasNext()) {
            CarSort carSort = (CarSort) itr.next();
            System.out.println("Car colors: "+carSort.getColor());
        }
     }
}
Siddu
fuente
0

Usando bucles simples:

public static void compareSortOrder (List<String> sortOrder, List<String> listToCompare){
        int currentSortingLevel = 0;
        for (int i=0; i<listToCompare.size(); i++){
            System.out.println("Item from list: " + listToCompare.get(i));
            System.out.println("Sorting level: " + sortOrder.get(currentSortingLevel));
            if (listToCompare.get(i).equals(sortOrder.get(currentSortingLevel))){

            } else {
                try{
                    while (!listToCompare.get(i).equals(sortOrder.get(currentSortingLevel)))
                        currentSortingLevel++;
                    System.out.println("Changing sorting level to next value: " + sortOrder.get(currentSortingLevel));
                } catch (ArrayIndexOutOfBoundsException e){

                }

            }
        }
    }

Y orden de clasificación en la lista

public static List<String> ALARMS_LIST = Arrays.asList(
            "CRITICAL",
            "MAJOR",
            "MINOR",
            "WARNING",
            "GOOD",
            "N/A");
tonto
fuente