Jak posortować listę według różnych parametrów w różnym czasie

Mam klasę o nazwie Person z wieloma właściwościami, na przykład:

public class Person {
    private int id;
    private String name, address;
    // Many more properties.
}

Wiele Person-obiektów jest przechowywanych w ArrayList<Person>. Chcę posortować tę listę według wielu parametrów sortowania i różnych od czasu do czasu. Na przykład może raz chcę sortować według name rosnąco, a następnie address malejąco, a innym razem po prostu przez id malejąco.

I nie chcę tworzyć własnych metod sortowania (tzn. chcę używać Collections.sort(personList, someComparator). Jakie jest najbardziej eleganckie rozwiązanie, które osiąga to?

Author: Cœur, 2009-09-14

9 answers

Myślę, że Twoje podejście enum jest w zasadzie dźwiękowe, ale instrukcje switch naprawdę potrzebują bardziej obiektowego podejścia. Consider:

enum PersonComparator implements Comparator<Person> {
    ID_SORT {
        public int compare(Person o1, Person o2) {
            return Integer.valueOf(o1.getId()).compareTo(o2.getId());
        }},
    NAME_SORT {
        public int compare(Person o1, Person o2) {
            return o1.getFullName().compareTo(o2.getFullName());
        }};

    public static Comparator<Person> decending(final Comparator<Person> other) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                return -1 * other.compare(o1, o2);
            }
        };
    }

    public static Comparator<Person> getComparator(final PersonComparator... multipleOptions) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                for (PersonComparator option : multipleOptions) {
                    int result = option.compare(o1, o2);
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            }
        };
    }
}

Przykład użycia (ze statycznym importem).

public static void main(String[] args) {
    List<Person> list = null;
    Collections.sort(list, decending(getComparator(NAME_SORT, ID_SORT)));
}
 192
Author: Yishai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-14 13:26:34

Możesz utworzyć komparatory dla każdej właściwości, którą chcesz posortować, a następnie spróbować "łączenie w komparatory": -) w ten sposób:

public class ChainedComparator<T> implements Comparator<T> {
    private List<Comparator<T>> simpleComparators; 
    public ChainedComparator(Comparator<T>... simpleComparators) {
        this.simpleComparators = Arrays.asList(simpleComparators);
    }
    public int compare(T o1, T o2) {
        for (Comparator<T> comparator : simpleComparators) {
            int result = comparator.compare(o1, o2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}
 25
Author: Tadeusz Kopec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-28 07:49:46

Jednym ze sposobów jest utworzenie Comparator, która przyjmuje jako argumenty listę właściwości do sortowania według, Jak pokazuje ten przykład.

public class Person {
    private int id;
    private String name, address;

    public static Comparator<Person> getComparator(SortParameter... sortParameters) {
        return new PersonComparator(sortParameters);
    }

    public enum SortParameter {
        ID_ASCENDING, ID_DESCENDING, NAME_ASCENDING,
        NAME_DESCENDING, ADDRESS_ASCENDING, ADDRESS_DESCENDING
    }

    private static class PersonComparator implements Comparator<Person> {
        private SortParameter[] parameters;

        private PersonComparator(SortParameter[] parameters) {
            this.parameters = parameters;
        }

        public int compare(Person o1, Person o2) {
            int comparison;
            for (SortParameter parameter : parameters) {
                switch (parameter) {
                    case ID_ASCENDING:
                        comparison = o1.id - o2.id;
                        if (comparison != 0) return comparison;
                        break;
                    case ID_DESCENDING:
                        comparison = o2.id - o1.id;
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_ASCENDING:
                        comparison = o1.name.compareTo(o2.name);
                        if (comparison != 0) return comparison;
                        break;
                    case NAME_DESCENDING:
                        comparison = o2.name.compareTo(o1.name);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_ASCENDING:
                        comparison = o1.address.compareTo(o2.address);
                        if (comparison != 0) return comparison;
                        break;
                    case ADDRESS_DESCENDING:
                        comparison = o2.address.compareTo(o1.address);
                        if (comparison != 0) return comparison;
                        break;
                }
            }
            return 0;
        }
    }
}

Może być następnie użyty w kodzie na przykład w następujący sposób:

cp = Person.getComparator(Person.SortParameter.ADDRESS_ASCENDING,
                          Person.SortParameter.NAME_DESCENDING);
Collections.sort(personList, cp);
 16
Author: runaros,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-14 12:43:29

Jednym z podejść byłoby skomponowanie Comparator s. może to być metoda biblioteczna(jestem pewien, że gdzieś tam istnieje).

public static <T> Comparator<T> compose(
    final Comparator<? super T> primary,
    final Comparator<? super T> secondary
) {
    return new Comparator<T>() {
        public int compare(T a, T b) {
            int result = primary.compare(a, b);
            return result==0 ? secondary.compare(a, b) : result;
        }
        [...]
    };
}

Użycie:

Collections.sort(people, compose(nameComparator, addressComparator));

Alternatywnie, zauważ, że Collections.sort jest rodzajem stabilnym. Jeśli wydajność nie jest absolutnie kluczowa, sortujesz jako kolejność Drugorzędna przed podstawową.

Collections.sort(people, addressComparator);
Collections.sort(people, nameComparator);
 8
Author: Tom Hawtin - tackline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-07 13:00:33

Komparatory pozwalają ci to zrobić bardzo łatwo i naturalnie. Możesz tworzyć pojedyncze instancje komparatorów, zarówno w samej klasie Person, jak i w klasie Service odpowiadającej twoim potrzebom.
Przykłady, używając anonimowych klas wewnętrznych:

    public static final Comparator<Person> NAME_ASC_ADRESS_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         int nameOrder = p1.getName().compareTo(p2.getName);
         if(nameOrder != 0) {
           return nameOrder;
         }
         return -1 * p1.getAdress().comparedTo(p2.getAdress());
         // I use explicit -1 to be clear that the order is reversed
      }
    };

