关键词搜索

源码搜索 ×
×

Java中使用Comparable和Comparator实现字段排序功能

发布2016-12-23浏览2698次

详情内容

Comparable 简介

Comparable 是排序接口。

若一个类实现了Comparable接口,就意味着“该类支持排序”。  即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。

此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。

Comparable 定义

Comparable 接口仅仅只包括一个函数,它的定义如下:

  1. package java.lang;
  2. import java.util.*;
  3. public interface Comparable<T> {
  4. public int compareTo(T o);
  5. }

说明:
假设我们通过 x.compareTo(y) 来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。

 

 


Comparator 简介

Comparator 是比较器接口。

我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。

 

Comparator 定义

Comparator 接口仅仅只包括两个函数,它的定义如下:

  1. package java.util;
  2. public interface Comparator<T> {
  3. int compare(T o1, T o2);
  4. boolean equals(Object obj);
  5. }

说明:
(01) 若一个类要实现Comparator接口:它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。

        为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。

(02) int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。



Comparator 和 Comparable 比较

Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。

而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

 


Comparator 和 Comparable 实例讲解

首先我们定义一个实现Comparable的接口的实体类,Person:

  1. package com.boonya.program.compare;
  2. /**
  3. * 可比较排序Person
  4. *
  5. * @package com.boonya.program.compare.ComparablePerson
  6. * @date 2016年12月23日 下午10:36:10
  7. * @author pengjunlin
  8. * @comment
  9. * @update
  10. */
  11. public class Person implements Comparable<Person> {
  12. private long id;
  13. private String name;
  14. private int age;
  15. private String email;
  16. private String address;
  17. public Person(long id, String name, int age, String email,
  18. String address) {
  19. super();
  20. this.id = id;
  21. this.name = name;
  22. this.age = age;
  23. this.email = email;
  24. this.address = address;
  25. }
  26. public Person() {
  27. super();
  28. }
  29. public long getId() {
  30. return id;
  31. }
  32. public void setId(long id) {
  33. this.id = id;
  34. }
  35. public String getName() {
  36. return name;
  37. }
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41. public int getAge() {
  42. return age;
  43. }
  44. public void setAge(int age) {
  45. this.age = age;
  46. }
  47. public String getEmail() {
  48. return email;
  49. }
  50. public void setEmail(String email) {
  51. this.email = email;
  52. }
  53. public String getAddress() {
  54. return address;
  55. }
  56. public void setAddress(String address) {
  57. this.address = address;
  58. }
  59. @Override
  60. public int compareTo(Person o) {
  61. if (this == o) {
  62. return 0;
  63. } else if (o != null && o instanceof Person) {
  64. Person u = (Person) o;
  65. if (id < u.id) {
  66. return -1;
  67. }else if(id == u.id){
  68. return 0;
  69. } else {
  70. return 1;
  71. }
  72. } else {
  73. return -1;
  74. }
  75. }
  76. @Override
  77. public String toString() {
  78. return "Person [id=" + id + ", name=" + name + ", age=" + age
  79. + ", email=" + email + ", address=" + address + "]";
  80. }
  81. }
然后定义一个排序枚举类,SortEnum:

  1. package com.boonya.program.compare;
  2. /**
  3. * 排序类型枚举
  4. *
  5. * @package com.boonya.program.compare.SortEnum
  6. * @date 2016年12月23日 上午10:58:44
  7. * @author pengjunlin
  8. * @comment
  9. * @update
  10. */
  11. public enum SortEnum {
  12. /**
  13. * 升序
  14. */
  15. ASC(0),
  16. /**
  17. * 降序
  18. */
  19. DESC(1);
  20. private int value=0;
  21. public int getValue() {
  22. return value;
  23. }
  24. public void setValue(int value) {
  25. this.value = value;
  26. }
  27. private SortEnum(int value) {
  28. this.value = value;
  29. }
  30. }

