Вопрос-ответ

How to sort by two fields in Java?

Как выполнить сортировку по двум полям в 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));
}
}
}

Компаратор сначала сравнивает имена, если они не равны, он возвращает результат их сравнения, в противном случае он возвращает результат сравнения при сравнении возраста обоих лиц.

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

java