    public static final Comparator<Person> ID_DESC
     = new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
         return -1 * p1.getId().comparedTo(p2.getId());
         // I use explicit -1 to be clear that the order is reversed
      }
    };
    // and other comparator instances as needed... 

Jeśli masz ich wiele, Możesz również uporządkować swój kod porównawczy W DOWOLNY SPOSÓB. Na przykład możesz:

  • ]}
  • mają CompositeComparator, który łączy niektóre istniejące komparatory
  • mieć NullComparator, który obsługuje przypadki null, a następnie deleguje do innego komparatora
  • itd...
 4
Author: KLE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-14 12:56:31

Myślę, że łączenie sortowników z klasą osoby, tak jak w twojej odpowiedzi, nie jest dobrym pomysłem, ponieważ łączy porównanie (Zwykle biznesowe) i obiekt modelu, aby zbliżyć się do siebie. Za każdym razem, gdy chcesz zmienić / dodać coś sorter, musisz dotknąć klasy person, co zwykle jest czymś, czego nie chcesz robić.

Korzystanie z usługi lub czegoś podobnego, która zapewnia instancje porównawcze, takie jak KLE, brzmi o wiele bardziej elastycznie i rozszerzalnie.

 2
Author: gia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-14 13:09:38

Moje podejście opiera się na Yishai ' s. główną luką jest to, że nie ma sposobu, aby sortować najpierw rosnąco dla atrybutu, a następnie decending dla innego. Nie można tego zrobić za pomocą wyliczeń. W tym celu korzystałem z zajęć. Ponieważ Sortownik silnie zależy od typu, który preferowałem, aby zaimplementować go jako wewnętrzną klasę osoby.

Klasa "osoba" z klasą wewnętrzną "SortOrder":

import java.util.Comparator;

public class Person {
    private int id;
    private String firstName; 
    private String secondName;

    public Person(int id, String firstName, String secondName) {
        this.id = id;
        this.firstName = firstName;
        this.secondName = secondName;   
    }

