最近项目中有排序的需求,就查看了一下Java文档,发现有两个接口都可以进行排序,Comparable 和 Comparator 两接口到底有啥区别?何时用?怎么用?使用场景我都在底下一一研究分享出来:

一、Comparable 比较器

(1)Comparable 是接口,可以认为是一个内比较器,实现了Comparable 接口的类有一个特点,就是这些类可以和自己进行比较,比较逻辑依赖于 comparaTo() 方法。如果借用Collections.sort() 方法来进行排序,那么这个类必须实现 Comparable 接口并实现 compareTo() 方法,java的很多类都实现了Comparable接口,比如 String、Integer 等类

  1. public interface Comparable<T> {
  2. public int compareTo(T o);
  3. }

  调用此方法,也就是同一个List中的同类型元素进行比较,即this和o比较;若返回值大于0则this > o,返回值等于0则是this = o,返回值小于0则是this < o;

(2)实例代码:

  1. public class UserComparable implements Comparable<UserComparable>{
  2.  
  3. private static Logger logger = LoggerFactory.getLogger(UserComparable.class);
  4.  
  5. private String name;
  6.  
  7. private Integer age;
  8.  
  9. public UserComparable(String name, Integer age) {
  10. this.name = name;
  11. this.age = age;
  12. }
  13.  
  14. public String getName() {
  15. return name;
  16. }
  17.  
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21.  
  22. public Integer getAge() {
  23. return age;
  24. }
  25.  
  26. public void setAge(Integer age) {
  27. this.age = age;
  28. }
  29.  
  30. @Override
  31. public int compareTo(UserComparable user) {
  32. if (this.age.compareTo(user.getAge()) > 0) {
  33. return 1;
  34. } else if (this.age.compareTo(user.getAge()) == 0) {
  35. return 0;
  36. } else {
  37. return -1;
  38. }
  39. }
  40.  
  41. @Override
  42. public String toString() {
  43. return "name = " + this.getName() + ", age = " + this.getAge();
  44. }
  45.  
  46. public static void main(String[] args) {
  47. List<UserComparable> userList = Lists.newArrayList();
  48. userList.add(new UserComparable("xiaoxiao", 22));
  49. userList.add(new UserComparable("honghong", 19));
  50. userList.add(new UserComparable("mingming", 29));
  51. userList.add(new UserComparable("shuishui", 26));
  52. userList.add(new UserComparable("yangyang", 34));
  53. //排序前
  54. logger.info("排序前");
  55. userList.stream().forEach(user -> System.out.println(user.toString()));
  56. //排序后
  57. logger.info("排序后");
  58. Collections.sort(userList);
  59. userList.stream().forEach(user -> System.out.println(user.toString()));
  60. }
  61.  
  62. }

(3)执行结果:

  1. 17:25:59.511 [main] INFO com.springboot.base.comparable.UserComparable - 排序前
  2. name = xiaoxiao, age = 22
  3. name = honghong, age = 19
  4. name = mingming, age = 29
  5. name = shuishui, age = 26
  6. name = yangyang, age = 34
  7. 17:25:59.596 [main] INFO com.springboot.base.comparable.UserComparable - 排序后
  8. name = honghong, age = 19
  9. name = xiaoxiao, age = 22
  10. name = shuishui, age = 26
  11. name = mingming, age = 29
  12. name = yangyang, age = 34

(4)总结:  

  1. public static <T extends Comparable<? super T>> void sort(List<T> list) {
  2. list.sort(null);
  3. }

  Collections.sort();如若借助这个方法进行排序,List集合存储的元素必须是实现 Comparable 接口并重写compareTo()方法的对象;要求在此类中实现 compareTo() 接口,耦合性比较高,不建议使用!

二、Comparator比较器

(1)Comparator接口是一个函数式接口,只有一个抽象方法 compare(),compare比较的o1和o2,返回值大于0则o1大于o2,以此类推;

  1. @FunctionalInterface
  2. public interface Comparator<T> {
  3.  
  4. /**
  5. * 唯一抽象方法
  6. */
  7. int compare(T o1, T o2);
  8.  
  9. /**
  10. * 列表逆序
  11. */
  12. default java.util.Comparator<T> reversed() {
  13. return Collections.reverseOrder(this);
  14. }
  15.  
  16. /**
  17. * 静态方法
  18. */
  19. public static <T, U> java.util.Comparator<T> comparing(
  20. Function<? super T, ? extends U> keyExtractor,
  21. java.util.Comparator<? super U> keyComparator)
  22. {
  23. Objects.requireNonNull(keyExtractor);
  24. Objects.requireNonNull(keyComparator);
  25. return (java.util.Comparator<T> & Serializable)
  26. (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
  27. keyExtractor.apply(c2));
  28. }
  29. }

(2)实例代码:

  1. public class User {
  2.  
  3. private static Logger logger = LoggerFactory.getLogger(User.class);
  4.  
  5. private String name;
  6.  
  7. private Integer age;
  8.  
  9. public User(String name, Integer age) {
  10. this.name = name;
  11. this.age = age;
  12. }
  13.  
  14. public String getName() {
  15. return name;
  16. }
  17.  
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21.  
  22. public Integer getAge() {
  23. return age;
  24. }
  25.  
  26. public void setAge(Integer age) {
  27. this.age = age;
  28. }
  29.  
  30. @Override
  31. public String toString() {
  32. return "name = " + this.getName() + ", age = " + this.getAge();
  33. }
  34.  
  35. public static void main(String[] args) {
  36. List<User> userList = Lists.newArrayList();
  37. userList.add(new User("xiaoxiao", 22));
  38. userList.add(new User("honghong", 19));
  39. userList.add(new User("mingming", 29));
  40. userList.add(new User("shuishui", 26));
  41. userList.add(new User("yangyang", 34));
  42. //排序前
  43. logger.info("升序排序前");
  44. userList.stream().forEach(user -> System.out.println(user.toString()));
  45. //排序后
  46. logger.info("升序排序后");
  47. Collections.sort(userList, new UserComparator());
  48. userList.stream().forEach(user -> System.out.println(user.toString()));
  49. logger.info("升序再逆序");
  50. Collections.reverse(userList);
  51. userList.stream().forEach(user -> System.out.println(user.toString()));
  52. }
  53.  
  54. }
  55.  
  56. class UserComparator implements Comparator<User> {
  57.  
  58. @Override
  59. public int compare(User user1, User user2) {
  60. if (user1.getAge().compareTo(user2.getAge()) > 0) {
  61. return 1;
  62. } else if (user1.getAge().compareTo(user2.getAge()) == 0) {
  63. return 0;
  64. } else {
  65. return -1;
  66. }
  67. }
  68. }

(3)执行结果:

  1. 17:58:09.704 [main] INFO com.springboot.base.comparator.User - 升序排序前
  2. name = xiaoxiao, age = 22
  3. name = honghong, age = 19
  4. name = mingming, age = 29
  5. name = shuishui, age = 26
  6. name = yangyang, age = 34
  7. 17:58:09.817 [main] INFO com.springboot.base.comparator.User - 升序排序后
  8. name = honghong, age = 19
  9. name = xiaoxiao, age = 22
  10. name = shuishui, age = 26
  11. name = mingming, age = 29
  12. name = yangyang, age = 34
  13. 17:58:09.819 [main] INFO com.springboot.base.comparator.User - 升序再逆序
  14. name = yangyang, age = 34
  15. name = mingming, age = 29
  16. name = shuishui, age = 26
  17. name = xiaoxiao, age = 22
  18. name = honghong, age = 19

(4)总结:

  Collections.sort(List list, Comparator comparator)方法,明显可以看出来第二个参数只需要传递一个实现Comparator接口,实现compare()方法的实例对象,实现类会定义排序的逻辑功能;

  优点:由于排序逻辑的实现是在要排序类(User)的外部编写,不会在要排序类(User)里面编写,这样就可以解除要排序类(User)和排序逻辑分离,降低耦合性;

(5)由于Java8的新特性,只要满足有 @FunctionalInterface 注解,就能使用Lambda表达式简化排序代码:

  1.      /**
  2. * 方式一:使用匿名内部类,创建一个实现Comparable接口的类对象,并重写compare()方法,编写排序逻辑
  3. */
  4. userList.stream().sorted(new Comparator<User>() {
  5. @Override
  6. public int compare(User user1, User user2) {
  7. if (user1.getAge().compareTo(user2.getAge()) > 0) {
  8. return 1;
  9. } else if (user1.getAge().compareTo(user2.getAge()) == 0) {
  10. return 0;
  11. } else {
  12. return -1;
  13. }
  14. }
  15. }).collect(Collectors.toList());
  16. userList.stream().forEach(user -> System.out.println(user.toString()));
  17.  
  18. /**
  19. * 方式二:使用Lambda表达式;由于sorted()方法需要一个实现Comparator接口,并重写compare()方法的对象,
  20. * compare()方法接收两个参数,Lambda表达式会根据 userList 集合存储的对象类型,自动推导出 user1,user2 的类型,
  21. * 并传递到compare()方法中,进行排序操作(省略这些的代码,在编译时期,会自动的推导出源代码)
  22. */
  23. userList.stream().sorted((user1, user2) -> user1.getAge().compareTo(user2.getAge())).collect(Collectors.toList());
  24. userList.stream().forEach(user -> System.out.println(user.toString()));
  25.  
  26. /**
  27. * 方式三:更加简化的方式二Lambda表达式,由于 Comparator 接口存在 comparing() 静态方法,接收参数,
  28. * 比较方式,使用类::方法对其相应对象属性值进行排序
  29. */
  30. userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
         userList.stream().forEach(user -> System.out.println(user.toString()));

  推荐排序一般还是使用 Comparator 接口,结合 Lambda 表达式进行排序,编写简单,简捷;

