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

877

Я читал о сортировке 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);
    ...
}
Теги:
sorting
date
comparator

23 ответа

1190
Лучший ответ

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

Поскольку

Теперь вы можете написать последний пример в более короткой форме, используя lambda expression для 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));

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

  • 27
    +1 за упоминание о том, что он должен возвращать int и что для этого лучше использовать Date#compareTo() . Почему об этом не говорится выше, другой ответ мне не подходит. Эта ссылка также может быть полезна: Учебник по упорядочению объектов на Sun.com .
  • 5
    Я думаю, что лучший ответ должен также включать правильный способ сделать это в Java 8. Collections.sort (list, Comparator.comparing (MyObject :: getStartDate)); который читает лучше и менее подвержен ошибкам. Очень легко написать return o1.getStartDate (). CompareTo (o1.getStartDate ());
Показать ещё 9 комментариев
172

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

Два примера:

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) {
        ...
    }
});
  • 0
    Я попробовал это - но когда я хочу получить доступ к классу компаратора ChairWeightComparator снаружи в любом другом классе, я не получаю доступ к этому классу (конечно, нет, так как он не общедоступен). Нужно ли создавать новый общедоступный класс ChairWeightComparator в отдельном файле? - Я действительно первый, кто попробовал это через 3 года, или я скучал по чему-то?
  • 0
    @ user387184 - просто сделайте его общедоступным и поместите его в свой собственный файл (предпочтительно в собственный пакет), и вы сможете использовать его везде в своем проекте. Не нужно создавать дополнительный класс!
Показать ещё 2 комментария
135

Для сортировки 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/…
  • 0
    Это сортирует, но дает двойное значение для каждого элемента
36

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

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

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

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

Collections.sort(myArrayList);

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

29

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)
  • 2
    Или Collections.sort(studList, Comparator.comparing(Student::getFirstName));
  • 4
    .. или studList.sort(Comparator.comparing(Student::getFirstName));
Показать ещё 1 комментарий
25

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

import static java.util.Comparator.comparing;

Collections.sort(list, comparing(MyObject::getStartDate));
  • 0
    @ user387184 к сожалению, android не поддерживает Java 8, хотя может быть и обходной путь (я его не тестировал).
11
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
    Пожалуйста, добавьте объяснение к вашему ответу.
Показать ещё 1 комментарий
9

Поскольку технологии появляются каждый день, ответ будет меняться во времени. Я взглянул на 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 раза), но вы можете найти более читаемый код?

  • 0
    Это сортирует, но дает двойное значение для каждого элемента, как этого избежать
  • 0
    @ Сэм, это не должно ... это работает, как ожидалось. Если вы не используете новую версию с ошибкой, которую я рекомендую вам опубликовать на форуме. Во всяком случае, этот ответ был пост до Java 8, если вы используете его, то он будет намного лучше, чем с помощью lambdaj
Показать ещё 1 комментарий
5

Самый простой способ с 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());
5

От Java 8 и далее нам не нужно напрямую использовать Collections.sort(). Интерфейс List имеет метод sort() по умолчанию:

List<User> users = Arrays.asList(user1,user2,user3);
users.sort( (u1, u2) -> { 
return u1.getFirstName.compareTo(u2.getFirstName());}); 

См. http://visvv.blogspot.in/2016/01/sorting-objects-in-java-8.html.

5

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

4

Вы можете попробовать 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);
3

Вы можете сортировать с помощью java 8

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

or

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

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

Collections.sort(stdList, (o1, o2) -> o1.getName().compareTo(o2.getName()));
  • 1
    Collections.sort(stdList, Comparator.comparing(SomeClass::getName));
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

Да, это возможно, например, в этом ответе Я сортирую по свойству 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 :)
2

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

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

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

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

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

2

Для Java 8:

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

или

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

Кажется, что работает со строковым типом даты так же, как "2015-12-14T21: 55: 51Z"

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

Collections.sort(list, comparing(ClassName::getName, Comparator.nullsLast(Comparator.naturalOrder())));
  • 0
    Для вызова требуется уровень API 24
2

В этой библиотеке здесь вы можете отсортировать список настраиваемых объектов на нескольких столбцах. Библиотека использует возможности версии 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);
2

Я нашел больше всего, если не все из этих ответов полагаются на базовый класс (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);
            }
        }
    }
}
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.

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

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

ваш класс customComparator должен реализовать java.util.Comparator для использования. он также должен переопределять compare() AND equals()

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

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

0

Новый, так как 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);
}

}//конец класса Fruit

Ещё вопросы

Сообщество Overcoder
Наверх
Меню