Сортировка ArrayList объектов с использованием настраиваемого порядка сортировки

119

Я хочу реализовать функцию сортировки для своего приложения адресной книги.

Я хочу отсортировать файл ArrayList<Contact> contactArray. Contact- это класс, который содержит четыре поля: имя, домашний номер, номер мобильного телефона и адрес. Я хочу разобратьname .

Как мне написать для этого специальную функцию сортировки?

Sameera0
источник

Ответы:

270

Вот руководство по порядку объектов:

Хотя я и приведу несколько примеров, все равно рекомендую прочитать.


Существуют различные способы сортировки файлов ArrayList. Если вы хотите определить естественный (по умолчанию) порядок , вам необходимо разрешить Contactреализацию Comparable. Предполагая, что вы хотите выполнить сортировку по умолчанию name, выполните (для простоты нулевые проверки опущены):

public class Contact implements Comparable<Contact> {

    private String name;
    private String phone;
    private Address address;

    public int compareTo(Contact other) {
        return name.compareTo(other.name);
    }

    // Add/generate getters/setters and other boilerplate.
}

так что ты можешь просто сделать

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

Collections.sort(contacts);

Если вы хотите определить внешний управляемый порядок (который переопределяет естественный порядок), вам необходимо создать Comparator:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

Вы даже можете определить Comparators в самом Contactобъекте, чтобы вы могли повторно использовать их вместо того, чтобы создавать их каждый раз заново:

public class Contact {

    private String name;
    private String phone;
    private Address address;

    // ...

    public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.phone.compareTo(other.phone);
        }
    };

    public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
        public int compare(Contact one, Contact other) {
            return one.address.compareTo(other.address);
        }
    };

}

который можно использовать следующим образом:

List<Contact> contacts = new ArrayList<Contact>();
// Fill it.

// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);

// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);

И в довершение всего, вы можете подумать об использовании универсального компаратора javabean :

public class BeanComparator implements Comparator<Object> {

    private String getter;

    public BeanComparator(String field) {
        this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
    }

    public int compare(Object o1, Object o2) {
        try {
            if (o1 != null && o2 != null) {
                o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
                o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
            }
        } catch (Exception e) {
            // If this exception occurs, then it is usually a fault of the developer.
            throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
        }

        return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
    }

}

которые вы можете использовать следующим образом:

// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));

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

BalusC
источник
2
Я бы добавил возможность предварительно определить несколько компараторов, а затем использовать их по имени ...
Стобор
2
Фактически, я только что сделал. Легче, чем пытаться объясниться.
Stobor
@BalusC: Нет проблем. Я не могу поверить в идею, я получил ее от String.CASE_INSENSITIVE_ORDERдрузей, но мне она нравится. Делает полученный код более читабельным.
Stobor
1
Те определения компаратора, вероятно, тоже должны быть, staticа может быть, finalтоже ... Или что-то в этом роде ..
Stobor
Хе-хе ... BeanComparator похож на Awesome On A Stick! :-) (Я не помню точное логическое сравнение нулей, но нужно ли ему (o1 == null && o2 == null) ? 0 :в начале этой возвращаемой строки?)
Stobor
28

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

Collection.sort(yourList, Comparator.comparing(YourClass::getFieldToSortOn));

или поскольку у List теперь есть sortметод

yourList.sort(Comparator.comparing(YourClass::getFieldToSortOn));

Объяснение:

Начиная с Java 8, функциональные интерфейсы (интерфейсы только с одним абстрактным методом - они могут иметь больше стандартных или статических методов) могут быть легко реализованы с помощью:

Поскольку Comparator<T>имеет только один абстрактный метод, int compare(T o1, T o2)это функциональный интерфейс.

Так что вместо (пример из ответа @BalusC )

Collections.sort(contacts, new Comparator<Contact>() {
    public int compare(Contact one, Contact other) {
        return one.getAddress().compareTo(other.getAddress());
    }
}); 

мы можем сократить этот код до:

Collections.sort(contacts, (Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress());
});

Мы можем упростить эту (или любую) лямбду, пропустив

  • типы аргументов (Java выведет их на основе сигнатуры метода)
  • или {return...}

Так что вместо

