Java-List集合排序之Comparable与Comparator的使用


Collections类说明

Collections是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。其中就有用于集合排序的sort方法,此方法主要是通过ComparableComparator来实现排序。以下介绍两者之间的使用以及区别。

Comparable用法

注意:在使用Collections的sort(List<T> list) 方法排序时,要求集合元素(对象)T必须是Comparable接口的实现类,同时重写Comparable的抽象方法 - int compareTo(T t)。方法说明如下:

 * 在使用Collection的sort排序的集合元素都必须是Comparable接口的实现类,该接口表示子类是可以比较的。 * 同时实现接口必须重写抽象方法。 *  - int compareTo(T t); * 该方法用于使当前对象与给定对象进行比较。 * 返回值为一个int值,该值表示大小关系,它不关注具体的取值是多少,而关注的是取值范围。 *  当返回值>0时:当前对象比参数对象大(T是传入的参数,this表示当前对象) *  当返回值<0时:当前对象比参数对象小(T是传入的参数,this表示当前对象) *  当返回值=0时:当前对象等于参数对象(T是传入的参数,this表示当前对象)

代码案例一(Java基本包装类型排序)

Integer、String等一些基本包装类,JDK默认都实现了Comparable接口,所以可以使用方法一直接进行比较排序(自然排序)

package example.demo03; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * @author yuanchaoyong */
public class ComparatorTest { public static void main(String[] args) { // 测试案例1
 testInteger(); // 测试案例2
 testString(); } /** * List<Integer> 排序测试 * Integer 默认实现了Comparable接口 * Collections.sort默认是升序排序(自然排序) */
    public static  void testInteger(){ List<Integer> integers = Arrays.asList(12,7,15,4); Collections.sort(integers); System.out.println(integers); } /** * List<String> 排序测试 * String 默认实现了Comparable接口 * Collections.sort默认是升序排序(自然排序) */
    public static  void testString(){ List<String> strings = Arrays.asList("aaa","ccc","bbb","rrr"); Collections.sort(strings); System.out.println(strings); } }

输出结果

 

代码案例二 (自定义对象排序)

平常工作开发的时候,基本包装类型一般无法满足需求。所以我们定义了自定义对象,有时候需要根据对象的某一个属性进行排序。这个时候需要对象实现Comparable接口,同时重写Comparable的抽象方法 - int compareTo(T t)。这里以用户(User)年龄进行升序排序案例说明。

package example.demo03; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * @author yuanchaoyong */
public class ComparatorTest02 { public static void main(String[] args) { List<User> users = Arrays.asList(new User("王五",50),new User("张三",30),new User("李四",40)); Collections.sort(users); System.out.println(users); } } /** * 用户对象 * name * age */
class User   implements Comparable<User>{ String name; Integer age; User(String n, Integer a) { name = n; age = a; } @Override public int compareTo(User u) { return this.age - u.age ; } @Override public String toString() { return String.format("{name=%s, age=%d}", name, age); } }

 

Comparable优缺点

通过以上案例知道,Comparable的确实现了我们想要的排序结果。但是使用Collections的sort(List<T> list) 方法排序集合时,集合元素必须实现Comparable接口并且定义比较规则。这个时候就开始出现一些问题。比如我们同一个工程使用了User对象,单个场景使用age进行排序,但是另一种情况下使用name排序,这个时候就出现了冲突。并且集合元素需要去实现Comparable接口,这对于我们的代码会有一些"侵入性",也不利于我们后续的代码扩展。

所以一般不建议使用,推荐使用Collections的sort(List<T> list, Comparator<? super T> c)的重载方法。

Comparator用法

Collections的sort(List<T> list, Comparator<? super T> c)

 * 重载的sort方法要求传入一个外部的比较器。 * 该方法不再要求集合元素必须实现Comparable接口。 * 并且也不再使用集合元素自身的比较规则排序了,而是根据给定的这个额外的比较器的比较规则,对集合元素进行排序。 * 实际开发中也推荐使用这种方式排序集合元素,若集合元素是自定义的,创建比较器时也推荐使用匿名内部类的形式。

代码案例一 (自定义对象排序)

package example.demo03; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * @author yuanchaoyong */
public class ComparatorTest03 { public static void main(String[] args) { List<Student> students = Arrays.asList(new Student("张三",30),new Student("李四",40),new Student("王五",50)); // 使用匿名内部类进行排序
        Collections.sort(students, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // 自定义排序规则
                return o1.age- o2.age; } }); System.out.println(students); } } class Student { String name; Integer age; Student(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return String.format("{name=%s, age=%d}", name, age); } }

结果

 

代码案例二 (使用List自带的排序方法)

package example.demo03; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * @author yuanchaoyong */
public class ComparatorTest03 { public static void main(String[] args) { //test01();
 test02(); } /** * 使用List自带的排序方法 */
    private static void test02() { List<Student> students = Arrays.asList(new Student("张三",30),new Student("李四",40),new Student("王五",50)); students.sort(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.age- o2.age; } }); System.out.println(students); } /** * Collections.sort方法测试 */
    private static void test01() { List<Student> students = Arrays.asList(new Student("张三",30),new Student("李四",40),new Student("王五",50)); // 使用匿名内部类进行排序
        Collections.sort(students, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // 自定义排序规则
                return o1.age- o2.age; } }); System.out.println(students); } } class Student { String name; Integer age; Student(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return String.format("{name=%s, age=%d}", name, age); } }

 

JDK8之lambda简化写法

Comparator接口是一个函数式接口,所以我们可以使用更优秀的写法,简化代码。

函数式接口就是只定义一个抽象方法的接口。
JDK1.8开始支持默认方法,即便这个接口拥有很多默认方法,只要接口只有一个抽象方法,那么这个接口就是函数式接口。
package example.demo03; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; /** * @author yuanchaoyong */
public class ComparatorTest04 { public static void main(String[] args) { test02(); } /** * lambda写法测试 */
    private static void test02() { List<Teacher> teachers = Arrays.asList(new Teacher("张三", 30), new Teacher("李四", 40), new Teacher("王五", 50)); // 多种写法,以下只是作为参考 // 写法1 // teachers.stream().sorted((o1,o2)-> o1.age- o2.age).collect(Collectors.toList()); // 写法2 //Collections.sort(teachers,(o1,o2)-> o1.getAge()- o2.getAge()); // 写法3
        List<Teacher> teacherList = teachers.stream(). sorted((Comparator.comparingInt(Teacher::getAge))) .collect(Collectors.toList()); System.out.println(teachers); } } class Teacher { String name; Integer age; Teacher(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public Integer getAge() { return age; } @Override public String toString() { return String.format("{name=%s, age=%d}", name, age); } }

额外参考资源

List<类> list; 代表某集合 //返回 对象集合以类属性一升序排序
list.stream().sorted(Comparator.comparing(类::属性一)); //返回 对象集合以类属性一降序排序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序 //返回 对象集合以类属性一升序 属性二升序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二)); //返回 对象集合以类属性一降序 属性二升序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序 //返回 对象集合以类属性一降序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序 //返回 对象集合以类属性一升序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序 //空/Null数据排序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(item -> item.属性二, Comparator.nullsLast(Date::compareTo))).collect(Collectors.toList()); //空/Null数据分组
Map<String, List<类>> map = list.stream().collect(Collectors.groupingBy(item -> { if (item.属性一 == null || item.属性一.equals("")) {   return ""; } return DateFormat.getDateInstance().format(item.属性一); }))

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM