15 ответов:
можно использовать
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"сравнивает две строки лексикографически" - с docs.
Collections.sortстатический метод в собственной библиотеке коллекций. Он выполняет фактическую сортировку, вам просто нужно предоставить компаратор, который определяет, как должны сравниваться два элемента в вашем списке: это достигается путем обеспечения собственной реализацииcompareметод.
для тех, кто может использовать Java 8 streaming API, есть более аккуратный подход, который хорошо документирован здесь: лямбды и сортировки
Я искал эквивалент 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); } }
использование подхода Java 8 Streams...
//Creates and sorts a stream (does not sort the original list) persons.stream().sorted(Comparator.comparing(Person::getName).thenComparing(Person::getAge));и подход 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 is similar SYNTAX to the Streams above, but it sorts the original list!! persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
вам нужно реализовать свой собственный
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)); } } }компаратор сначала сравнивает имена,если они не равны он возвращает результат от сравнения их, иначе он возвращает результат сравнения при сравнении возрастов обоих лиц.
этот код является только черновиком: поскольку класс является неизменяемым, вы можете подумать о создании его синглтона, вместо этого создается новый экземпляр для каждой сортировки.
пусть ваш класс person реализует
Comparable<Person>а затем реализовать метод compareTo, например:public int compareTo(Person o) { int result = name.compareToIgnoreCase(o.name); if(result==0) { return Integer.valueOf(age).compareTo(o.age); } else { return result; } }это будет сортировать сначала по имени (без учета регистра), а затем по возрасту. Затем вы можете запустить
Arrays.sort()илиCollections.sort()на коллекции или массиве объектов Person.
использовать
Comparatorи затем поместить объекты вCollection, потомCollections.sort();class Person { String fname; String lname; int age; public Person() { } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getFname() { return fname; } public void setFname(String fname) { this.fname = fname; } public String getLname() { return lname; } public void setLname(String lname) { this.lname = lname; } public Person(String fname, String lname, int age) { this.fname = fname; this.lname = lname; this.age = age; } @Override public String toString() { return fname + "," + lname + "," + age; } } public class Main{ public static void main(String[] args) { List<Person> persons = new java.util.ArrayList<Person>(); persons.add(new Person("abc3", "def3", 10)); persons.add(new Person("abc2", "def2", 32)); persons.add(new Person("abc1", "def1", 65)); persons.add(new Person("abc4", "def4", 10)); System.out.println(persons); Collections.sort(persons, new Comparator<Person>() { @Override public int compare(Person t, Person t1) { return t.getAge() - t1.getAge(); } }); System.out.println(persons); } }
гуава это
ComparisonChainобеспечивает чистый способ сделать это. Обратитесь к этому ссылке.утилита для выполнения оператора цепного сравнения. Например:
public int compareTo(Foo that) { return ComparisonChain.start() .compare(this.aString, that.aString) .compare(this.anInt, that.anInt) .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast()) .result(); }
вы можете сделать так:
List<User> users = Lists.newArrayList( new User("Pedro", 12), new User("Maria", 10), new User("Rafael",12) ); users.sort( Comparator.comparing(User::getName).thenComparing(User::getAge) );
или вы можете использовать тот факт, что
Collections.sort()(илиArrays.sort()) является стабильным (он не переупорядочивает элементы, которые равны) и использоватьComparatorдля сортировки по возрасту, а затем еще один для сортировки по имени.В данном конкретном случае это не очень хорошая идея, но если вы должны быть в состоянии изменить порядок сортировки во время выполнения, это может быть полезно.
вы можете использовать универсальный последовательный компаратор для сортировки коллекции по нескольким полям.
import org.apache.commons.lang3.reflect.FieldUtils; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * @author MaheshRPM */ public class SerialComparator<T> implements Comparator<T> { List<String> sortingFields; public SerialComparator(List<String> sortingFields) { this.sortingFields = sortingFields; } public SerialComparator(String... sortingFields) { this.sortingFields = Arrays.asList(sortingFields); } @Override public int compare(T o1, T o2) { int result = 0; try { for (String sortingField : sortingFields) { if (result == 0) { Object value1 = FieldUtils.readField(o1, sortingField, true); Object value2 = FieldUtils.readField(o2, sortingField, true); if (value1 instanceof Comparable && value2 instanceof Comparable) { Comparable comparable1 = (Comparable) value1; Comparable comparable2 = (Comparable) value2; result = comparable1.compareTo(comparable2); } else { throw new RuntimeException("Cannot compare non Comparable fields. " + value1.getClass() .getName() + " must implement Comparable<" + value1.getClass().getName() + ">"); } } else { break; } } } catch (IllegalAccessException e) { throw new RuntimeException(e); } return result; } }
создать столько компараторов, сколько необходимо. После этого вызовите метод "thenComparing" для каждой категории заказа. Это способ делать потоками. Смотрите:
//Sort by first and last name System.out.println("\n2.Sort list of person objects by firstName then " + "by lastName then by age"); Comparator<Person> sortByFirstName = (p, o) -> p.firstName.compareToIgnoreCase(o.firstName); Comparator<Person> sortByLastName = (p, o) -> p.lastName.compareToIgnoreCase(o.lastName); Comparator<Person> sortByAge = (p, o) -> Integer.compare(p.age,o.age); //Sort by first Name then Sort by last name then sort by age personList.stream().sorted( sortByFirstName .thenComparing(sortByLastName) .thenComparing(sortByAge) ).forEach(person-> System.out.println(person));посмотреть: сортировка пользовательского объекта по нескольким полям-компаратор (лямбда-поток)
вы можете использовать подход Java 8 Lambda для достижения этой цели. Вот так:
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Arrays.sort(persons, new PersonComparator()); import java.util.Comparator; public class PersonComparator implements Comparator<? extends Person> { @Override public int compare(Person o1, Person o2) { if(null == o1 || null == o2 || null == o1.getName() || null== o2.getName() ){ throw new NullPointerException(); }else{ int nameComparisonResult = o1.getName().compareTo(o2.getName()); if(0 == nameComparisonResult){ return o1.getAge()-o2.getAge(); }else{ return nameComparisonResult; } } } } class Person{ int age; String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }обновленная версия:
public class PersonComparator implements Comparator<? extends Person> { @Override public int compare(Person o1, Person o2) { int nameComparisonResult = o1.getName().compareToIgnoreCase(o2.getName()); return 0 == nameComparisonResult?o1.getAge()-o2.getAge():nameComparisonResult; } }
класс
Bookтакой:package books; public class Book { private Integer id; private Integer number; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "book{" + "id=" + id + ", number=" + number + ", name='" + name + '\'' + '\n' + '}'; } }сортировка основного класса с макетными объектами
package books; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class Main { public static void main(String[] args) { System.out.println("Hello World!"); Book b = new Book(); Book c = new Book(); Book d = new Book(); Book e = new Book(); Book f = new Book(); Book g = new Book(); Book g1 = new Book(); Book g2 = new Book(); Book g3 = new Book(); Book g4 = new Book(); b.setId(1); b.setNumber(12); b.setName("gk"); c.setId(2); c.setNumber(12); c.setName("gk"); d.setId(2); d.setNumber(13); d.setName("maths"); e.setId(3); e.setNumber(3); e.setName("geometry"); f.setId(3); f.setNumber(34); b.setName("gk"); g.setId(3); g.setNumber(11); g.setName("gk"); g1.setId(3); g1.setNumber(88); g1.setName("gk"); g2.setId(3); g2.setNumber(91); g2.setName("gk"); g3.setId(3); g3.setNumber(101); g3.setName("gk"); g4.setId(3); g4.setNumber(4); g4.setName("gk"); List<Book> allBooks = new ArrayList<Book>(); allBooks.add(b); allBooks.add(c); allBooks.add(d); allBooks.add(e); allBooks.add(f); allBooks.add(g); allBooks.add(g1); allBooks.add(g2); allBooks.add(g3); allBooks.add(g4); System.out.println(allBooks.size()); Collections.sort(allBooks, new Comparator<Book>() { @Override public int compare(Book t, Book t1) { int a = t.getId()- t1.getId(); if(a == 0){ int a1 = t.getNumber() - t1.getNumber(); return a1; } else return a; } }); System.out.println(allBooks); } }
Я не уверен, что в этом случае некрасиво писать компаратор внутри класса Person. Получилось вот так:
public class Person implements Comparable <Person> { private String lastName; private String firstName; private int age; public Person(String firstName, String lastName, int BirthDay) { this.firstName = firstName; this.lastName = lastName; this.age = BirthDay; } public int getAge() { return age; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } @Override public int compareTo(Person o) { // default compareTo } @Override public String toString() { return firstName + " " + lastName + " " + age + ""; } public static class firstNameComperator implements Comparator<Person> { @Override public int compare(Person o1, Person o2) { return o1.firstName.compareTo(o2.firstName); } } public static class lastNameComperator implements Comparator<Person> { @Override public int compare(Person o1, Person o2) { return o1.lastName.compareTo(o2.lastName); } } public static class ageComperator implements Comparator<Person> { @Override public int compare(Person o1, Person o2) { return o1.age - o2.age; } } } public class Test { private static void print() { ArrayList<Person> list = new ArrayList(); list.add(new Person("Diana", "Agron", 31)); list.add(new Person("Kay", "Panabaker", 27)); list.add(new Person("Lucy", "Hale", 28)); list.add(new Person("Ashley", "Benson", 28)); list.add(new Person("Megan", "Park", 31)); list.add(new Person("Lucas", "Till", 27)); list.add(new Person("Nicholas", "Hoult", 28)); list.add(new Person("Aly", "Michalka", 28)); list.add(new Person("Adam", "Brody", 38)); list.add(new Person("Chris", "Pine", 37)); Collections.sort(list, new Person.lastNameComperator()); Iterator<Person> it = list.iterator(); while(it.hasNext()) System.out.println(it.next().toString()); } }
Comments