定义Person的比较器,PersonComparator:

  1. package com.boonya.program.compare;
  2. import java.util.Comparator;
  3. /**
  4. * 比较器PersonComparator
  5. *
  6. * @package com.boonya.program.compare.PersonComparator
  7. * @date 2016年12月23日 上午10:56:53
  8. * @author pengjunlin
  9. * @comment
  10. * @update
  11. */
  12. public class PersonComparator implements Comparator<Person> {
  13. private String sortFieldName;
  14. private SortEnum sortEnum;
  15. public String getSortFieldName() {
  16. return sortFieldName;
  17. }
  18. public void setSortFieldName(String sortFieldName) {
  19. this.sortFieldName = sortFieldName;
  20. }
  21. public SortEnum getSortEnum() {
  22. return sortEnum;
  23. }
  24. public void setSortEnum(SortEnum sortEnum) {
  25. this.sortEnum = sortEnum;
  26. }
  27. public PersonComparator() {
  28. super();
  29. }
  30. public PersonComparator(SortEnum sortEnum) {
  31. super();
  32. this.sortEnum = sortEnum;
  33. }
  34. public PersonComparator(String sortFieldName, SortEnum sortEnum) {
  35. super();
  36. this.sortFieldName = sortFieldName;
  37. this.sortEnum = sortEnum;
  38. }
  39. @Override
  40. public int compare(Person o1, Person o2) {
  41. if (sortFieldName == null) {
  42. if (sortEnum == SortEnum.DESC) {
  43. return o2.compareTo(o1);
  44. } else {
  45. return o1.compareTo(o2);
  46. }
  47. } else {
  48. if (sortEnum == SortEnum.DESC) {
  49. if (sortFieldName.equals("id")) {
  50. long val = o2.getId() - o1.getId();
  51. if (val < 0)
  52. return 1;
  53. else if (val > 0)
  54. return -1;
  55. else
  56. return 0;
  57. } else if (sortFieldName.equals("name")) {
  58. return o2.getName().compareTo(o1.getName());
  59. } else if (sortFieldName.equals("age")) {
  60. int val = o2.getAge() - o1.getAge();
  61. if (val < 0)
  62. return 1;
  63. else if (val > 0)
  64. return -1;
  65. else
  66. return 0;
  67. } else if (sortFieldName.equals("email")) {
  68. return o2.getEmail().compareTo(o1.getEmail());
  69. } else if (sortFieldName.equals("address")) {
  70. return o2.getAddress().compareTo(o1.getAddress());
  71. }
  72. return o2.compareTo(o1);
  73. } else {
  74. if (sortFieldName.equals("id")) {
  75. long val = o1.getId() - o2.getId();
  76. if (val > 0)
  77. return 1;
  78. else if (val < 0)
  79. return -1;
  80. else
  81. return 0;
  82. } else if (sortFieldName.equals("name")) {
  83. return o1.getName().compareTo(o2.getName());
  84. } else if (sortFieldName.equals("age")) {
  85. int val = o1.getAge() - o2.getAge();
  86. if (val > 0)
  87. return 1;
  88. else if (val < 0)
  89. return -1;
  90. else
  91. return 0;
  92. } else if (sortFieldName.equals("email")) {
  93. return o1.getEmail().compareTo(o2.getEmail());
  94. } else if (sortFieldName.equals("address")) {
  95. return o1.getAddress().compareTo(o2.getAddress());
  96. }
  97. return o1.compareTo(o2);
  98. }
  99. }
  100. }
  101. }

