Сортировать ArrayList пользовательских объектов по свойству


1145

Я читал о сортировке ArrayLists с помощью Comparator, но во всех примерах, compareToкоторые использовали люди, согласно некоторым исследованиям, это метод для строк.

Я хотел отсортировать ArrayList пользовательских объектов по одному из их свойств: объекту Date ( getStartDay()). Обычно я сравниваю их, item1.getStartDate().before(item2.getStartDate())поэтому мне было интересно, могу ли я написать что-то вроде:

public class CustomComparator {
    public boolean compare(Object object1, Object object2) {
        return object1.getStartDate().before(object2.getStartDate());
    }
}

public class RandomName {
    ...
    Collections.sort(Database.arrayList, new CustomComparator);
    ...
}


2
Ответ @Yishai в этом посте демонстрирует элегантное использование enum для пользовательской сортировки и групповой сортировки (несколько аргументов) с использованием компаратора.
Gunalmel

Ответы:


1539

Поскольку Dateреализует Comparable, у него есть compareToметод, как и Stringделает.

Так что ваш обычай Comparatorможет выглядеть так:

public class CustomComparator implements Comparator<MyObject> {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
}

compare()Метод должен возвращать int, так что вы не могли непосредственно возвращать , booleanкак вы планировали в любом случае.

Ваш код сортировки будет примерно таким, как вы написали:

Collections.sort(Database.arrayList, new CustomComparator());

Немного более короткий способ написать все это, если вам не нужно повторно использовать ваш компаратор, это написать его как встроенный анонимный класс:

Collections.sort(Database.arrayList, new Comparator<MyObject>() {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
});

поскольку

Теперь вы можете написать последний пример в более короткой форме, используя лямбда-выражение для Comparator:

Collections.sort(Database.arrayList, 
                        (o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

И Listесть sort(Comparator)метод, так что вы можете сократить это еще дальше:

Database.arrayList.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

Это настолько распространенная идиома, что есть встроенный метод для генерации Comparatorкласса с Comparableключом:

Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate));

Все это эквивалентные формы.


33
+1 за упоминание о том, что он должен вернуться intи что вам лучше использовать Date#compareTo()для этого. Почему об этом не говорится выше, другой ответ мне не подходит. Эта ссылка также может быть полезна: Учебник по упорядочению объектов на Sun.com .
BalusC

5
Я думаю, что лучший ответ должен также включать правильный способ сделать это в Java 8. Collections.sort (list, Comparator.comparing (MyObject :: getStartDate)); который читает лучше и менее подвержен ошибкам. Это очень легко написать return o1.getStartDate (). CompareTo (o1.getStartDate ());
Куба

2
Класс компаратора должен быть статичным :)
Jarmez De La Rocha

4
@ Куба еще лучше, пользуйся List.sort().
shmosel

3
Это решение не работает на Android API <24. Ребята, вы знаете решение для этого?
Джим Клермонтс

194

Классы, которые имеют естественный порядок сортировки (класс Number, в качестве примера), должны реализовывать интерфейс Comparable, в то время как классы, не имеющие естественного порядка сортировки (класс Chair, в качестве примера), должны быть снабжены Comparator (или анонимным Comparator учебный класс).

Два примера:

public class Number implements Comparable<Number> {
    private int value;

    public Number(int value) { this.value = value; }
    public int compareTo(Number anotherInstance) {
        return this.value - anotherInstance.value;
    }
}

public class Chair {
    private int weight;
    private int height;

    public Chair(int weight, int height) {
        this.weight = weight;
        this.height = height;
    }
    /* Omitting getters and setters */
}
class ChairWeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getWeight() - chair2.getWeight();
    }
}
class ChairHeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getHeight() - chair2.getHeight();
    }
}

Применение:

List<Number> numbers = new ArrayList<Number>();
...
Collections.sort(numbers);

List<Chair> chairs = new ArrayList<Chair>();
// Sort by weight:
Collections.sort(chairs, new ChairWeightComparator());
// Sort by height:
Collections.sort(chairs, new ChairHeightComparator());

// You can also create anonymous comparators;
// Sort by color:
Collections.sort(chairs, new Comparator<Chair>() {
    public int compare(Chair chair1, Chair chair2) {
        ...
    }
});

Я попробовал это - но когда я хочу получить доступ к классу компаратора ChairWeightComparator снаружи в любом другом классе, я не получаю доступ к этому классу (конечно, нет, так как он не общедоступен). Нужно ли создавать новый общедоступный класс ChairWeightComparator в отдельном файле? - Я действительно первый, кто попробовал это через 3 года, или я скучал по чему-то?
user387184

