Comment sortinger par deux champs en Java?

J’ai un tableau d’objects person (int age; Ssortingng name;) .

Comment puis-je sortinger ce tableau par ordre alphabétique, puis par âge?

Quel algorithme utiliseriez-vous pour cela?

Vous pouvez utiliser Collections.sort comme suit:

 private static void order(List persons) { Collections.sort(persons, new Comparator() { public int compare(Object o1, Object o2) { Ssortingng x1 = ((Person) o1).getName(); Ssortingng 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 est maintenant sortingée par nom, puis par âge.

Ssortingng.compareTo “Compare deux chaînes lexicographiquement” – à partir des documents .

Collections.sort est une méthode statique dans la bibliothèque Collections native. Il effectue le sorting proprement dit, il vous suffit de fournir un comparateur qui définit comment deux éléments de votre liste doivent être comparés: ceci est réalisé en fournissant votre propre implémentation de la méthode de compare .

Pour ceux qui peuvent utiliser l’API de streaming Java 8, il existe une approche plus claire qui est bien documentée ici: Lambdas et sorting

Je cherchais l’équivalent du C # LINQ:

 .ThenBy(...) 

J’ai trouvé le mécanisme dans Java 8 sur le comparateur:

 .thenComparing(...) 

Voici donc l’extrait qui illustre l’algorithme.

  Comparator comparator = Comparator.comparing(person -> person.name); comparator = comparator.thenComparing(Comparator.comparing(person -> person.age)); 

Consultez le lien ci-dessus pour une méthode plus simple et une explication sur la façon dont l’inférence de type Java le rend un peu plus compliqué à définir par rapport à LINQ.

Voici le test unitaire complet pour référence:

 @Test public void testChainedSorting() { // Create the collection of people: ArrayList 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 comparator = Comparator.comparing(person -> person.name); comparator = comparator.thenComparing(Comparator.comparing(person -> person.age)); // Sort the stream: Stream personStream = people.stream().sorted(comparator); // Make sure that the output is as expected: List 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(Ssortingng name, int age) { this.age = age; this.name = name; } /** * The name of the person. */ public Ssortingng name; /** * The age of the person. */ public int age; @Override public Ssortingng toSsortingng() { if (name == null) return super.toSsortingng(); else return Ssortingng.format("%s : %d", this.name, this.age); } } 

Utilisation de l’approche Java 8 Streams …

 //Creates and sorts a stream (does not sort the original list) persons.stream().sorted(Comparator.comparing(Person::getName).thenComparing(Person::getAge)); 

Et l’approche 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()); } }); 

Enfin…

 //This is similar SYNTAX to the Streams above, but it sorts the original list!! persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge)); 

Vous devez implémenter votre propre Comparator , puis l’utiliser: par exemple

 Arrays.sort(persons, new PersonComparator()); 

Votre comparateur pourrait ressembler à ceci:

 public class PersonComparator implements Comparator { 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)); } } } 

Le comparateur compare d’abord les noms, s’ils ne sont pas égaux, il retourne le résultat en les comparant, sinon il renvoie le résultat de la comparaison en comparant les âges des deux personnes.

Ce code est seulement un brouillon: comme la classe est immuable, vous pouvez envisager de créer un singleton à la place, créant une nouvelle instance pour chaque sorting.

Demandez à votre classe de mettre en œuvre Comparable , puis implémentez la méthode compareTo, par exemple:

 public int compareTo(Person o) { int result = name.compareToIgnoreCase(o.name); if(result==0) { return Integer.valueOf(age).compareTo(o.age); } else { return result; } } 

Cela va sortinger d’abord par nom (insensiblement à la casse) et ensuite par âge. Vous pouvez ensuite exécuter Arrays.sort() ou Collections.sort() sur la collection ou le tableau d’objects Person.

Utilisez Comparator , puis placez des objects dans Collection , puis Collections.sort();

 class Person { Ssortingng fname; Ssortingng lname; int age; public Person() { } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Ssortingng getFname() { return fname; } public void setFname(Ssortingng fname) { this.fname = fname; } public Ssortingng getLname() { return lname; } public void setLname(Ssortingng lname) { this.lname = lname; } public Person(Ssortingng fname, Ssortingng lname, int age) { this.fname = fname; this.lname = lname; this.age = age; } @Override public Ssortingng toSsortingng() { return fname + "," + lname + "," + age; } } public class Main{ public static void main(Ssortingng[] args) { List persons = new java.util.ArrayList(); 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() { @Override public int compare(Person t, Person t1) { return t.getAge() - t1.getAge(); } }); System.out.println(persons); } } 

