Сортування за допомогою компаратора - за спаданням (визначені користувачем класи) [закрито]


78

Я хочу сортувати свої об'єкти за спаданням за допомогою компаратора.

class Person {
 private int age;
}

Тут я хочу відсортувати масив об’єктів Person.

Як я можу це зробити?

Відповіді:


117

Ви можете зробити низхідний сорт визначеного користувачем класу таким чином, перевизначивши метод compare () ,

Collections.sort(unsortedList,new Comparator<Person>() {
    @Override
    public int compare(Person a, Person b) {
        return b.getName().compareTo(a.getName());
    }
});

Або за допомогою Collection.reverse()сортування за спаданням як користувач Prince згадував у своєму коментарі .

І ви можете зробити висхідне сортування таким чином,

Collections.sort(unsortedList,new Comparator<Person>() {
    @Override
    public int compare(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
});

Замініть наведений вище код лямбда-виразом (Java 8 і далі), і ми отримаємо стислий вислів:

Collections.sort(personList, (Person a, Person b) -> b.getName().compareTo(a.getName()));

Починаючи з Java 8, List має метод sort (), який приймає Comparator як параметр (більш стислий):

personList.sort((a,b)->b.getName().compareTo(a.getName()));

Тут aі bвиводяться як тип особи за лямбда-виразом.


24
Або ви можете просто використовувати Collections.reverseOrder(...)для сортування за спаданням.
Принц

1
@Prince Не фанат такого підходу, він змушує мене поглянути на оригінальний метод, щоб побачити, як все сортується для початку (не
змінившись

1
Варто згадати, що compareToIgnoreCaseце також зручно при порівнянні Stringоб’єктів, найчастіше я використовую це замість простоcompareTo
Mark Keen

Працював як оберіг! Дякую.
Містер Порт Сент-Джо

69

Для чого тут варто - це моя стандартна відповідь. Єдине, що нове тут - це те, що використовує Collections.reverseOrder (). Плюс, це подає всі пропозиції на одному прикладі:

/*
**  Use the Collections API to sort a List for you.
**
**  When your class has a "natural" sort order you can implement
**  the Comparable interface.
**
**  You can use an alternate sort order when you implement
**  a Comparator for your class.
*/
import java.util.*;

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

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

    public String getName()
    {
        return name;
    }

    public int getAge()
    {
        return age;
    }

    public String toString()
    {
        return name + " : " + age;
    }

    /*
    **  Implement the natural order for this class
    */
    public int compareTo(Person p)
    {
        return getName().compareTo(p.getName());
    }

    static class AgeComparator implements Comparator<Person>
    {
        public int compare(Person p1, Person p2)
        {
            int age1 = p1.getAge();
            int age2 = p2.getAge();

            if (age1 == age2)
                return 0;
            else if (age1 > age2)
                return 1;
            else
                return -1;
        }
    }

    public static void main(String[] args)
    {
        List<Person> people = new ArrayList<Person>();
        people.add( new Person("Homer", 38) );
        people.add( new Person("Marge", 35) );
        people.add( new Person("Bart", 15) );
        people.add( new Person("Lisa", 13) );

        // Sort by natural order

        Collections.sort(people);
        System.out.println("Sort by Natural order");
        System.out.println("\t" + people);

        // Sort by reverse natural order

        Collections.sort(people, Collections.reverseOrder());
        System.out.println("Sort by reverse natural order");
        System.out.println("\t" + people);

        //  Use a Comparator to sort by age

        Collections.sort(people, new Person.AgeComparator());
        System.out.println("Sort using Age Comparator");
        System.out.println("\t" + people);

        //  Use a Comparator to sort by descending age

        Collections.sort(people,
            Collections.reverseOrder(new Person.AgeComparator()));
        System.out.println("Sort using Reverse Age Comparator");
        System.out.println("\t" + people);
    }
}

це насправді спрацювало для мене, і це також дуже просте рішення (лише один рядок).
розробник android

Ідеальний приклад! Дякую.
Майкл Уайлдермут,

Чи є спосіб сортувати рядки в TreeSet, використовуючи такий компаратор? Те саме, люди за віком.
Zeff520

Я ніколи не використовував TreeSet, але в документації API сказано - Елементи впорядковуються за їх природним упорядкуванням або за допомогою компаратора, який надається під час створення встановленого часу, залежно від того, який конструктор використовується, тому я гадаю, це можливо.
camickr

17

Я б створив компаратор для класу person, який можна параметризувати за допомогою певної поведінки сортування. Тут я можу встановити порядок сортування, але його можна змінити, щоб дозволити сортування і для інших атрибутів особи.

public class PersonComparator implements Comparator<Person> {

  public enum SortOrder {ASCENDING, DESCENDING}

  private SortOrder sortOrder;

  public PersonComparator(SortOrder sortOrder) {
    this.sortOrder = sortOrder;
  }

  @Override
  public int compare(Person person1, Person person2) {
    Integer age1 = person1.getAge();
    Integer age2 = person2.getAge();
    int compare = Math.signum(age1.compareTo(age2));

    if (sortOrder == ASCENDING) {
      return compare;
    } else {
      return compare * (-1);
    }
  }
}

(сподіваюсь, що він компілюється зараз, у мене під рукою немає IDE або JDK, кодованого "сліпим")

Редагувати

Завдяки Томасу відредагував код. Я б не сказав, що використання Math.signum є хорошим, продуктивним, ефективним, але я хотів би зберегти це як нагадування, що метод compareTo може повернути будь-яке ціле число і множення на (-1) не вдасться, якщо реалізація повертає Integer.MIN_INTEGER ... І я видалив сеттер, оскільки він досить дешевий для створення нового PersonComparator саме тоді, коли це потрібно.

Але я продовжую боротися, бо це свідчить про те, що я покладаюсь на існуючу порівняльну реалізацію. Міг зробити щось подібне, Comparable<Integer> age1 = new Integer(person1.getAge());але це виглядало надто негарно. Ідея полягала в тому, щоб показати зразок, який можна було б легко адаптувати до інших атрибутів Людини, таких як ім’я, день народження як Дата тощо.


6
Колись давно була гарна звичка залишати коментар, щоб допомогти щойно обраному автору покращити своє повідомлення.
Андреас Долк,

Я не голосував проти, але compare * (-1)схильний до переповнення. Я зробив ту ж помилку у своєму початковому дописі.
Томас Юнг

І SortOrder повинен бути встановлений у конструкторі і бути остаточним. Використання обгортки - кращий підхід, я гадаю:new Reverse(new PersonComparator())
Томас Юнг

І Integer age1 = ...;має бокс накладні витрати.
Томас Юнг

Так, так, так, достатньо місця для вдосконалення. Бокс був спеціальним, і сеттер дозволяє використовувати компаратор для зростання та спадання - з іншого боку, ви маєте рацію, краще кожен раз створюйте новий, а потім повторно використовуйте той самий. Будівництво досить дешеве.
Андреас Долк,

14
String[] s = {"a", "x", "y"};
Arrays.sort(s, new Comparator<String>() {

    @Override
    public int compare(String o1, String o2) {
        return o2.compareTo(o1);
    }
});
System.out.println(Arrays.toString(s));

-> [y, x, a]

Тепер вам потрібно застосувати компаратор для вашого класу Person. Щось на зразок (для зростання): compare(Person a, Person b) = a.id < b.id ? -1 : (a.id == b.id) ? 0 : 1або Integer.valueOf(a.id).compareTo(Integer.valueOf(b.id)).

Щоб мінімізувати плутанину, вам слід застосувати висхідний компаратор і перетворити його на спадний за допомогою обгортки ( наприклад, цієї ) new ReverseComparator<Person>(new PersonComparator()).


Тепер це трохи заплутаний приклад, оскільки String реалізує Comparable. І це -1 - це не найпростіша річ.
Божо

1
Найкращий аргумент проти -1 * х - це -1 * Integer.MIN_VALUE == Integer.MIN_VALUE. Що не те, що ти хочеш. Я поміняв аргументи, які і так легше.
Томас Юнг

1
І я думаю, це ReverseComparator, а не Reserve ...
Adriaan Koster

ну, це був не найкращий аргумент. Це просто заплутало початківців :) Це все ще є, бо це вимагало б від нього знання, що таке Порівняне, і усвідомлення того, що його Особа не реалізує це. Але тоді - мислення - це добре :)
Божо

