Как написать свой компаратор java

java.lang.Comparable и java.util.Comparator

  • java.lang.Comparable и java.util.Comparator
    • Введение
    • java.lang.Comparable
    • java.util.Comparator
    • Требования
    • Применение
      • Collection и Array
      • Структуры данных
      • Stream
    • Полезные ссылки

Введение

Как уже было сказано в статье про интерфейсы, интерфейс — это определение поведения. Умение объектов сравнивать себя с друг с другом — это точно такое же поведение.

Сравнение используется различными алгоритмами от сортировки и двоичного поиска до поддержания порядка в сортированных коллекциях вроде java.util.TreeMap.

Это повдение можно добавить как в непосредственно классы, с которыми мы работаем, так и объявить специальный класс, умеющий сравнивать два переданных объекта и решать кто из них больше, меньше или они вовсе равны.

Такие классы-судьи называются компараторы.

java.lang.Comparable

Для добавления поведения сравнения непосредственно классам, с которыми мы работаем, существует интерфейс java.lang.Comparable (от англ. сравнимый, сопоставимый):

public interface Comparable<T> {
    /**
     * Compares this object with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     *
     * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
     * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
     * <tt>y.compareTo(x)</tt> throws an exception.)
     *
     * <p>The implementor must also ensure that the relation is transitive:
     * <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies
     * <tt>x.compareTo(z)&gt;0</tt>.
     *
     * <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
     * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
     * all <tt>z</tt>.
     *
     * <p>It is strongly recommended, but <i>not</i> strictly required that
     * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>.  Generally speaking, any
     * class that implements the <tt>Comparable</tt> interface and violates
     * this condition should clearly indicate this fact.  The recommended
     * language is "Note: this class has a natural ordering that is
     * inconsistent with equals."
     *
     * <p>In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.
     *
     * @param   o the object to be compared.
     * @return  a negative integer, zero, or a positive integer as this object
     *          is less than, equal to, or greater than the specified object.
     *
     * @throws NullPointerException if the specified object is null
     * @throws ClassCastException if the specified object's type prevents it
     *         from being compared to this object.
     */
    public int compareTo(T o);
}

Интерфейс добавляет только один метод, который принимает объект и возвращает результат сравнения:

  • 0, если два объекта равны.
  • отрицательное число, если объект меньше того, с которым его сравнивают. Обычно возвращают значение -1.
  • положительное число, если объект больше того, с которым его сравнивают. Обычно возвращают значение 1.

По контракту метода при работе с null должно быть выброшено исключение java.lang.NullPointerException.

Во многих классах интерфейс java.lang.Comparable уже реализован, например, в классах java.lang.Integer, java.lang.String и т.д.

Когда имеет смысл задуматься о реализации этого интерфейса в вашем классе? Тогда, когда определен естественный порядок у объектов класса. Например, в числах!

Для примера научим объекты класса Person, этой рабочей лошадке всех примеров в интернете, сравниваться между собой:

public class Person implements Comparable<Person> {
    private String name;
    private int age;
    private LocalDate birthDate;

    Person(String name, int age, LocalDate birthDate) {
        this.age = age;
        this.name = name;
        this.birthDate = birthDate;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public int compareTo(Person p) {
        return name.compareTo(p.getName());
    }
}

В данной реализации сравнение объектов будет происходить по именам лексеграфически.

Но что делать, если вас не устраивает текущая реализация? Например, вы хотите сравнивать наших Person по возрасту, а не имени! Или вы попали в ситуацию, когда разработчик вообще не предусмотрел реализацию java.lang.Comparable в своем классе, а вам надо как-то добавить это поведение, уметь сравнивать объекты. Или ситуация такова, что вам необходимо реализовать несколько видов сравнений в разных случаях. На такой случай существуют компараторы, те самые классы-судьи.

java.util.Comparator

Компараторы — это отдельные классы, реализующие интерфейс java.util.Comparator.
Интерфейс java.util.Comparator содержит ряд методов, ключевым из которых является метод compare:

public interface Comparator<E> {
 
