Сортування ArrayList користувальницьких об'єктів за властивістю


1145

Я читав про сортування ArrayLists за допомогою компаратора, але у всіх прикладах люди, compareToякі, згідно з деякими дослідженнями, є методом для Strings.

Я хотів сортувати 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

Відповіді:


1537

Оскільки 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 (список, Comparator.comparing (MyObject :: getStartDate)); який читає краще і менш схильний до помилок. Дуже легко написати return o1.getStartDate (). Порівняти до (o1.getStartDate ());
Куба

2
Клас порівняння повинен бути статичним :)
Jarmez De La Rocha

4
@Kuba ще краще, користуйся List.sort().
shmosel

3
Це рішення не працює в Android API <24. Ви знаєте, рішення для цього?
Джим Клермонтс

194

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

Два приклади:

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 роки, або я пропустив що-небудь?
користувач387184

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

ви маєте на увазі створити новий файл - не клас та ввести код: "class ChairWeightComparator реалізує компаратор <Chair> {...."?
користувач387184

@ 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
Усі, хто використовує lambda stackoverflow.com/questions/2784514/…
сортування,

Це сортує, але дає подвійне значення для кожного елемента
Сем,

44

Так, ти можеш. Є два варіанти порівняння елементів, порівняльний інтерфейс та інтерфейс компаратора .

Обидва ці інтерфейси дозволяють мати різну поведінку. Порівняння дозволяє змусити об'єкт діяти так, як ви тільки що описали Strings (насправді String реалізує Порівняно). Другий, компаратор, дозволяє робити те, що ви просите зробити. Ви зробите це так:

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

Це призведе до того, що метод Collections.sort використає ваш компаратор для механізму сортування. Якщо об'єкти в ArrayList реалізують порівнянні, ви можете зробити щось подібне:

Collections.sort(myArrayList);

Клас Колекції містить ряд цих корисних, загальних інструментів.


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, хоча може бути вирішення проблеми (я цього не перевіряв).
Ассілія

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 рази), але чи можете ви знайти більш читабельний код?


Це сортує, але дає подвійне значення для кожного елемента, як цього уникнути
Сем,

@Sam, не повинно ... це працює, як очікувалося. Якщо ви не використовуєте нову версію з помилкою, яку я рекомендую опублікувати на форумі. Так чи інакше, ця відповідь була розміщена до java 8, якщо ви її використовуєте, то це буде набагато краще, ніж використання лямбдай
Federico Piazza

Мені довелося вилучати навіть елементи з циклу 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

Ви можете сортувати за допомогою 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]

Клас фруктів:

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, щоб використовувати його. він також повинен перевищувати порівняння () І дорівнює ()

порівняння () повинно відповісти на питання: Чи об'єкт 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
Здається, це дублікат цієї відповіді за три роки до цього.
Василь Бурк

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

0

Ваш власний клас може реалізувати інтерфейс "Порівняний", який вимагає впровадження методу Порівняння. У методі 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.

Ви можете перевірити цю статтю, якщо хочете почитати більше про Порівняльний інтерфейс (розкриття: я автор;)) 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.