@ user387184 - просто сделайте его общедоступным и поместите его в свой собственный файл (предпочтительно в собственный пакет), и вы сможете использовать его везде в своем проекте. Не нужно создавать дополнительный класс!
Бьорн

Вы имеете в виду создать новый файл, а не класс и вставить в код: "класс ChairWeightComparator реализует Comparator <Chair> {...."?
user387184

@ user387184, точно - но с ключевым словом publicперед class.
Бьорн

157

Для сортировки ArrayListвы можете использовать следующий фрагмент кода:

Collections.sort(studList, new Comparator<Student>(){
    public int compare(Student s1, Student s2) {
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
    }
});

1
Кто-нибудь использует лямбду stackoverflow.com/questions/2784514/…
Сортировщик

Это сортирует, но дает двойное значение для каждого элемента
Сэм

44

Да, ты можешь. Существует два варианта сравнения элементов: интерфейс Comparable и интерфейс Comparator .

Оба этих интерфейса допускают различное поведение. Comparable позволяет вам заставить объект действовать так, как вы только что описали Strings (фактически String реализует Comparable). Второй, Comparator, позволяет вам делать то, что вы просите. Вы бы сделали это так:

Collections.sort(myArrayList, new MyComparator());

Это заставит метод Collections.sort использовать ваш компаратор для механизма сортировки. Если объекты в ArrayList реализуют сравнимо, вы можете вместо этого сделать что-то вроде этого:

Collections.sort(myArrayList);

Класс Collections содержит ряд этих полезных, общих инструментов.


42

JAVA 8 лямбда-выражение

Collections.sort(studList, (Student s1, Student s2) ->{
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
});

ИЛИ

Comparator<Student> c = (s1, s2) -> s1.firstName.compareTo(s2.firstName);
studList.sort(c)

3
ИлиCollections.sort(studList, Comparator.comparing(Student::getFirstName));
Хольгер

5
.. илиstudList.sort(Comparator.comparing(Student::getFirstName));
Алексис С.

Порядок сортировки всегда будет возрастать в вашем случае. В моем примере я тоже позаботился о порядке сортировки. Спасибо господа.
Сортировщик

33

В Java 8 вы можете использовать ссылку на метод для вашего компаратора:

import static java.util.Comparator.comparing;

Collections.sort(list, comparing(MyObject::getStartDate));

@ user387184 к сожалению, android не поддерживает Java 8, хотя возможен обходной путь (я его не тестировал).
assylias

14

Поскольку технологии появляются каждый день, ответ со временем изменится. Я посмотрел на LambdaJ и, кажется, очень интересно.

Вы можете попробовать решить эти задачи с LambdaJ . Вы можете найти это здесь: http://code.google.com/p/lambdaj/

Вот вам пример:

Сортировать Итеративный

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
});

Сортировка с лямбда

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

Конечно, такая красота влияет на производительность (в среднем в 2 раза), но можете ли вы найти более читаемый код?


Это сортирует, но дает двойное значение для каждого элемента, как этого избежать
Сэм,

@ Сэм, это не должно ... это работает, как ожидалось. Если вы не используете новую версию с ошибкой, которую я рекомендую вам опубликовать на форуме. Во всяком случае, этот ответ был пост до java 8, если вы используете его, то он будет намного лучше, чем с помощью лямбдажа
Федерико Пьяцца

Мне пришлось удалить даже элементы в цикле foreach, иначе это дает мне двойное содержание.
Сэм

13
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

public class test {

public static class Person {
    public String name;
    public int id;
    public Date hireDate;

    public Person(String iname, int iid, Date ihireDate) {
        name = iname;
        id = iid;
        hireDate = ihireDate;
    }

    public String toString() {
        return name + " " + id + " " + hireDate.toString();
    }

    // Comparator
    public static class CompId implements Comparator<Person> {
        @Override
        public int compare(Person arg0, Person arg1) {
            return arg0.id - arg1.id;
        }
    }

