Comment utiliser Comparator pour définir un ordre de tri personnalisé?

90

Je souhaite développer une démo de tri pour la liste des voitures. J'utilise le tableau de données pour afficher la liste des voitures. Maintenant, je veux trier la liste par couleur de voiture. Ici, il n'est pas trié par ordre alphabétique. Je veux utiliser mon ordre de tri personnalisé comme la voiture rouge vient en premier, puis la bleue, etc.

Pour cela j'essaye d'utiliser Java Comparatoret Comparablemais cela permet de trier uniquement par ordre alphabétique.

Alors, quelqu'un peut-il me guider sur la manière de mettre en œuvre la technique à utiliser pour que le tri devienne plus rapide.

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
la source

Réponses:

100

Je vous recommande de créer une énumération pour les couleurs de votre voiture au lieu d'utiliser des chaînes et l'ordre naturel de l'énumération sera l'ordre dans lequel vous déclarez les constantes.

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

et

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

Vous changez la chaîne en PaintColor, puis en général votre liste de voitures devient:

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

...

Collections.sort(carList, new ColorComparator());
z7sg Ѫ
la source
comment pourrais-je exécuter cet exemple.PaintColors n'est pas accessible dans ColorComparator.Pourriez-vous illustrer à quoi ressemblerait la méthode principale.
Deepak
quelle est la sortie? est-ce toujours SILVER d'abord?
Deepak
@Deepak: oui, l'ordre naturel des enumvaleurs est l'ordre dans lequel ses valeurs sont définies.
Joachim Sauer
1
@SeanPatrickFloyd Je ne pense pas que se fier à l'ordre naturel des énumérations soit une meilleure pratique.
mike
1
@mike Je ne suis pas d'accord, mais il y a des arguments valables pour les deux points de vue
Sean Patrick Floyd
56

Que dis-tu de ça:

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 principe, je conviens que l'utilisation d'un enum est une approche encore meilleure, mais cette version est plus flexible car elle vous permet de définir différents ordres de tri.

Mettre à jour

Guava a cette fonctionnalité intégrée dans sa 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());
    }
}; 

Cette version est un peu moins verbeuse.


Mettre à jour à nouveau

Java 8 rend le comparateur encore moins verbeux:

Comparator<Car> carComparator = Comparator.comparing(
        c -> definedOrder.indexOf(c.getColor()));
Sean Patrick Floyd
la source
Pouvez-vous s'il vous plaît examiner cette question également. Merci. stackoverflow.com/questions/61934313/…
user2048204
25

Comparateur en ligne ...

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
la source
11

Je pense que cela peut être fait comme suit:

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

Pour le tri, utilisez ceci:

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

J'ai dû faire quelque chose de similaire à la réponse de Sean et ilalex.
Mais j'avais trop d'options pour définir explicitement l'ordre de tri et je n'avais besoin que de faire flotter certaines entrées au début de la liste ... dans l'ordre spécifié (non naturel).
J'espère que cela sera utile à quelqu'un d'autre.

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
la source
4

Je vais faire quelque chose comme ça:

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

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

Tous les crédits vont à @Sean Patrick Floyd :)

MariuszS
la source
2

Dans Java 8, vous pouvez faire quelque chose comme ceci:

Vous avez d'abord besoin d'un Enum:

public enum Color {
    BLUE, YELLOW, RED
}

Classe de voiture:

public class Car {

    Color color;

    ....

    public Color getColor() {
        return color;
    }

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

Et puis, en utilisant votre liste de voitures, vous pouvez simplement faire:

Collections.sort(carList, Comparator:comparing(CarSort::getColor));
Thermech
la source
Ce n'est pas un tri personnalisé.
zygimantus
D'ailleurs, ce n'est pas une manière "fonctionnelle" de le faire ... cela a des effets secondaires !!
TriCore
2

Définissez un type d'énumération comme

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

Modifier le type de données colorde Stringà Colors Modifier le type de retour et le type d'argument de la méthode de lecture et de définition de la couleur enColors

Définissez le type de comparateur comme suit

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

après avoir ajouté des éléments à List, appelez la méthode de tri de Collection en passant des objets liste et comparateur comme arguments

c'est-à-dire, Collections.sort(carList, new ColorComparator()); puis imprimez en utilisantListIterator .

l'implémentation complète de la classe est la suivante:

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
la source
0

En utilisant juste de simples boucles:

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){

                }

            }
        }
    }

Et l'ordre de tri dans la liste

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