(Contact one, Contact other) -> {
     return one.getAddress().compareTo(other.getAddress();
}

мы можем написать

(one, other) -> one.getAddress().compareTo(other.getAddress())

Также теперь Comparatorесть статические методы, такие как comparing(FunctionToComparableValue)илиcomparing(FunctionToValue, ValueComparator) которые мы могли бы использовать для простого создания компараторов, которые должны сравнивать некоторые конкретные значения из объектов.

Другими словами, мы можем переписать приведенный выше код как

Collections.sort(contacts, Comparator.comparing(Contact::getAddress)); 
//assuming that Address implements Comparable (provides default order).
Pshemo
источник
8

На этой странице рассказывается все, что вам нужно знать о сортировке коллекций, таких как ArrayList.

В основном вам нужно

  • сделайте так, чтобы ваш Contactкласс реализовал Comparableинтерфейс
    • создание public int compareTo(Contact anotherContact)внутри него метода .
  • Как только вы это сделаете, вы можете просто позвонить Collections.sort(myContactList);,
    • где myContactListнаходится ArrayList<Contact>(или любой другой сборник Contact).

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

Пример:

public class Contact implements Comparable<Contact> {

    ....

    //return -1 for less than, 0 for equals, and 1 for more than
    public compareTo(Contact anotherContact) {
        int result = 0;
        result = getName().compareTo(anotherContact.getName());
        if (result != 0)
        {
            return result;
        }
        result = getNunmber().compareTo(anotherContact.getNumber());
        if (result != 0)
        {
            return result;
        }
        ...
    }
}
bguiz
источник
5

BalusC и bguiz уже дали очень полные ответы о том, как использовать встроенные компараторы Java.

Я просто хочу добавить, что в google-коллекциях есть класс Ordering, который более «мощный», чем стандартные компараторы. Возможно, стоит проверить. Вы можете делать классные вещи, такие как составление порядков, их изменение, упорядочивание в зависимости от результата функции для ваших объектов ...

Вот сообщение в блоге, в котором упоминаются некоторые из его преимуществ.

Этьен Невё
источник
Обратите внимание, что коллекции google теперь являются частью Guava (общих java-библиотек Google), поэтому вы можете зависеть от Guava (или модуля коллекции Guava), если хотите использовать класс Ordering.
Этьен Невё
4

Вам нужно сделать так, чтобы ваши классы Contact реализовали Comparable , а затем реализовать compareTo(Contact)метод. Таким образом, Collections.sort сможет отсортировать их за вас. На странице, на которую я ссылался, compareTo «возвращает отрицательное целое число, ноль или положительное целое число, поскольку этот объект меньше, равен или больше указанного объекта».

Например, если вы хотите отсортировать по имени (от А до Я), ваш класс будет выглядеть так:

public class Contact implements Comparable<Contact> {

    private String name;

    // all the other attributes and methods

    public compareTo(Contact other) {
        return this.name.compareTo(other.name);
    }
}
Калеб Браси
источник
Со мной хорошо поработали, спасибо! Я также использовал compareToIgnoreCase, чтобы игнорировать регистр.
Rani Kheir
3

Используя lambdaj, вы можете отсортировать коллекцию ваших контактов (например, по имени) следующим образом

sort(contacts, on(Contact.class).getName());

или по их адресу:

sort(contacts, on(Contacts.class).getAddress());

и так далее. В общем, он предлагает DSL для доступа и управления вашими коллекциями различными способами, например, фильтрация или группировка ваших контактов на основе некоторых условий, агрегирование некоторых значений их свойств и т. Д.

Марио Фуско
источник
0

Collections.sort - хорошая реализация сортировки. Если у вас нет сопоставимой реализации для Contact, вам нужно будет передать реализацию Comparator

Отметить:

Алгоритм сортировки представляет собой модифицированную сортировку слиянием (в которой слияние опускается, если самый высокий элемент в нижнем подсписке меньше самого низкого элемента в высоком подсписке). Этот алгоритм предлагает гарантированную производительность n log (n). Указанный список должен быть изменяемым, но не должен изменять размер. Эта реализация выгружает указанный список в массив, сортирует массив и выполняет итерацию по списку, сбрасывая каждый элемент из соответствующей позиции в массиве. Это позволяет избежать производительности n2 log (n), которая может возникнуть при попытке отсортировать связанный список на месте.

Сортировка слиянием, вероятно, лучше, чем большинство поисковых алгоритмов, которые вы можете использовать.

Джошуа
источник
0

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

public void sortval(){

        String tempname="",tempnum="";

         if (name.size()>1) // check if the number of orders is larger than 1
            {
                for (int x=0; x<name.size(); x++) // bubble sort outer loop
                {
                    for (int i=0; i < name.size()-x-1; i++) {
                        if (name.get(i).compareTo(name.get(i+1)) > 0)
                        {

                            tempname = name.get(i);

                            tempnum=number.get(i);


                           name.set(i,name.get(i+1) );
                           name.set(i+1, tempname);

                            number.set(i,number.get(i+1) );
                            number.set(i+1, tempnum);


                        }
                    }
                }
            }



}
Сарат
источник
Вам потребуется больше времени, чтобы написать это, получить менее оптимальную производительность сортировки, написать больше ошибок (и, надеюсь, больше тестов), и код будет труднее передать другим людям. Так что это не так. Это может сработать, но это не значит, что это правильно.
Эрик
0

используйте этот метод:

private ArrayList<myClass> sortList(ArrayList<myClass> list) {
    if (list != null && list.size() > 1) {
        Collections.sort(list, new Comparator<myClass>() {
            public int compare(myClass o1, myClass o2) {
                if (o1.getsortnumber() == o2.getsortnumber()) return 0;
                return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
            }
        });
    }
    return list;
}

`

и использовать: mySortedlist = sortList(myList); Нет необходимости реализовывать компаратор в вашем классе. Если вы хотите поменять местами обратный порядок 1и-1

Бруно Мулас
источник
0

Хорошо, я знаю, что на это давным-давно ответили ... но вот новая информация:

Скажем, рассматриваемый класс Contact уже имеет определенный естественный порядок с помощью реализации Comparable, но вы хотите переопределить этот порядок, скажем, по имени. Вот современный способ сделать это:

List<Contact> contacts = ...;

contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());

Таким образом, он будет сначала сортировать по имени (в обратном порядке), а затем в случае коллизии имен он вернется к «естественному» порядку, реализованному самим классом Contact.

Шебла Цама
источник
-1

Вы должны использовать функцию Arrays.sort. Содержащие классы должны реализовывать Comparable.

monksy
источник
Вот почему я сказал «Массивы».
monksy
Проблема в том, что OP использует ArrayList, а не массив.
Пшемо