    public static class CompDate implements Comparator<Person> {
        private int mod = 1;
        public CompDate(boolean desc) {
            if (desc) mod =-1;
        }
        @Override
        public int compare(Person arg0, Person arg1) {
            return mod*arg0.hireDate.compareTo(arg1.hireDate);
        }
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SimpleDateFormat df = new SimpleDateFormat("mm-dd-yyyy");
    ArrayList<Person> people;
    people = new ArrayList<Person>();
    try {
        people.add(new Person("Joe", 92422, df.parse("12-12-2010")));
        people.add(new Person("Joef", 24122, df.parse("1-12-2010")));
        people.add(new Person("Joee", 24922, df.parse("12-2-2010")));
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Collections.sort(people, new Person.CompId());
    System.out.println("BY ID");
    for (Person p : people) {
        System.out.println(p.toString());
    }

    Collections.sort(people, new Person.CompDate(false));
    System.out.println("BY Date asc");
    for (Person p : people) {
        System.out.println(p.toString());
    }
    Collections.sort(people, new Person.CompDate(true));
    System.out.println("BY Date desc");
    for (Person p : people) {
        System.out.println(p.toString());
    }

}

}

7
Добро пожаловать в stackoverflow. На этот вопрос был дан ответ некоторое время назад. Прежде чем воскрешать старые темы, убедитесь, что ваш ответ добавляет что-то существенное в поток.
Ли

1
Пожалуйста, добавьте объяснение к вашему ответу.
Arashsoft

Просто скомпилируйте и запустите. Код является комментарием и объяснением.
CharlesW

9

Лучший простой способ с JAVA 8 для английского алфавита

Реализация класса

public class NewspaperClass implements Comparable<NewspaperClass>{
   public String name;

   @Override
   public int compareTo(NewspaperClass another) {
      return name.compareTo(another.name);
   }
}

Сортировать

  Collections.sort(Your List);

Если вы хотите отсортировать по алфавиту, который содержит не английские символы, вы можете использовать Locale ... Ниже кода используйте турецкую сортировку символов ...

Реализация класса

public class NewspaperClass implements Comparator<NewspaperClass> {
   public String name;
   public Boolean isUserNewspaper=false;
   private Collator trCollator = Collator.getInstance(new Locale("tr_TR"));



   @Override
   public int compare(NewspaperClass lhs, NewspaperClass rhs) {
      trCollator.setStrength(Collator.PRIMARY);
      return trCollator.compare(lhs.name,rhs.name);
   }
}

Сортировать

Collections.sort(your array list,new NewspaperClass());

9

Справочник по функциям и методам

Collections.sortМетод сортировки с Listпомощью Comparatorвы проходите. Это Comparatorможет быть реализовано с использованием Comparator.comparingметода, где вы можете передать ссылку на метод по мере необходимости Function. К счастью, реальный код намного проще и короче, чем это описание.

Для Java 8:

Collections.sort(list, comparing(ClassName::getName));

или

Collections.sort(list, comparing(ClassName::getName).reversed());

Другой способ

Collections.sort(list, comparing(ClassName::getName, Comparator.nullsLast(Comparator.naturalOrder())));

1
Для вызова требуется уровень API 24
akshay bhange


6

Java 8 Lambda сокращает сортировку.

Collections.sort(stdList, (o1, o2) -> o1.getName().compareTo(o2.getName()));

2
Collections.sort(stdList, Comparator.comparing(SomeClass::getName));
bcsb1001

5

Вы можете использовать Bean Comparator для сортировки по любому свойству в вашем пользовательском классе.


5

Да, это возможно, например, в этом ответе я сортирую по свойству vклассаIndexValue

    // Sorting by property v using a custom comparator.
    Arrays.sort( array, new Comparator<IndexValue>(){
        public int compare( IndexValue a, IndexValue b ){
            return a.v - b.v;
        }
    });

Если вы заметили здесь, я создаю анонимный внутренний класс (который является Java для замыканий) и передаю его непосредственно sortметоду классаArrays

Ваш объект также может реализовывать Comparable(это то, что делает String и большинство основных библиотек в Java), но это будет определять «естественный порядок сортировки» самого класса и не позволит вам подключать новые.


1
... но который вы можете просто переопределить Comparator:)
BalusC

5

Я обнаружил, что большинство, если не все из этих ответов полагаются на базовый класс (Object) для реализации сопоставимого или вспомогательного интерфейса.

Не с моим решением! Следующий код позволяет сравнивать поля объекта, зная их строковое имя. Вы можете легко изменить его, чтобы оно не использовало имя, но тогда вам нужно выставить его или создать один из объектов, с которыми вы хотите сравнить.

Collections.sort(anArrayListOfSomeObjectPerhapsUsersOrSomething, new ReflectiveComparator(). new ListComparator("name"));

public class ReflectiveComparator {
    public class FieldComparator implements Comparator<Object> {
        private String fieldName;

        public FieldComparator(String fieldName){
            this.fieldName = fieldName;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);

                Comparable object1FieldValue = (Comparable) field.get(object1);
                Comparable object2FieldValue = (Comparable) field.get(object2);

