如何在Java中的两个字段进行sorting?
我有一些对象person ( int age; String name;)
。
如何sorting这个数组人按字母sorting,然后按年龄?
哪个algorithm是为了这个?
你可以使用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; } else { Integer x1 = ((Person) o1).getAge(); Integer x2 = ((Person) o2).getAge(); return x1.compareTo(x2); } }}); }
List<Persons>
现在按名称sorting,然后按年龄sorting。
String.compareTo
“按字典顺序比较两个string” – 来自文档 。
Collections.sort
是本地Collections库中的静态方法。 它进行实际的sorting,你只需要提供一个比较器来定义列表中的两个元素应该如何比较:这是通过提供你自己的compare
方法的实现来实现的。
对于那些能够使用Java 8 streaming API的人来说,这里有一个很好的方法,这里有很好的文档: Lambda和sorting
我正在寻找相当于C#的LINQ:
.ThenBy(...)
我在比较器中findJava 8中的机制:
.thenComparing(...)
所以这里是演示algorithm的片段。
Comparator<Person> comparator = Comparator.comparing(person -> person.name); comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
查看上面的链接以获得更好的方式,并解释了Java的types推断如何使它与LINQ相比更加笨拙。
以下是完整的unit testing供参考:
@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)); } } }
比较器首先比较名称,如果不相等则返回比较结果,否则比较两个人的年龄。
这个代码只是一个草稿:因为这个类是不可变的,你可以考虑构build一个单例,而不是为每个sorting创build一个新的实例。
让您的人员类实现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; } }
这将首先按名称sorting(不区分大小写),然后按年龄sorting。 然后,您可以在Person对象的集合或数组上运行Arrays.sort()
或Collections.sort()
。
或者,您可以利用Collections.sort()
(或Arrays.sort()
)是稳定的(它不会重新排列相同的元素),并使用Comparator
按年龄先sorting,然后按名称sorting。
在这个特定的情况下,这不是一个好主意,但是如果你必须能够在运行时改变sorting顺序的话,这可能是有用的。
使用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); } }
您可以使用通用串行比较器按多个字段sorting集合。
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; } }
你可以这样做:
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) );
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' + '}'; } }
用模拟对象sorting主类
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()); } }
根据需要创build更多的比较器。 之后,为每个订单类别调用方法“thenComparing”。 这是Streams做的一种方式。 看到:
//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));
看: 在多个字段上sorting用户定义的对象 – 比较器(lambdastream)
番石榴的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(); }