最后测试一下我们的排序,PersonComparatorTest:

  1. package com.boonya.program.compare;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import org.junit.Test;
  5. /**
  6. * 比较器测试
  7. *
  8. * @package com.boonya.program.compare.PersonComparatorTest
  9. * @date 2016年12月23日 上午10:58:06
  10. * @author pengjunlin
  11. * @comment
  12. * @update
  13. */
  14. public class PersonComparatorTest {
  15. @Test
  16. public void sortAscTest() {
  17. ArrayList<Person> persons = new ArrayList<Person>();
  18. Person p1 = new Person(1, "Allen", 12, "Allen@support.com", "New York");
  19. Person p2 = new Person(5, "Bob", 9, "Bob@support.com", "Atlantic");
  20. Person p3 = new Person(2, "Author", 20, "Author@support.com", "Washington");
  21. Person p4 = new Person(3, "Peter", 30, "Peter@support.com", "Los Angilis");
  22. Person p5 = new Person(4, "Mercy", 21, "Mercy@support.com", "San Fransisco");
  23. persons.add(p1);
  24. persons.add(p2);
  25. persons.add(p3);
  26. persons.add(p4);
  27. persons.add(p5);
  28. System.out.println(">>>>========sort by id asc===========<<<<");
  29. PersonComparator comparator=new PersonComparator(SortEnum.ASC);
  30. Collections.sort(persons, comparator);// 用我们写好的Comparator对persons进行排序
  31. for (int i = 0; i < persons.size(); i++) {
  32. System.out.println(persons.get(i).toString());
  33. }
  34. }
  35. @Test
  36. public void sortDescTest() {
  37. ArrayList<Person> persons = new ArrayList<Person>();
  38. Person p1 = new Person(1, "Allen", 12, "Allen@support.com", "New York");
  39. Person p2 = new Person(5, "Bob", 9, "Bob@support.com", "Atlantic");
  40. Person p3 = new Person(2, "Author", 20, "Author@support.com", "Washington");
  41. Person p4 = new Person(3, "Peter", 30, "Peter@support.com", "Los Angilis");
  42. Person p5 = new Person(4, "Mercy", 21, "Mercy@support.com", "San Fransisco");
  43. persons.add(p1);
  44. persons.add(p2);
  45. persons.add(p3);
  46. persons.add(p4);
  47. persons.add(p5);
  48. System.out.println(">>>>========sort by id desc===========<<<<");
  49. PersonComparator comparator=new PersonComparator(SortEnum.DESC);
  50. Collections.sort(persons, comparator);// 用我们写好的Comparator对persons进行排序
  51. for (int i = 0; i < persons.size(); i++) {
  52. System.out.println(persons.get(i).toString());
  53. }
  54. }
  55. @Test
  56. public void sortByFieldAscTest() {
  57. ArrayList<Person> persons = new ArrayList<Person>();
  58. Person p1 = new Person(1, "Allen", 12, "Allen@support.com", "New York");
  59. Person p2 = new Person(5, "Bob", 9, "Bob@support.com", "Atlantic");
  60. Person p3 = new Person(2, "Author", 20, "Author@support.com", "Washington");
  61. Person p4 = new Person(3, "Peter", 30, "Peter@support.com", "Los Angilis");
  62. Person p5 = new Person(4, "Mercy", 21, "Mercy@support.com", "San Fransisco");
  63. persons.add(p1);
  64. persons.add(p2);
  65. persons.add(p3);
  66. persons.add(p4);
  67. persons.add(p5);
  68. System.out.println(">>>>========sort by name asc===========<<<<");
  69. PersonComparator comparator=new PersonComparator("name",SortEnum.ASC);
  70. Collections.sort(persons, comparator);// 用我们写好的Comparator对persons进行排序
  71. for (int i = 0; i < persons.size(); i++) {
  72. System.out.println(persons.get(i).toString());
  73. }
  74. System.out.println(">>>>========sort by age asc===========<<<<");
  75. PersonComparator comparator2=new PersonComparator("age",SortEnum.ASC);
  76. Collections.sort(persons, comparator2);// 用我们写好的Comparator对persons进行排序
  77. for (int i = 0; i < persons.size(); i++) {
  78. System.out.println(persons.get(i).toString());
  79. }
  80. System.out.println(">>>>========sort by email asc===========<<<<");
  81. PersonComparator comparator3=new PersonComparator("email",SortEnum.ASC);
  82. Collections.sort(persons, comparator3);// 用我们写好的Comparator对persons进行排序
  83. for (int i = 0; i < persons.size(); i++) {
  84. System.out.println(persons.get(i).toString());
  85. }
  86. System.out.println(">>>>========sort by address asc===========<<<<");
  87. PersonComparator comparator4=new PersonComparator("address",SortEnum.ASC);
  88. Collections.sort(persons, comparator4);// 用我们写好的Comparator对persons进行排序
  89. for (int i = 0; i < persons.size(); i++) {
  90. System.out.println(persons.get(i).toString());
  91. }
  92. }
  93. }