    /**
     * Compares its two arguments for order.  Returns a negative integer,
     * zero, or a positive integer as the first argument is less than, equal
     * to, or greater than the second.<p>
     *
     * In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.<p>
     *
     * The implementor must ensure that <tt>sgn(compare(x, y)) ==
     * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>compare(x, y)</tt> must throw an exception if and only
     * if <tt>compare(y, x)</tt> throws an exception.)<p>
     *
     * The implementor must also ensure that the relation is transitive:
     * <tt>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
     * <tt>compare(x, z)&gt;0</tt>.<p>
     *
     * Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
     * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
     * <tt>z</tt>.<p>
     *
     * It is generally the case, but <i>not</i> strictly required that
     * <tt>(compare(x, y)==0) == (x.equals(y))</tt>.  Generally speaking,
     * any comparator that violates this condition should clearly indicate
     * this fact.  The recommended language is "Note: this comparator
     * imposes orderings that are inconsistent with equals."
     *
     * @param o1 the first object to be compared.
     * @param o2 the second object to be compared.
     * @return a negative integer, zero, or a positive integer as the
     *         first argument is less than, equal to, or greater than the
     *         second.
     * @throws NullPointerException if an argument is null and this
     *         comparator does not permit null arguments
     * @throws ClassCastException if the arguments' types prevent them from
     *         being compared by this comparator.
     */
    int compare(T o1, T o2);
    
    // остальные методы
}

Метод также возвращает числовое значение — если оно отрицательное, то объект o1 считается меньше, предшествует объекту o2, иначе — наоборот. А если метод возвращает ноль, то объекты равны.

Компаратор на все тот же Person, но сравнивающий по именам:

class PersonBirthDateComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthDate().compareTo(b.getBirthDate());
    }
}

Далее везде, где вам необходимо поведение сравнения по датам рождения вы явно спрашиваете PersonBirthDateComparator через вызов compare.

Надо отметить, что многие классы умеют работать и с компараторами, и с классами, реализующими java.lang.Comparable, например, java.util.TreeMap:

Map<Person, String> treeMap = new TreeMap<>(); // 1
Map<Person, String> birthDateTreeMap = new TreeMap<>(new PersonBirthDateComparator()); // 2

В первом случае дерево TreeMap будет строиться с помощью сравнения по именам, во втором же мы явно передали компаратор, отвечающий за сравнения и будет использован именно он.

Требования

Помните три важных свойства сравнивающей функции: рефлексивность (сравнение элемента с самим собой всегда даёт 0), антисимметричность (сравнение A с B и B с A должны дать разный знак) и транзитивность (если сравнение A с B и B с C выдаёт одинаковый результат, то и сравнение A с C должно выдать такой же).

При несоблюдении этих свойств результат работы алгоритмов сортировки и структур данных, основанных на сравнении, как например, java.util.TreeMap, будет неверным.

Применение

Collection и Array

Классы java.util.Collections и java.util.Arrays предоставляют метод sort для сортировки с помощью компаратора списков и массивов соответственно:

    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        // ...
    }

и

public static <T> void sort(T[] a, Comparator<? super T> c) {
    // ...
}

Структуры данных

Структуры данных, типа java.util.TreeMap или java.util.TreeSet работают только с элементами, реализующими java.lang.Comparable, или требуют в конструкторе компаратор.

Set<Message> messages = new TreeSet(comparator);

Вопрос:

Небольшое лирическое отступление от темы!

А что будет, если мы создадим TreeMap и ключом укажем класс, который не реализует java.lang.Comparable и при этом не передадим компаратор?

class Student {
    private int age;

    public Student(int age) {
        this.age = age;
    }

    // getters and setters
}

// psvm
Map<Person, String> treeMap = new TreeMap<>(); 

Ответ:

Объект TreeMap будет создан, но при первом же добавлении элемента будет выброшено исключение, так как для работы TreeMap необходимо, чтобы ключи умели сравниваться, мы же не добавили этого поведения и не предоставили компаратор, поэтому работать TreeMap не сможет:

Exception in thread "main" java.lang.ClassCastException: class Student cannot be cast to class java.lang.Comparable

Подробнее о работе TreeMap.


Stream

В Java 8 появились Stream-ы, позволяющие использовать компараторы прямо в цепочке вычислений:

stream
    .sorted(Comparator.naturalOrder())
    .forEach(e -> System.out.println(e));

Вопрос:

Я написал свой компаратор, работающий с целыми числами:

class IntegerComparator implements Comparator<Integer> {
    public int compare(Integer a, Integer b) {
        return a - b;
    }
}

Видите ли вы ошибку?

Ответ:

Компаратор реализован неправильно, так как не учитывает возможности переполнения (overflow) у java.lang.Integer.

int a = -2000000000;
int b =  2000000000;
System.out.println(a - b); // prints "294967296"

Помните об этом, когда работаете с числами в Java!

Также помните о существовании NaN у Double и Float. Оно не больше, не меньше и не равно никакому другому числу.


Полезные ссылки

  1. Тагир Валеев Пишите компараторы правильно
  2. Timur Batyrshinov Compare и Comparator в Java
  3. Subtraction comparator
  4. Overflow and Underflow in Java

Stop(Id, Name) is a java class, and i want to store these stop objects in a java.util.Set and those objects should be sorted according to the Id of Stop.
this is my comparator

public class StopsComparator implements Comparator{

    @Override
    public int compare(Object o1, Object o2) {
        // TODO Auto-generated method stub
        Stop stop1 = (Stop)o1;
        Stop stop2 = (Stop)o2;

        return stop1.getStopId().compareTo(stop2.getStopId());
    }
}


 private Set<Stop> stops = new TreeSet<Stop>(new StopsComparator());

but its not giving correct result?

Bhesh Gurung's user avatar

Bhesh Gurung

50.1k22 gold badges91 silver badges141 bronze badges

asked Dec 24, 2011 at 18:30

Ramesh Kotha's user avatar

Ramesh KothaRamesh Kotha

8,22617 gold badges65 silver badges89 bronze badges

2

Does Stop implement an equals method that works on the same field as your comparator? If not then that will lead to problems. You also might want to switch to have your object implement Comparable (although that wouldn’t fix the problem you’re seeing).

Once you implement an equals() method, then you should also implement a hashCode() method that works on the same field.

Findbugs would have probably told you these things. Its extremely useful.

answered Dec 24, 2011 at 18:41

BillRobertson42's user avatar

BillRobertson42BillRobertson42

12.5k4 gold badges40 silver badges57 bronze badges

The following code works for me —

public class Stop {

    private Long id;
    private String name;

    public Stop(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Stop{" +
                "id=" + id +
                ", name='" + name + ''' +
                '}';
    }

    private static class StopComparator implements Comparator<Stop> {


        public int compare(Stop o1, Stop o2) {
            return o1.getId().compareTo(o2.getId());
        }
    }

    public static void main(String[] args) {
        Set<Stop> set = new TreeSet<Stop>(new StopComparator());
        set.add(new Stop(102L, "name102"));
        set.add(new Stop(66L, "name66"));
        set.add(new Stop(72L, "name72"));
        System.out.println(set);
    }
}

prints —

[Stop{id=66, name=’name66′}, Stop{id=72, name=’name72′}, Stop{id=102,
name=’name102′}]

Ofc you need to implement equals and hashcode so that class behaves consistently in each Set implementation, but for TreeSet this should work as is since TreeSet relies on compareTo method while performing add, remove or contains operations (instead of equals like HashSet).

answered Dec 24, 2011 at 19:04

Premraj's user avatar

2

This is from the Comparator docs:

The ordering imposed by a comparator c on a set of elements S is said to be consistent with equals if and only if c.compare(e1, e2)==0 has the same boolean value as e1.equals(e2) for every e1 and e2 in S.

Caution should be exercised when using a comparator capable of imposing an ordering inconsistent with equals to order a sorted set (or sorted map). Suppose a sorted set (or sorted map) with an explicit comparator c is used with elements (or keys) drawn from a set S. If the ordering imposed by c on S is inconsistent with equals, the sorted set (or sorted map) will behave «strangely.» In particular the sorted set (or sorted map) will violate the general contract for set (or map), which is defined in terms of equals.

I would recommend to try implementing equals and hashCode.

Community's user avatar

answered Dec 24, 2011 at 18:37

Bhesh Gurung's user avatar

Bhesh GurungBhesh Gurung

50.1k22 gold badges91 silver badges141 bronze badges

В этом посте будет обсуждаться, как сортировать список объектов с помощью Comparator в Java.

А Comparator — это функция сравнения, которая упорядочивает наборы объектов, которые не имеют естественного порядка. Разработчик этого класса должен переопределить абстрактный метод compare() определено в java.util.Comparator, который сравнивает два своих аргумента для порядка. Значение, возвращаемое compare() метод определяет положение первого объекта относительно второго объекта.

  • Если compare() возвращает отрицательное целое число, первый аргумент меньше второго.
  • Если compare() возвращает ноль, первый аргумент равен второму.
  • Если compare() возвращает положительное целое число, первый аргумент больше второго.

 
Есть несколько способов реализовать компараторы в Java:

1. Передайте компаратор в качестве аргумента sort() метод

Компараторы, если они передаются методу сортировки (например, Collections.sort (а также Arrays.sort), позволяют точно контролировать порядок сортировки. В следующем примере мы получаем Comparator что сравнивает Person предметы по возрасту.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

import java.util.*;

class Person

{

    private String name;

    private int age;

    public Person(String name, int age)

    {

        this.name = name;

        this.age = age;

    }

    @Override

    public String toString()

    {

        return «{« +

                        «name='» + name + »’ +

                        «, age=» + age +

                        ‘}’;

    }

    public String getName() {

        return name;

    }

    public int getAge() {

        return age;

    }

}

class Main

{

    public static void main(String[] args)

    {

        List<Person> persons = new ArrayList<>(Arrays.asList(

                                            new Person(«John», 15),

                                            new Person(«Sam», 25),

                                            new Person(«Will», 20),

                                            new Person(«Dan», 20),

                                            new Person(«Joe», 10)

                                        ));

        Collections.sort(persons, new Comparator<Person>() {

            @Override

            public int compare(Person p1, Person p2) {

                return p1.getAge() p2.getAge();

            }

        });

        System.out.println(persons);

    }

}

Скачать  Выполнить код

результат:

[{name=’Joe’, age=10}, {name=’John’, age=15}, {name=’Will’, age=20}, {name=’Dan’, age=20}, {name=’Sam’, age=25}]

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

Collections.sort(persons, new Comparator<Person>() {

    @Override

    public int compare(Person p1, Person p2) {

        return p1.getAge() p2.getAge();

    }

});

можно переписать как:

Collections.sort(persons, (p1, p2) -> p1.getAge() p2.getAge());

 
Java 8 представила несколько улучшений для Comparator интерфейс. Теперь Comparator имеет статические методы, такие как comparing(), который может легко создавать компараторы для сравнения некоторых конкретных значений объектов. Например, для получения Comparator что сравнивает Person объектов по их возрасту, мы можем сделать:

Comparator<Person> byAge = Comparator.comparing(Person::getAge);

Collections.sort(persons, byAge);

 
Приведенный выше код будет сортировать список людей только по age поле. Если два человека имеют одинаковый возраст, их относительный порядок в отсортированном списке не является фиксированным. Таким образом, предпочтительно сравнивать объекты, используя несколько полей, чтобы избежать таких случаев.

Как сравнить объекты с несколькими полями?

1. Мы можем легко отсортировать список людей сначала по age а затем name, как показано ниже. Теперь для лиц одного возраста порядок определяется по имени человека.

Collections.sort(persons, new Comparator<Person>() {

    @Override

    public int compare(Person p1, Person p2)

    {

        if (p1.getAge() != p2.getAge()) {

            return p1.getAge() p2.getAge();

        }

        return p1.getName().compareTo(p2.getName());

    }

});

Скачать  Выполнить код

 
Обратите внимание, что мы просто уменьшили значение int примитивный тип age друг от друга, а для объекта String встроенный метод сравнения compareTo() используется. Как правило, эта цепочка может продолжаться и дальше, включая и другие свойства.

 
2. Мы также можем сделать это с лямбда-выражениями, используя .thenComparing() метод, который эффективно объединяет два сравнения в одно:

Comparator<Person> byAge = Comparator.comparing(Person::getAge);

Comparator<Person> byName = Comparator.comparing(Person::getName);

Collections.sort(persons, byAge.thenComparing(byName));

Скачать  Выполнить код

 
3. Мы также можем использовать Guava ComparisonChain для выполнения оператора цепного сравнения, как показано ниже:

Collections.sort(persons, new Comparator<Person>() {

    @Override

    public int compare(Person p1, Person p2)

    {

        return ComparisonChain.start()

                        .compare(p1.getAge(), p2.getAge())

                        .compare(p1.getName(), p2.getName())

                        .result();

    }

});

Скачать код

 
Возвращаемое значение compare() будет иметь тот же знак, что и первый ненулевой результат сравнения в цепочке, или будет равен нулю, если все результаты сравнения были равны нулю. Обратите внимание, что ComparisonChain прекращает вызывать свои входы compareTo а также compare методы, как только один из них возвращает ненулевой результат.

 
4. Мы также можем использовать CompareToBuilder класса библиотеки Apache Commons Lang, чтобы помочь в реализации Comparator.compare() метод. Чтобы использовать этот класс, напишите следующий код:

Collections.sort(persons, new Comparator<Person>() {

    @Override

    public int compare(Person p1, Person p2)

    {

        return new CompareToBuilder()

                        .append(p1.getAge(), p2.getAge())

                        .append(p1.getName(), p2.getName())

                        .toComparison();

    }

});

Скачать код

 
Значения сравниваются в том порядке, в котором они добавляются к построителю. Если какое-либо сравнение возвращает ненулевой результат, то это значение будет возвращено функцией toComparison(), и все последующие сравнения пропускаются.

Мы можем даже реализовать Comparator в отдельный класс, а затем передать экземпляр этого класса в sort() метод. Это показано ниже:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

import java.util.*;

class Person

{

    private String name;

    private int age;

    public Person(String name, int age)

    {

        this.name = name;

        this.age = age;

    }

    @Override

    public String toString()

    {

        return «{« +

                        «name='» + name + »’ +

                        «, age=» + age +

                        ‘}’;

    }

    public String getName() {

        return name;

    }

    public int getAge() {

        return age;

    }

}

class MyComparator implements Comparator<Person>

{

    @Override

    public int compare(Person p1, Person p2)

    {

        if (p1.getAge() != p2.getAge()) {

            return p1.getAge() p2.getAge();

        }

        return p1.getName().compareTo(p2.getName());

    }

}

class Main

{

    public static void main(String[] args)

    {

        List<Person> persons = new ArrayList<>(Arrays.asList(

                                            new Person(«John», 15),

                                            new Person(«Sam», 25),

                                            new Person(«Will», 20),

                                            new Person(«Dan», 20),

                                            new Person(«Joe», 10)

                                        ));

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

        System.out.println(persons);

    }

}

Скачать  Выполнить код

результат:

[{name=’Joe’, age=10}, {name=’John’, age=15}, {name=’Dan’, age=20}, {name=’Will’, age=20}, {name=’Sam’, age=25}]

3. Передать компаратор в List.sort() метод

Java 8 представила несколько улучшений для List интерфейс. В настоящее время List имеет собственный метод сортировки sort() который сортирует список в соответствии с порядком, индуцированным указанным Comparator. Это показано ниже:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

import java.util.*;

class Person

{

    private String name;

    private int age;

    public Person(String name, int age)

    {

        this.name = name;

        this.age = age;

    }

    @Override

    public String toString()

    {

        return «{« +

                        «name='» + name + »’ +

                        «, age=» + age +

                        ‘}’;

    }

    public String getName() {

        return name;

    }

    public int getAge() {

        return age;

    }

}

class Main

{

    public static void main(String[] args)

    {

        List<Person> persons = new ArrayList<>(Arrays.asList(

                                            new Person(«John», 15),

                                            new Person(«Sam», 25),

                                            new Person(«Will», 20),

                                            new Person(«Dan», 20),

                                            new Person(«Joe», 10)

                                        ));

        persons.sort(Comparator.comparing(Person::getAge)

                                .thenComparing(Comparator.comparing(Person::getName)));

        System.out.println(persons);

    }

}

Скачать  Выполнить код

результат:

[{name=’Joe’, age=10}, {name=’John’, age=15}, {name=’Dan’, age=20}, {name=’Will’, age=20}, {name=’Sam’, age=25}]

4. Передать компаратор в Stream.sorted() метод

Мы также можем передать наш компаратор в sorted() метод Stream класс, который возвращает поток, состоящий из элементов этого потока, отсортированных в соответствии с предоставленным Comparator. Вот рабочий пример:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Comparator;

import java.util.List;

import java.util.stream.Collectors;

class Person

{

    private String name;

    private int age;

    public Person(String name, int age)

    {

        this.name = name;

        this.age = age;

    }

    @Override

    public String toString()

    {

        return «{« +

                        «name='» + name + »’ +

                        «, age=» + age +

                        ‘}’;

    }

    public String getName() {

        return name;

    }

    public int getAge() {

        return age;

    }

}

class Main

{

    public static void main(String[] args)

    {

        List<Person> persons = new ArrayList<>(Arrays.asList(

                                            new Person(«John», 15),

                                            new Person(«Sam», 25),

                                            new Person(«Will», 20),

                                            new Person(«Dan», 20),

                                            new Person(«Joe», 10)

                                        ));

        persons = persons.stream()

                        .sorted(Comparator.comparing(Person::getAge)

                                        .thenComparing(Comparator.comparing(Person::getName)))

                        .collect(Collectors.toList());

        System.out.println(persons);

    }

}

Скачать  Выполнить код

результат:

[{name=’Joe’, age=10}, {name=’John’, age=15}, {name=’Dan’, age=20}, {name=’Will’, age=20}, {name=’Sam’, age=25}]

Это все, что касается сортировки списка объектов с помощью Comparator в Java.

 
Продолжить чтение:

Сортировка списка объектов с помощью Comparable в Java

Employees : [Employee{id=1010, name=’Rajeev’, salary=100000.0, joiningDate=2010-07-10}, Employee{id=1004, name=’Chris’, salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name=’David’, salary=134000.0, joiningDate=2017-09-28}, Employee{id=1009, name=’Steve’, salary=100000.0, joiningDate=2016-05-18}]

Employees (Sorted by Name) : [Employee{id=1004, name=’Chris’, salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name=’David’, salary=134000.0, joiningDate=2017-09-28}, Employee{id=1010, name=’Rajeev’, salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name=’Steve’, salary=100000.0, joiningDate=2016-05-18}]

Employees (Sorted by Salary) : [Employee{id=1004, name=’Chris’, salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name=’Rajeev’, salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name=’Steve’, salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name=’David’, salary=134000.0, joiningDate=2017-09-28}]

Employees (Sorted by JoiningDate) : [Employee{id=1010, name=’Rajeev’, salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name=’Steve’, salary=100000.0, joiningDate=2016-05-18}, Employee{id=1004, name=’Chris’, salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name=’David’, salary=134000.0, joiningDate=2017-09-28}]

Employees (Sorted by Name in descending order) : [Employee{id=1009, name=’Steve’, salary=100000.0, joiningDate=2016-05-18}, Employee{id=1010, name=’Rajeev’, salary=100000.0, joiningDate=2010-07-10}, Employee{id=1015, name=’David’, salary=134000.0, joiningDate=2017-09-28}, Employee{id=1004, name=’Chris’, salary=95000.5, joiningDate=2017-03-19}]

Employees (Sorted by Salary and Name) : [Employee{id=1004, name=’Chris’, salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name=’Rajeev’, salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name=’Steve’, salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name=’David’, salary=134000.0, joiningDate=2017-09-28}]

A comparator interface is used to order the objects of user-defined classes. A comparator object is capable of comparing two objects of the same class. Following function compare obj1 with obj2.

Syntax: 

public int compare(Object obj1, Object obj2):

Suppose we have an Array/ArrayList of our own class type, containing fields like roll no, name, address, DOB, etc, and we need to sort the array based on Roll no or name?

Method 1: One obvious approach is to write our own sort() function using one of the standard algorithms. This solution requires rewriting the whole sorting code for different criteria like Roll No. and Name.

Method 2: Using comparator interface- Comparator interface is used to order the objects of a user-defined class. This interface is present in java.util package and contains 2 methods compare(Object obj1, Object obj2) and equals(Object element). Using a comparator, we can sort the elements based on data members. For instance, it may be on roll no, name, age, or anything else.

Method of Collections class for sorting List elements is used to sort the elements of List by the given comparator.  

public void sort(List list, ComparatorClass c)

To sort a given List, ComparatorClass must implement a Comparator interface.

How do the sort() method of Collections class work? 

Internally the Sort method does call Compare method of the classes it is sorting. To compare two elements, it asks “Which is greater?” Compare method returns -1, 0, or 1 to say if it is less than, equal, or greater to the other. It uses this result to then determine if they should be swapped for their sort.

Example

Java

import java.io.*;

import java.lang.*;

import java.util.*;

class Student {

    int rollno;

    String name, address;

    public Student(int rollno, String name, String address)

    {

        this.rollno = rollno;

        this.name = name;

        this.address = address;

    }

    public String toString()

    {

        return this.rollno + " " + this.name + " "

            + this.address;

    }

}

class Sortbyroll implements Comparator<Student> {

    public int compare(Student a, Student b)

    {

        return a.rollno - b.rollno;

    }

}

class Sortbyname implements Comparator<Student> {

    public int compare(Student a, Student b)

    {

        return a.name.compareTo(b.name);

    }

}

class GFG {

    public static void main(String[] args)

    {

        ArrayList<Student> ar = new ArrayList<Student>();

        ar.add(new Student(111, "Mayank", "london"));

        ar.add(new Student(131, "Anshul", "nyc"));

        ar.add(new Student(121, "Solanki", "jaipur"));

        ar.add(new Student(101, "Aggarwal", "Hongkong"));

        System.out.println("Unsorted");

        for (int i = 0; i < ar.size(); i++)

            System.out.println(ar.get(i));

        Collections.sort(ar, new Sortbyroll());

        System.out.println("nSorted by rollno");

        for (int i = 0; i < ar.size(); i++)

            System.out.println(ar.get(i));

        Collections.sort(ar, new Sortbyname());

        System.out.println("nSorted by name");

        for (int i = 0; i < ar.size(); i++)

            System.out.println(ar.get(i));

    }

}

Output

Unsorted
111 Mayank london
131 Anshul nyc
121 Solanki jaipur
101 Aggarwal Hongkong

Sorted by rollno
101 Aggarwal Hongkong
111 Mayank london
121 Solanki jaipur
131 Anshul nyc

Sorted by name
101 Aggarwal Hongkong
131 Anshul nyc
111 Mayank london
121 Solanki jaipur

By changing the return value inside the compare method, you can sort in any order that you wish to, for example: For descending order just change the positions of ‘a’ and ‘b’ in the above compare method.

Sort collection by more than one field

In the previous example, we have discussed how to sort the list of objects on the basis of a single field using Comparable and Comparator interface But, what if we have a requirement to sort ArrayList objects in accordance with more than one field like firstly, sort according to the student name and secondly, sort according to student age.

Example

Java

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.Iterator;

import java.util.List;

class Student {

    String Name;

    int Age;

    public Student(String Name, Integer Age)

    {

        this.Name = Name;

        this.Age = Age;

    }

    public String getName() { return Name; }

    public void setName(String Name) { this.Name = Name; }

    public Integer getAge() { return Age; }

    public void setAge(Integer Age) { this.Age = Age; }

    @Override public String toString()

    {

        return "Customer{"

            + "Name=" + Name + ", Age=" + Age + '}';

    }

    static class CustomerSortingComparator

        implements Comparator<Student> {

        @Override

        public int compare(Student customer1,

                           Student customer2)

        {

            int NameCompare = customer1.getName().compareTo(

                customer2.getName());

            int AgeCompare = customer1.getAge().compareTo(

                customer2.getAge());

            return (NameCompare == 0) ? AgeCompare

                                      : NameCompare;

        }

    }

    public static void main(String[] args)

    {

        List<Student> al = new ArrayList<>();

        Student obj1 = new Student("Ajay", 27);

        Student obj2 = new Student("Sneha", 23);

        Student obj3 = new Student("Simran", 37);

        Student obj4 = new Student("Ajay", 22);

        Student obj5 = new Student("Ajay", 29);

        Student obj6 = new Student("Sneha", 22);

        al.add(obj1);

        al.add(obj2);

        al.add(obj3);

        al.add(obj4);

        al.add(obj5);

        al.add(obj6);

        Iterator<Student> custIterator = al.iterator();

        System.out.println("Before Sorting:n");

        while (custIterator.hasNext()) {

            System.out.println(custIterator.next());

        }

        Collections.sort(al,

                         new CustomerSortingComparator());

        System.out.println("nnAfter Sorting:n");

        for (Student customer : al) {

            System.out.println(customer);

        }

    }

}

Output

Before Sorting:

Customer{Name=Ajay, Age=27}
Customer{Name=Sneha, Age=23}
Customer{Name=Simran, Age=37}
Customer{Name=Ajay, Age=22}
Customer{Name=Ajay, Age=29}
Customer{Name=Sneha, Age=22}


After Sorting:

Customer{Name=Ajay, Age=22}
Customer{Name=Ajay, Age=27}
Customer{Name=Ajay, Age=29}
Customer{Name=Simran, Age=37}
Customer{Name=Sneha, Age=22}
Customer{Name=Sneha, Age=23}

This article is contributed by Rishabh Mahrsee. If you like GeeksforGeeks and would like to contribute, you can also write an article and mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

Понравилась статья? Поделить с друзьями:
  • Как написать свой комикс
  • Как написать свой код для программы
  • Как написать свой клип
  • Как написать свой клиент майнкрафт
  • Как написать свой класс python