                return object1FieldValue.compareTo(object2FieldValue);
            }catch (Exception e){}

            return 0;
        }
    }

    public class ListComparator implements Comparator<Object> {
        private String fieldName;

        public ListComparator(String fieldName) {
            this.fieldName = fieldName;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Comparable o1FieldValue = (Comparable) field.get(object1);
                Comparable o2FieldValue = (Comparable) field.get(object2);

                if (o1FieldValue == null){ return -1;}
                if (o2FieldValue == null){ return 1;}
                return o1FieldValue.compareTo(o2FieldValue);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("Field doesn't exist", e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Field inaccessible", e);
            }
        }
    }
}

5

Вы можете попробовать Guava Ordering :

Function<Item, Date> getStartDate = new Function<Item, Date>() {
    public Date apply(Item item) {
        return item.getStartDate();
    }
};

List<Item> orderedItems = Ordering.natural().onResultOf(getStartDate).
                          sortedCopy(items);

5

Вы можете сортировать, используя Java 8

yourList.sort(Comparator.comparing(Classname::getName));

or

yourList.stream().forEach(a -> a.getBObjects().sort(Comparator.comparing(Classname::getValue)));

3

Этот фрагмент кода может быть полезным. Если вы хотите отсортировать объект в моем случае, я хочу отсортировать по VolumeName:

public List<Volume> getSortedVolumes() throws SystemException {
    List<Volume> volumes = VolumeLocalServiceUtil.getAllVolumes();
    Collections.sort(volumes, new Comparator<Volume>() {
        public int compare(Volume o1, Volume o2) {
            Volume p1 = (Volume) o1;
            Volume p2 = (Volume) o2;
            return p1.getVolumeName().compareToIgnoreCase(
                    p2.getVolumeName());
        }
    });
    return volumes;
}

Это работает. Я использую это в моем JSP.


3

С помощью этой библиотеки здесь вы можете сортировать список пользовательских объектов на несколько столбцов. Библиотека использует функции версии 8.0. Образец также доступен там. Вот образец, чтобы сделать

SortKeys sortKeys = new SortKeys();
sortKeys.addField("firstName")
            .addField("age", true); // This (true) will sort the age descending

// Other ways to specify a property to the sorter are
//      .addField("lastName", String.class);
//      .addField("dob", Date.class, true);

// Instantiate a ListSorter
ListSorter listSorter = new ListSorter();

// Pass the data to sort (listToSort) and the "by keys" to sort (sortKeys)
List sortedList = (List<Person>) listSorter.sortList(listToSort, sortKeys);

3

Вы можете ознакомиться с этой презентацией на форуме Java в Штутгарте, Германия, в 2016 году.

Лишь несколько слайдов используют немецкий язык, 99% контента - это исходный код Java на английском языке; подобно

someCollection.sort(
  OurCustomComparator
    .comparing(Person::getName)
    .thenComparing(Person::getId)
);

где OurCustomComparatorиспользуются методы по умолчанию (и другие интересные идеи). Как показано, приведение к очень лаконичному коду для выбора метода получения для сортировки; и супер простое сцепление (или изменение) критериев сортировки.

Если вы в java8, вы найдете много материала, чтобы начать.


3

Новое с версии 1.8 - это метод List.sort () вместо использования Collection.sort (), поэтому вы напрямую вызываете mylistcontainer.sort ()

Вот фрагмент кода, который демонстрирует функцию List.sort ():

List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit("Kiwi","green",40));
fruits.add(new Fruit("Banana","yellow",100));
fruits.add(new Fruit("Apple","mixed green,red",120));
fruits.add(new Fruit("Cherry","red",10));

// a) using an existing compareto() method
fruits.sort((Fruit f1,Fruit f2) -> f1.getFruitName().compareTo(f2.getFruitName()));
System.out.println("Using String.compareTo(): " + fruits);
//Using String.compareTo(): [Apple is: mixed green,red, Banana is: yellow, Cherry is: red, Kiwi is: green]

// b) Using a comparable class
fruits.sort((Fruit f1,Fruit f2) -> f1.compareTo(f2));  
System.out.println("Using a Comparable Fruit class (sort by color): " + fruits);
// Using a Comparable Fruit class (sort by color): [Kiwi is green, Apple is: mixed green,red, Cherry is: red, Banana is: yellow]

Класс Fruit это:

public class Fruit implements Comparable<Fruit>
{
    private String name;
    private String color;
    private int quantity;

    public Fruit(String name,String color,int quantity)
    { this.name = name; this.color = color; this.quantity = quantity; }

