Como uso o Comparator para definir uma ordem de classificação personalizada?

89

Quero desenvolver uma demonstração de classificação para a lista de carros. Estou usando a tabela de dados para exibir a lista de carros. Na verdade, quero classificar a lista pela cor do carro. Aqui, não é classificado por ordem alfabética. Quero usar minha ordem de classificação personalizada, como Carro vermelho vem primeiro, depois Azul etc.

Para isso tento usar Java Comparatore Comparablemas permite ordenar apenas em ordem alfabética.

Então, alguém pode me orientar na forma de implementar a técnica a ser utilizada para que a triagem seja mais 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
fonte

Respostas:

100

Eu recomendo que você crie um enum para as cores do carro em vez de usar Strings e a ordem natural do enum será a ordem em que você declara as constantes.

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

e

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

Você altera a String para PaintColor e, em seguida, em principal, sua lista de carros se torna:

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

...

Collections.sort(carList, new ColorComparator());
z7sg Ѫ
fonte
como eu executaria este exemplo. ColorColors não é acessado em ColorComparator. você poderia ilustrar como seria o método principal.
Deepak,
qual é a saída? é sempre PRATA primeiro?
Deepak,
@Deepak: sim, a ordem natural dos enumvalores é a ordem em que seus valores são definidos.
Joachim Sauer
1
@SeanPatrickFloyd Não acho que confiar na ordem natural de enums seja uma prática recomendada.
mike
1
@mike eu discordo, mas há argumentos válidos para ambos os pontos de vista
Sean Patrick Floyd
56

Que tal agora:

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())));
    }
};

Em princípio, concordo que usar um enumé uma abordagem ainda melhor, mas esta versão é mais flexível, pois permite definir diferentes ordens de classificação.

Atualizar

O Guava tem esta funcionalidade embutida em sua Orderingclasse:

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 versão é um pouco menos prolixa.


Atualizar novamente

Java 8 torna o Comparator ainda menos prolixo:

Comparator<Car> carComparator = Comparator.comparing(
        c -> definedOrder.indexOf(c.getColor()));
Sean Patrick Floyd
fonte
Você pode, por favor, examinar esta questão também. Te agradece. stackoverflow.com/questions/61934313/…
user2048204
25

Comparador em linha ...

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
fonte
11

Acho que isso pode ser feito da seguinte maneira:

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 classificação, use isto:

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

Eu tive que fazer algo semelhante à resposta de Sean e ilalex.
Mas eu tinha muitas opções para definir explicitamente a ordem de classificação e só precisava flutuar certas entradas para a frente da lista ... na ordem especificada (não natural).
Espero que isso seja útil para outra pessoa.

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
fonte
3

Vou fazer algo assim:

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

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

Todos os créditos vão para @Sean Patrick Floyd :)

MariuszS
fonte
2

No Java 8, você pode fazer algo assim:

Você precisa primeiro de um Enum:

public enum Color {
    BLUE, YELLOW, RED
}

Classe de carro:

public class Car {

    Color color;

    ....

    public Color getColor() {
        return color;
    }

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

E então, usando sua lista de carros, você pode simplesmente fazer:

Collections.sort(carList, Comparator:comparing(CarSort::getColor));
Thermech
fonte
Esta não é uma classificação personalizada.
zygimantus de
Além disso, não é uma maneira "funcional" de fazer isso ... isso tem efeitos colaterais !!
TriCore
2

Defina um tipo de Enum como

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

Alterar tipo de dados colora partir Stringde Colors alteração tipo de retorno e tipo de argumento de getter e setter método de cor paraColors

Defina o tipo de comparador como segue

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

depois de adicionar elementos a Lista, chame o método de classificação de Coleção passando objetos de lista e comparador como argumentos

ou seja, Collections.sort(carList, new ColorComparator()); imprima usando ListIterator.

a implementação completa da classe é a seguinte:

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
fonte
0

Usando apenas loops 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){

                }

            }
        }
    }

E ordem de classificação na lista

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