Jak używać narzędzia Komparator do definiowania niestandardowej kolejności sortowania?

91

Chcę opracować demo sortowania listy samochodów. Używam tabeli danych do wyświetlenia listy samochodów. Teraz chcę posortować listę według koloru samochodu. Tutaj nie jest sortowane według kolejności alfabetycznej. Chcę użyć mojej niestandardowej kolejności sortowania, na przykład czerwony samochód, potem niebieski itp.

Do tego staram się używać Java Comparatori Comparableale pozwala uporządkować w kolejności alfabetycznej tylko.

Czy zatem ktoś może wskazać mi drogę do wdrożenia techniki, której należy użyć, aby sortowanie było szybsze.

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
źródło

Odpowiedzi:

101

Zalecam utworzenie wyliczenia dla kolorów samochodu zamiast używania ciągów znaków, a naturalną kolejnością wyliczenia będzie kolejność deklarowania stałych.

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

i

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

Zmieniasz ciąg na PaintColor, a następnie w głównej liście samochodów staje się:

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

...

Collections.sort(carList, new ColorComparator());
z7sg Ѫ
źródło
jak miałbym uruchomić ten przykład. nie ma dostępu do metodyaintColors w ColorComparator. czy mógłbyś zilustrować, jak wyglądałaby główna metoda.
Deepak,
jaki jest wynik? czy zawsze najpierw SREBRNE?
Deepak,
@Deepak: tak, naturalnym porządkiem enumwartości jest kolejność, w jakiej są zdefiniowane.
Joachim Sauer
1
@SeanPatrickFloyd Nie sądzę, aby poleganie na naturalnej kolejności wyliczeń było najlepszą praktyką.
mike
1
@mike Nie zgadzam się, ale istnieją ważne argumenty przemawiające za obydwoma punktami widzenia
Sean Patrick Floyd
57

Co powiesz na to:

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

Zasadniczo zgadzam się, że użycie metody enumjest jeszcze lepszym podejściem, ale ta wersja jest bardziej elastyczna, ponieważ umożliwia definiowanie różnych kolejności sortowania.

Aktualizacja

Guava ma tę funkcjonalność upieczoną w swojej Orderingklasie:

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

Ta wersja jest nieco mniej szczegółowa.


Zaktualizuj ponownie

Java 8 sprawia, że ​​komparator jest jeszcze mniej rozwlekły:

Comparator<Car> carComparator = Comparator.comparing(
        c -> definedOrder.indexOf(c.getColor()));
Sean Patrick Floyd
źródło
Czy mógłbyś również spojrzeć na to pytanie. Dzięki Ci. stackoverflow.com/questions/61934313/…
user2048204
25

Komparator w linii ...

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
źródło
11

Myślę, że można to zrobić w następujący sposób:

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

Do sortowania użyj tego:

Collections.sort(carList, new ColorComparator(sortOrder));
ilalex
źródło
7

Musiałem zrobić coś podobnego do odpowiedzi Seana i ilalexa.
Ale miałem zbyt wiele opcji, aby jawnie zdefiniować porządek sortowania dla i potrzebowałem tylko przesunąć niektóre wpisy na początek listy ... w określonej (nienaturalnej) kolejności.
Mam nadzieję, że jest to pomocne dla kogoś innego.

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
}
matowy1616
źródło
4

Zrobię coś takiego:

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

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

Wszystkie kredyty należą do @Sean Patrick Floyd :)

Mariusz S.
źródło
3

W Javie 8 możesz zrobić coś takiego:

Najpierw potrzebujesz Enum:

public enum Color {
    BLUE, YELLOW, RED
}

Klasa samochodu:

public class Car {

    Color color;

    ....

    public Color getColor() {
        return color;
    }

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

Następnie, korzystając z listy samochodów, możesz po prostu:

Collections.sort(carList, Comparator:comparing(CarSort::getColor));
Thermech
źródło
To nie jest sortowanie niestandardowe.
zygimantus
Poza tym to nie jest "funkcjonalny" sposób na zrobienie tego ... ma to skutki uboczne !!
TriCore
2

Zdefiniuj jeden typ wyliczenia jako

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

Zmień typ danych colorz Stringna Colors Zmień typ zwracanego i typ argumentu metody pobierającej i ustawiającej kolor naColors

Zdefiniuj typ komparatora w następujący sposób

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

po dodaniu elementów do listy, wywołaj metodę sortowania Collection, przekazując jako argumenty obiekty listy i komparatora

tzn. Collections.sort(carList, new ColorComparator()); drukuj za pomocą ListIterator.

pełna implementacja klasy wygląda następująco:

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
źródło
0

Używając tylko prostych pętli:

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

                }

            }
        }
    }

I posortuj kolejność na liście

public static List<String> ALARMS_LIST = Arrays.asList(
            "CRITICAL",
            "MAJOR",
            "MINOR",
            "WARNING",
            "GOOD",
            "N/A");
głupiec
źródło