Wie definiere ich mit Comparator eine benutzerdefinierte Sortierreihenfolge?

88

Ich möchte eine Sortierdemo für die Fahrzeugliste entwickeln. Ich verwende eine Datentabelle, um die Fahrzeugliste anzuzeigen. Eigentlich möchte ich die Liste nach Autofarbe sortieren. Hier ist es nicht nach alphabetischer Reihenfolge sortiert. Ich möchte meine benutzerdefinierte Sortierreihenfolge verwenden, z. B. rotes Auto zuerst, dann blaues usw.

Dafür versuche ich Java zu verwenden Comparatorund Comparablees erlaubt aber nur in alphabetischer Reihenfolge zu sortieren.

Kann mir jemand den Weg weisen, die zu verwendende Technik zu implementieren, damit die Sortierung schneller wird?

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
quelle

Antworten:

100

Ich empfehle Ihnen, eine Aufzählung für Ihre Fahrzeugfarben zu erstellen, anstatt Strings zu verwenden. Die natürliche Reihenfolge der Aufzählung ist die Reihenfolge, in der Sie die Konstanten deklarieren.

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

und

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

Sie ändern den String in PaintColor und dann wird Ihre Fahrzeugliste hauptsächlich zu:

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

...

Collections.sort(carList, new ColorComparator());
z7sg Ѫ
quelle
Wie würde ich dieses Beispiel ausführen? Auf ColorColors wird in ColorComparator nicht zugegriffen. Können Sie veranschaulichen, wie die Hauptmethode aussehen würde?
Deepak
Was ist die Ausgabe? ist es immer zuerst SILBER?
Deepak
@Deepak: Ja, die natürliche Reihenfolge der enumWerte ist die Reihenfolge, in der ihre Werte definiert werden.
Joachim Sauer
1
@ SeanPatrickFloyd Ich denke nicht, dass es eine bewährte Methode ist, sich auf die natürliche Reihenfolge von Aufzählungen zu verlassen.
Mike
1
@mike Ich bin anderer Meinung, aber es gibt gültige Argumente für beide Sichtweisen
Sean Patrick Floyd
56

Wie wäre es damit:

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

Grundsätzlich stimme ich zu, dass die Verwendung von ein enumnoch besserer Ansatz ist, aber diese Version ist flexibler, da Sie damit verschiedene Sortierreihenfolgen definieren können.

Aktualisieren

Guava hat diese Funktionalität in seine OrderingKlasse integriert:

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

Diese Version ist etwas weniger ausführlich.


Erneut aktualisieren

Java 8 macht den Komparator noch weniger ausführlich:

Comparator<Car> carComparator = Comparator.comparing(
        c -> definedOrder.indexOf(c.getColor()));
Sean Patrick Floyd
quelle
Kannst du bitte auch diese Frage untersuchen? Danke. stackoverflow.com/questions/61934313/…
user2048204
25

Komparator in Reihe ...

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
quelle
10

Ich denke, das kann wie folgt gemacht werden:

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

Verwenden Sie zum Sortieren Folgendes:

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

Ich musste etwas Ähnliches tun wie Sean und Ilalex.
Ich hatte jedoch zu viele Optionen, um die Sortierreihenfolge explizit zu definieren, und musste nur bestimmte Einträge in der angegebenen (nicht natürlichen) Reihenfolge an den Anfang der Liste setzen.
Hoffentlich ist dies für jemand anderen hilfreich.

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
quelle
2

In Java 8 können Sie Folgendes tun:

Sie benötigen zuerst eine Aufzählung:

public enum Color {
    BLUE, YELLOW, RED
}

Fahrzeugklasse:

public class Car {

    Color color;

    ....

    public Color getColor() {
        return color;
    }

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

Und dann können Sie mithilfe Ihrer Fahrzeugliste einfach Folgendes tun:

Collections.sort(carList, Comparator:comparing(CarSort::getColor));
Thermech
quelle
Dies ist keine benutzerdefinierte Sortierung.
Zygimantus
Außerdem ist es keine "funktionale" Art, dies zu tun ... dies hat Nebenwirkungen !!
TriCore
2

Definieren Sie einen Aufzählungstyp als

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

Ändern Sie den Datentyp von colorvon Stringin Colors Ändern Sie den Rückgabetyp und den Argumenttyp des Getter und die Setter-Farbmethode inColors

Definieren Sie den Komparatortyp wie folgt

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

Rufen Sie nach dem Hinzufügen von Elementen zur Liste die Sortiermethode der Sammlung auf, indem Sie Listen- und Vergleichsobjekte als Argumente übergeben

dh Collections.sort(carList, new ColorComparator()); dann drucken mit ListIterator.

Die Implementierung der vollständigen Klasse ist wie folgt:

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
quelle
2

Ich werde so etwas tun:

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

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

Alle Credits gehen an @Sean Patrick Floyd :)

MariuszS
quelle
0

Mit einfachen Schleifen:

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

                }

            }
        }
    }

Und Sortierreihenfolge in Liste

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