Как выполнить сортировку по двум полям в Java?
У меня есть массив объектов person (int age; String name;)
.
Как я могу отсортировать этот массив в алфавитном порядке по имени, а затем по возрасту?
Какой алгоритм вы бы использовали для этого?
Переведено автоматически
Ответ 1
Вы можете использовать Collections.sort
следующим образом:
private static void order(List<Person> persons) {
Collections.sort(persons, new Comparator() {
public int compare(Object o1, Object o2) {
String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);
if (sComp != 0) {
return sComp;
}
Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}
List<Persons>
теперь сортируется по имени, затем по возрасту.
String.compareTo
"Сравнивает две строки лексикографически" - из документов.
Collections.sort
это статический метод в библиотеке native Collections. Он выполняет фактическую сортировку, вам просто нужно предоставить компаратор, который определяет, как следует сравнивать два элемента в вашем списке: это достигается путем предоставления вашей собственной реализации compare
метода.
Ответ 2
Для тех, кто может использовать потоковый API Java 8, существует более аккуратный подход, который хорошо документирован здесь: лямбды и сортировка
Я искал эквивалент C # LINQ:
.ThenBy(...)
Я нашел механизм в Java 8 в компараторе:
.thenComparing(...)
Итак, вот фрагмент, демонстрирующий алгоритм.
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Ознакомьтесь со ссылкой выше, чтобы узнать более аккуратный способ и объяснение того, как вывод типов Java делает его определение немного более неуклюжим по сравнению с LINQ.
Вот полный модульный тест для справки:
@Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));
// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);
// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age);
// Andi : 2
// Barry : 20
// Barry : 50
// Bob : 40
// Bob : 42
// Bob : 72
// Cathy : 40
// Dan : 4
// Debby : 3
}
/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* @param name The name of the person.
* @param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}
/**
* The name of the person.
*/
public String name;
/**
* The age of the person.
*/
public int age;
@Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}
Ответ 3
Используя подход Java 8 Streams, со ссылками на методы в геттерах...
// Create a stream...
var sortedList = persons.stream()
// sort it (does not sort the original list)...
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
// and collect to a new list
.collect(Collectors.toList());
Также возможна сборка в массив:
persons.stream()
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
.toArray(String[]::new);
И подход Java 8 Lambda...
//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
if (p1.getName().compareTo(p2.getName()) == 0) {
return p1.getAge().compareTo(p2.getAge());
} else {
return p1.getName().compareTo(p2.getName());
}
});
Наконец...
// This syntax is similar to the Streams example above, but sorts the original list!!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Ответ 4
Вам нужно реализовать свое собственное Comparator
, а затем использовать его: например
Arrays.sort(persons, new PersonComparator());
Ваш компаратор может выглядеть примерно так:
public class PersonComparator implements Comparator<? extends Person> {
public int compare(Person p1, Person p2) {
int nameCompare = p1.name.compareToIgnoreCase(p2.name);
if (nameCompare != 0) {
return nameCompare;
} else {
return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age));
}
}
}
Компаратор сначала сравнивает имена, если они не равны, он возвращает результат их сравнения, в противном случае он возвращает результат сравнения при сравнении возраста обоих лиц.
Этот код является всего лишь черновиком: поскольку класс неизменяем, вы могли бы подумать о создании из него синглтона, вместо этого создавая новый экземпляр для каждой сортировки.