    public abstract static class SortOrder implements Comparator<Person> {
        public static SortOrder PERSON_ID = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return Integer.valueOf(p1.getId()).compareTo(p2.getId());
            }
        };
        public static SortOrder PERSON_FIRST_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getFirstName().compareTo(p2.getFirstName());
            }
        };
        public static SortOrder PERSON_SECOND_NAME = new SortOrder() {
            public int compare(Person p1, Person p2) {
                return p1.getSecondName().compareTo(p2.getSecondName());
            }
        };

        public static SortOrder invertOrder(final SortOrder toInvert) {
            return new SortOrder() {
                public int compare(Person p1, Person p2) {
                    return -1 * toInvert.compare(p1, p2);
                }
            };
        }

        public static Comparator<Person> combineSortOrders(final SortOrder... multipleSortOrders) {
            return new Comparator<Person>() {
                public int compare(Person p1, Person p2) {
                    for (SortOrder personComparator: multipleSortOrders) {
                        int result = personComparator.compare(p1, p2);
                        if (result != 0) {
                            return result;
                        }
                    }
                    return 0;
                }
            };
        }
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSecondName() {
        return secondName;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();

        result.append("Person with id: ");
        result.append(id);
        result.append(" and firstName: ");
        result.append(firstName);
        result.append(" and secondName: ");
        result.append(secondName);
        result.append(".");

        return result.toString();
    }
}

Przykład użycia osoby klasowej i jej Sortowanie:

import static multiplesortorder.Person.SortOrder.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import multiplesortorder.Person;

public class Application {

    public static void main(String[] args) {
        List<Person> listPersons = new ArrayList<Person>(Arrays.asList(
                 new Person(0, "...", "..."),
                 new Person(1, "...", "...")
             ));

         Collections.sort(listPersons, combineSortOrders(PERSON_FIRST_NAME, invertOrder(PERSON_ID)));

         for (Person p: listPersons) {
             System.out.println(p.toString());
         }
    }
}

ORUMOo

 2
Author: oRUMOo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-10 12:10:24

Ostatnio napisałem komparator do sortowania wielu pól w oddzielonym rekordzie łańcuchowym. Pozwala na zdefiniowanie ogranicznika, struktury rekordów i reguł sortowania(niektóre z nich są specyficzne dla typu). Można tego użyć, konwertując rekord osoby na oddzielony ciąg znaków.

Wymagane informacje są przesyłane do samego komparatora, programowo lub za pośrednictwem pliku XML.

XML jest sprawdzany przez plik XSD osadzony w pakiecie. Na przykład poniżej znajduje się rekord rozdzielany tabulatorami układ z czterema polami (z których dwa są sortowalne):

<?xml version="1.0" encoding="ISO-8859-1"?> 
<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <delimiter>&#009;</delimiter>

    <column xsi:type="Decimal">
        <name>Column One</name>
    </column>

    <column xsi:type="Integer">
        <name>Column Two</name>
    </column>

    <column xsi:type="String">
        <name>Column Three</name>
        <sortOrder>2</sortOrder>
        <trim>true</trim>
        <caseSensitive>false</caseSensitive>        
        <stripAccents>true</stripAccents>
    </column>

    <column xsi:type="DateTime">
        <name>Column Four</name>
        <sortOrder>1</sortOrder>
        <ascending>true</ascending>
        <nullLowSortOrder>true</nullLowSortOrder>
        <trim>true</trim>
        <pattern>yyyy-MM-dd</pattern>
    </column>

</row>

Wtedy użyłbyś tego w Javie w następujący sposób:

Comparator<String> comparator = new RowComparator(
              new XMLStructureReader(new File("layout.xml")));

Biblioteka znajduje się tutaj:

Http://sourceforge.net/projects/multicolumnrowcomparator/

 0
Author: Constantin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-16 03:17:33

Załóżmy, że istnieje klasa Coordinate i trzeba ją sortować w obie strony według współrzędnych X i Y. Potrzebne są do tego dwa różne komparatory. Poniżej znajduje się próbka

class Coordinate
{

    int x,y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    static Comparator<Coordinate> getCoordinateXComparator() {
        return new Comparator<Coordinate>() {

            @Override
            public int compare(Coordinate Coordinate1, Coordinate Coordinate2) {
                if(Coordinate1.x < Coordinate2.x)
                    return 1;
                else
                    return 0;
            }
            // compare using Coordinate x
        };
    }

    static Comparator<Coordinate> getCoordinateYComparator() {
        return new Comparator<Coordinate>() {

            @Override
            public int compare(Coordinate Coordinate1, Coordinate Coordinate2) {
                if(Coordinate1.y < Coordinate2.y)
                    return 1;
                else
                    return 0;
            }
            // compare using Coordinate y
        };
    }
}
 0
Author: ravi ranjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 08:01:19