Comparable 和 Comparator 总结:

  两种方法各有优劣, 用Comparable 简单, 只需实现 Comparable 接口的对象重写compareTo()方法,直接就成为一个可以比较的对象,若要修改排序逻辑,必须要修改要排序类的原代码,耦合性太高;

  用Comparator 的好处是若排序功能需要修改,不需要修改要排序类的源代码, 但需另写类实现Comparator接口重写compare()方法,可以借用Lambda表达式进行简便操作!

玩转 Comparator 和 Comparable 两接口的更多相关文章

  1. Java集合中Comparator和Comparable接口的使用

    在Java集合中,如果要比较引用类型泛型的List,我们使用Comparator和Comparable两个接口. Comparable接口 -- 默认比较规则,可比较的 实现该接口表示:这个类的实例可 ...

  2. 接口Comparator和Comparable的区别和联系

    1. Comparator 和 Comparable 相同的地方 他们都是java的一个接口, 并且是用来对自定义的class比较大小的. 什么是自定义class: 如 public class Pe ...

  3. Map容器——TreeMap及常用API,Comparator和Comparable接口

    TreeMap及常用API ①   TreeMap类通过使用红黑树实现Map接口; ②   TreeMap提供按排序顺序存储键/值对的有效手段,同时允许快速检索; ③   不像散列(HashMap), ...

  4. Comparator和Comparable

    java.util 接口 Comparator<T>   compare int compare(T o1, T o2) 比较用来排序的两个参数.根据第一个参数小于.等于或大于第二个参数分 ...

  5. 【原】Comparator和Comparable的联系与区别

    1.知识点了解 Comparator和Comparable都是用用来实现集合中元素的比较.排序的,所以,经常在集合外定义Comparator接口的方法和集合内实现Comparable接口的方法中实现排 ...

  6. java之Comparator与Comparable

    转自:http://blog.csdn.net/zhangerqing 当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定 ...

  7. java的Comparator和Comparable

    java的Comparator和Comparable 当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序.      ...

  8. Comparator与Comparable用法与区别

    一.概述.   Comparator和Comparable两者都属于集合框架的一部分,都是用来在对象之间进行比较的,但两者又有些许的不同,我们先通过一个例子来看一下他们的区别,然后再分别学习下它们的源 ...

  9. 关于Comparator和Comparable的理解

    我们都知道,实现Comparator和Comparable这两个接口,可以实现对对象比较大小.那这个两个又有什么区别呢? comparator 1.接口类在java.util包里面,实现接口时需要导入 ...

随机推荐

  1. java安全编码指南之:ThreadPool的使用

    目录 简介 java自带的线程池 提交给线程池的线程要是可以被中断的 正确处理线程池中线程的异常 线程池中使用ThreadLocal一定要注意清理 简介 在java中,除了单个使用Thread之外,我 ...

  2. 彩贝网app破解登入参数(涉及app脱壳,反编译java层,so层动态注册,反编译so层)

    一.涉及知识点 app脱壳 java层 so层动态注册 二.抓包信息 POST /user/login.html HTTP/1.1 x-app-session: 1603177116420 x-app ...

  3. spring-boot-route(二十一)quartz实现动态定时任务

    Quartz是一个定时任务的调度框架,涉及到的主要概念有以下几个: Scheduler:调度器,所有的调度都由它控制,所有的任务都由它管理. Job:任务,定义业务逻辑. JobDetail:基于Jo ...

  4. eclipse 开发常见问题集锦

    问题1: eclipse导入外部项目,中文显示乱码(如下图) 方案:项目名-->右键属性-->如下图: 问题2: jsp/html页面eclipse双击打开,代码在工作区不显示(如下图:) ...

  5. CVE-2019-15107漏洞复现

    特别说明 漏洞复现参考了teeom sec的panda潘森的文章,是根据他的思路进行复现. 搭建dockers环境, 然后安装vulhub(此处省略,自行百度) 进入vulhub/webmin/CVE ...

  6. python的各种库的用法

    scipy.io 用于输入和输出数据的操作,可操作matlab的.mat文件. (1)加载.mat文件的数据 import scipy.io as sci data_dir = sci.loadmat ...

  7. windows7 安装配置NodeJS、NPM

    转载自https://blog.csdn.net/dengxw00/article/details/82974808 windows7 安装配置NodeJS.NPM一.安装 NodeJS1.登陆官网( ...

  8. Java的Arrays.sort()方法到底用的什么排序算法

    暂时网上看过很多JDK8中Arrays.sort的底层原理,有些说是插入排序,有些说是归并排序,也有说大于域值用计数排序法,否则就使用插入排序...其实不全对.让我们分析个究竟: 1 // Use Q ...

  9. 想springboot启动完成后执行某个方法

    如题,很多时候,我们都需要在springboot项目启动后初始化化一些自己的数据 原文地址:https://www.jianshu.com/p/f80f833ab8f6 实现方法有2个. 一.Appl ...

  10. c语言博客作业——顺序结构,分支结构

    1.PTA截图 2.本章学习总结 2.1学习内容总结 数据的输入和输出:%d表示输入输出整数 %.lf表示输入浮点数 %.nf表示输出结果保留n位小数 if-else的分支结构可以有限个分类情况进行处 ...