Integer.compareзгадує у своїй документації наступне:, The value returned is identical to what would be returned by: Integer.valueOf(x).compareTo(Integer.valueOf(y))тому ви можете Integer.compareзамість цього використовувати .
keyser

5

Використання колекцій Google:

class Person {
 private int age;

 public static Function<Person, Integer> GET_AGE =
  new Function<Person, Integer> {
   public Integer apply(Person p) { return p.age; }
  };

}

public static void main(String[] args) {
 ArrayList<Person> people;
 // Populate the list...

 Collections.sort(people, Ordering.natural().onResultOf(Person.GET_AGE).reverse());
}

0
package com.test;

import java.util.Arrays;

public class Person implements Comparable {

private int age;

private Person(int age) {
    super();
    this.age = age;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
}

@Override
public int compareTo(Object o) {
    Person other = (Person)o;
    if (this == other)
        return 0;
    if (this.age < other.age) return 1;
    else if (this.age == other.age) return 0;
    else return -1;

}

public static void main(String[] args) {

    Person[] arr = new Person[4];
    arr[0] = new Person(50);
    arr[1] = new Person(20);
    arr[2] = new Person(10);
    arr[3] = new Person(90);

    Arrays.sort(arr);

    for (int i=0; i < arr.length; i++ ) {
        System.out.println(arr[i].age);
    }
}

}

Ось один із способів зробити це.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.