输出结果:

  1. >>>>========sort by id asc===========<<<<
  2. Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
  3. Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
  4. Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
  5. Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
  6. Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
  7. >>>>========sort by id desc===========<<<<
  8. Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
  9. Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
  10. Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
  11. Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
  12. Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
  13. >>>>========sort by name asc===========<<<<
  14. Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
  15. Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
  16. Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
  17. Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
  18. Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
  19. >>>>========sort by age asc===========<<<<
  20. Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
  21. Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
  22. Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
  23. Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
  24. Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
  25. >>>>========sort by email asc===========<<<<
  26. Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
  27. Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]
  28. Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
  29. Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
  30. Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
  31. >>>>========sort by address asc===========<<<<
  32. Person [id=5, name=Bob, age=9, email=Bob@support.com, address=Atlantic]
  33. Person [id=3, name=Peter, age=30, email=Peter@support.com, address=Los Angilis]
  34. Person [id=1, name=Allen, age=12, email=Allen@support.com, address=New York]
  35. Person [id=4, name=Mercy, age=21, email=Mercy@support.com, address=San Fransisco]
  36. Person [id=2, name=Author, age=20, email=Author@support.com, address=Washington]

使用反射的方式实现List集合对象任意字段排序

参考:https://github.com/Tony36051/sortByField/blob/master/src/main/java/SortByField.java

  1. package main.java;
  2. import java.lang.invoke.MethodHandle;
  3. import java.lang.invoke.MethodHandles;
  4. import java.lang.invoke.MethodType;
  5. import java.lang.reflect.Field;
  6. import java.lang.reflect.InvocationTargetException;
  7. import java.lang.reflect.Method;
  8. import java.util.Collections;
  9. import java.util.Comparator;
  10. import java.util.List;
  11. public class SortByField {
  12. static class Cmp<E> implements Comparator<E> {
  13. Method getMethod = null;
  14. Field fieldToGet = null;
  15. MethodHandle cmpMethodHandle;
  16. // Method cmpMethod ;
  17. Cmp(MethodHandle cmpMethodHandle, /* Method cmpMethod, */Method getMethod) {
  18. this.getMethod = getMethod;
  19. this.cmpMethodHandle = cmpMethodHandle;
  20. // this.cmpMethod = cmpMethod;
  21. }
  22. Cmp(MethodHandle cmpMethodHandle, Field fieldToGet) {
  23. this.cmpMethodHandle = cmpMethodHandle;
  24. this.fieldToGet = fieldToGet;
  25. }
  26. @Override
  27. public int compare(E o1, E o2) {
  28. if (o2 == null) {
  29. return -1;
  30. } else if (o1 == null) {
  31. return 1;
  32. }
  33. try {
  34. if (getMethod != null) {
  35. return (int) cmpMethodHandle.invokeExact((Comparable<?>) getMethod.invoke(o1),
  36. getMethod.invoke(o2));
  37. // return (int)cmpMethod.invoke(getMethod.invoke(o1),
  38. }
  39. if (fieldToGet != null) {
  40. return (int) cmpMethodHandle.invokeExact((Comparable<?>) fieldToGet.get(o1), fieldToGet.get(o2));
  41. }
  42. } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  43. System.out.println("If sortByField() isn't modifie, it won't print errorStackTrace. Default return 0");
  44. e.printStackTrace();
  45. } catch (Throwable e) {
  46. e.printStackTrace();
  47. }
  48. return 0;
  49. }
  50. }
  51. /**
  52. * sort a list containing JavaBean according specific key( field ).
  53. * Mostly, sortByField take ~1.5 times as much as Traditional implementation when list.size() > 100K
  54. *
  55. * @param list:
  56. * list to be sorted
  57. * @param fieldName:
  58. * sort list according this field
  59. * @param order:
  60. * asc(default) or desc
  61. * @author Tony
  62. * @email 360517703@163.com
  63. * @Time 2015-08-14 11:12
  64. */
  65. public static <E> void sortByField(List<E> list, String fieldName, String order) {
  66. if (list == null || list.size() < 2) { // no need to sort
  67. return;
  68. }
  69. if (fieldName == null || fieldName.trim().equals(""))
  70. return; // won't sort if fieldName is null or ""
  71. // get actual class of generic E
  72. Class<?> eClazz = null; // use reflect to get the actual class
  73. boolean isAllNull = true; // default all elements are null
  74. for (E e : list) {
  75. if (e != null) {
  76. isAllNull = false;
  77. eClazz = e.getClass();
  78. break;
  79. }
  80. }
  81. if (isAllNull)
  82. return; // no need to sort, because all elements are null
  83. // check fieldName in Class E
  84. Field keyField = null; // the <fieldName> Field as sort key
  85. try {
  86. keyField = eClazz.getDeclaredField(fieldName);
  87. } catch (NoSuchFieldException e1) {
  88. e1.printStackTrace();
  89. System.out.println("The List<E> doesn't contain fieldName. That is "
  90. + String.format("%s has no Field %s.", eClazz, fieldName));
  91. } catch (SecurityException e1) {
  92. e1.printStackTrace();
  93. System.out.println("deny access to class or field.");
  94. }
  95. // check field is either Comparable
  96. Class<?> fieldClazz = keyField.getType();
  97. boolean isComparable = Comparable.class.isAssignableFrom(fieldClazz);
  98. if (isComparable == false)
  99. return; // if the class of fieldName is not comparable, don't sort
  100. // try to use getter method to get field first. Because a little faster
  101. // than Field.get(Object)
  102. StringBuilder getterName; // adapt to JavaBean getter method
  103. if (fieldClazz.getSimpleName().equals("Boolean")) {
  104. getterName = new StringBuilder("is");
  105. } else {
  106. getterName = new StringBuilder("get");
  107. }
  108. char[] cs = fieldName.toCharArray();
  109. if (cs[0] >= 'a' && cs[0] <= 'z')
  110. cs[0] -= 32; // change the first char to lowerCase
  111. getterName.append(cs);
  112. Method getterMethod = null;
  113. try {
  114. getterMethod = eClazz.getDeclaredMethod(getterName.toString());
  115. } catch (NoSuchMethodException | SecurityException e1) {
  116. // System.out.println("Field " + fieldName + " has no " + getterName
  117. // + "() . ");
  118. // e1.printStackTrace();
  119. }
  120. /*
  121. * // get compare method for specified field. //Abandoned. Because
  122. * MethodHandle.invokeExact() is a little faster than Method.invoke()
  123. * Method cmpMethod = null; try { cmpMethod =
  124. * fieldClazz.getDeclaredMethod("compareTo", fieldClazz); } catch
  125. * (NoSuchMethodException | SecurityException e1) { System.out.println(
  126. * "deny access to class or method(comparaTo).\nImpossible to show errorStrackTrace Because of Comparable check"
  127. * ); e1.printStackTrace(); cmpMethod.setAccessible(true); }
  128. */
  129. Cmp<E> cmp;
  130. MethodHandles.Lookup lookup = MethodHandles.lookup();
  131. MethodType type = MethodType.methodType(int.class, Object.class);
  132. MethodHandle mh = null;
  133. try {
  134. mh = lookup.findVirtual(Comparable.class, "compareTo", type);
  135. } catch (NoSuchMethodException | IllegalAccessException e1) {
  136. // TODO Auto-generated catch block
  137. e1.printStackTrace();
  138. }
  139. if (getterMethod != null) {
  140. // cmpMethod.setAccessible(true);
  141. getterMethod.setAccessible(true);
  142. cmp = new Cmp<E>(mh, getterMethod);
  143. } else { // if cannot find getter method, use reflect to get specified
  144. // field
  145. keyField.setAccessible(true);
  146. cmp = new Cmp<E>(mh, keyField);
  147. }
  148. if (order.equalsIgnoreCase("desc")) {
  149. Collections.sort(list, Collections.reverseOrder(cmp));
  150. return;
  151. }
  152. Collections.sort(list, cmp);
  153. }
  154. /**
  155. * sort a list containing JavaBean according specific key( field ) order by
  156. * ascend.
  157. *
  158. * Mostly, sortByField take ~1.5 times as much as Traditional implementation when list.size() > 100K
  159. * @param list
  160. * list to be sort
  161. * @param fieldName
  162. * sort list according this field
  163. * @author Tony
  164. * @email 360517703@163.com
  165. * @Time 2015-08-14 11:12
  166. */
  167. public static <E> void sortByField(List<E> list, String fieldName) {
  168. sortByField(list, fieldName, "asc");
  169. }
  170. }



相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载