    public String getFruitName() { return name; }        
    public String getColor() { return color; }  
    public int getQuantity() { return quantity; }

    @Override public final int compareTo(Fruit f) // sorting the color
    {
        return this.color.compareTo(f.color);
    }     
    @Override public String toString()
    {   
        return (name + " is: " + color);
    }
} // end of Fruit class   

2

Ваш класс customComparator должен реализовывать java.util.Comparator для использования. он также должен переоценивать сравнить () И равно ()

Функция compare () должна ответить на вопрос: объект 1 меньше, равен или больше объекта 2?

полные документы: http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html


1

Я предпочитаю этот процесс:

public class SortUtil
{    
    public static <T> List<T> sort(List<T> list, String sortByProperty)
    {
            Collections.sort(list, new BeanComparator(sortByProperty));
            return list;
    }
}

List<T> sortedList = SortUtil<T>.sort(unsortedList, "startDate");

Если в вашем списке объектов есть свойство с именем startDate, вы вызываете его снова и снова. Вы даже можете приковать их startDate.time.

Это требует вашего объекта быть , Comparableкоторый означает , что вам нужна compareTo, equalsи hashCodeреализация.

Да, это может быть быстрее ... Но теперь вам не нужно создавать новый компаратор для каждого типа сортировки. Если вы можете сэкономить на времени разработки и отказаться от времени выполнения, вы можете пойти с этим.


3
1, этот ответ был дан 2 часа назад с предоставленным рабочим кодом. Нет необходимости перепечатывать одно и то же решение и загромождать форум, тем более что BeanComparator не является стандартным классом, поэтому это не совсем решение, если автор не знает, о чем вы говорите. Если вам нравится оригинальное предложение, вы можете проголосовать за него и добавить комментарий, если хотите.
camickr

0

Использование Java 8 позволяет определить Comparatorв одной строке, используяComparator.comparing()

Используйте любой из следующих способов:

Опция 1:

listToBeSorted.sort(Comparator.comparing(CustomObject::getStartDate));

Вариант 2:

Collections.sort(listToBeSorted, Comparator.comparing(CustomObject::getStartDate));

1
Кажется, дубликат этого ответа от 3 лет назад.
Базилик Бурк

1
Не совсем, только вариант 2 здесь аналогичен варианту 1 в упомянутом ответе. Я чувствую, что два разных ответа могут иметь хотя бы некоторое сходство, если они отвечают на один и тот же вопрос.
Сахил Чхабра

0

Ваш пользовательский класс может реализовывать интерфейс «Comparable», который требует реализации метода CompareTo. В методе CompareTo вы можете определить, что означает, что объект меньше или больше другого объекта. Так что в вашем примере это может выглядеть примерно так:

public class MyCustomClass implements Comparable<MyCustomClass>{

..........

 @Override
public int compareTo(MyCustomClass a) {
    if(this.getStartDate().before(a.getStartDate())){
        return -1;
    }else if(a.getStartDate().before(this.getStartDate())){
        return 1;
    }else {
        return 0;
    }
}

Отрицательное число указывает, что это меньше, чем сравниваемый объект. Положительное число указывает, что это больше, чем по сравнению с объектом, а ноль означает, что объекты равны.

Затем вы можете использовать collection.sort (myList), чтобы отсортировать список без подачи в компаратор. Этот метод также имеет преимущество в том, что вещи сортируются автоматически, если вы используете структуры данных отсортированной коллекции, такие как TreeSet или TreeMap.

Вы можете проверить эту статью, если хотите больше узнать об интерфейсе Comparable (раскрытие: я автор;)) https://nullbeans.com/the-java-comparable-interface-automatic-sort-of-collections/


0

Вы также можете использовать Springs PropertyComparator, если у вас есть только путь свойства String к (вложенному) свойству, которое вы хотите отсортировать:

List<SomeObject> list = ...;
PropertyComparator<HitWithInfo> propertyComparator = new PropertyComparator<>(
    "property.nested.myProperty", false, true);
list.sort(propertyComparator);

Недостатком является то, что этот компаратор молча игнорирует свойства, которые не существуют или недоступны, и обрабатывает это как нулевое значение для сравнения. Это означает, что вы должны тщательно протестировать такой компаратор или как-то проверить существование пути свойства.


0

используя API потока java-8, вы можете отсортировать ArrayListпо:

 Comparator<Person> birthdayComparator = Comparator.comparing(Person::getBirthday);
 List<Person> sortedList = list.stream().sorted(birthdayComparator).collect(toList());

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.