Guava’s ComparisonChain fournit une méthode propre pour le faire. Reportez-vous à ce lien .

Un utilitaire pour effectuer une instruction de comparaison en chaîne. Par exemple:

  public int compareTo(Foo that) { return ComparisonChain.start() .compare(this.aSsortingng, that.aSsortingng) .compare(this.anInt, that.anInt) .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast()) .result(); } 

Vous pouvez faire comme ça:

 List users = Lists.newArrayList( new User("Pedro", 12), new User("Maria", 10), new User("Rafael",12) ); users.sort( Comparator.comparing(User::getName).thenComparing(User::getAge) ); 

Ou vous pouvez exploiter le fait que Collections.sort() (ou Arrays.sort() ) est stable (il ne réorganise pas les éléments qui sont égaux) et utilisez un Comparator pour sortinger d’abord par âge, puis un autre pour sortinger par nom .

Dans ce cas précis, ce n’est pas une très bonne idée, mais si vous devez pouvoir modifier l’ordre de sorting à l’exécution, cela peut être utile.

Vous pouvez utiliser un comparateur série générique pour sortinger les collections selon plusieurs champs.

 import org.apache.commons.lang3.reflect.FieldUtils; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * @author MaheshRPM */ public class SerialComparator implements Comparator { List sortingFields; public SerialComparator(List sortingFields) { this.sortingFields = sortingFields; } public SerialComparator(Ssortingng... sortingFields) { this.sortingFields = Arrays.asList(sortingFields); } @Override public int compare(T o1, T o2) { int result = 0; try { for (Ssortingng 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; } } 

Créez autant de comparateurs que nécessaire. Ensuite, appelez la méthode “thenComparing” pour chaque catégorie de commande. C’est une façon de faire par Streams. Voir:

 //Sort by first and last name System.out.println("\n2.Sort list of person objects by firstName then " + "by lastName then by age"); Comparator sortByFirstName = (p, o) -> p.firstName.compareToIgnoreCase(o.firstName); Comparator sortByLastName = (p, o) -> p.lastName.compareToIgnoreCase(o.lastName); Comparator 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)); 

Look: Trier l’object défini par l’utilisateur sur plusieurs champs – Comparator (stream lambda)

Vous pouvez utiliser l’approche Java 8 Lambda pour y parvenir. Comme ça:

 persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge)); 
 Arrays.sort(persons, new PersonComparator()); import java.util.Comparator; public class PersonComparator implements Comparator { @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; Ssortingng name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } } 

Version mise à jour:

 public class PersonComparator implements Comparator { @Override public int compare(Person o1, Person o2) { int nameComparisonResult = o1.getName().compareToIgnoreCase(o2.getName()); return 0 == nameComparisonResult?o1.getAge()-o2.getAge():nameComparisonResult; } } 

Pour un Book classe comme celui-ci:

 package books; public class Book { private Integer id; private Integer number; private Ssortingng 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 Ssortingng getName() { return name; } public void setName(Ssortingng name) { this.name = name; } @Override public Ssortingng toSsortingng() { return "book{" + "id=" + id + ", number=" + number + ", name='" + name + '\'' + '\n' + '}'; } } 

sorting de la classe principale avec des objects fictifs

 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(Ssortingng[] 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 allBooks = new ArrayList(); 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() { @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); } } 

Je ne suis pas sûr que ce soit laid d’écrire le compartiment à l’intérieur de la classe Person dans ce cas. A-t-il aimé ça:

 public class Person implements Comparable  { private Ssortingng lastName; private Ssortingng firstName; private int age; public Person(Ssortingng firstName, Ssortingng lastName, int BirthDay) { this.firstName = firstName; this.lastName = lastName; this.age = BirthDay; } public int getAge() { return age; } public Ssortingng getFirstName() { return firstName; } public Ssortingng getLastName() { return lastName; } @Override public int compareTo(Person o) { // default compareTo } @Override public Ssortingng toSsortingng() { return firstName + " " + lastName + " " + age + ""; } public static class firstNameComperator implements Comparator { @Override public int compare(Person o1, Person o2) { return o1.firstName.compareTo(o2.firstName); } } public static class lastNameComperator implements Comparator { @Override public int compare(Person o1, Person o2) { return o1.lastName.compareTo(o2.lastName); } } public static class ageComperator implements Comparator { @Override public int compare(Person o1, Person o2) { return o1.age - o2.age; } } } public class Test { private static void print() { ArrayList 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 it = list.iterator(); while(it.hasNext()) System.out.println(it.next().toSsortingng()); } }