玩转 Comparator 和 Comparable 两接口
最近项目中有排序的需求,就查看了一下Java文档,发现有两个接口都可以进行排序,Comparable 和 Comparator 两接口到底有啥区别?何时用?怎么用?使用场景我都在底下一一研究分享出来:
一、Comparable 比较器
(1)Comparable 是接口,可以认为是一个内比较器,实现了Comparable 接口的类有一个特点,就是这些类可以和自己进行比较,比较逻辑依赖于 comparaTo() 方法。如果借用Collections.sort() 方法来进行排序,那么这个类必须实现 Comparable 接口并实现 compareTo() 方法,java的很多类都实现了Comparable接口,比如 String、Integer 等类
1 2 3 | public interface Comparable<T> { public int compareTo(T o); } |
调用此方法,也就是同一个List中的同类型元素进行比较,即this和o比较;若返回值大于0则this > o,返回值等于0则是this = o,返回值小于0则是this < o;
(2)实例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | public class UserComparable implements Comparable<UserComparable>{ private static Logger logger = LoggerFactory.getLogger(UserComparable. class ); private String name; private Integer age; public UserComparable(String name, Integer age) { this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this .age = age; } @Override public int compareTo(UserComparable user) { if ( this .age.compareTo(user.getAge()) > 0 ) { return 1 ; } else if ( this .age.compareTo(user.getAge()) == 0 ) { return 0 ; } else { return - 1 ; } } @Override public String toString() { return "name = " + this .getName() + ", age = " + this .getAge(); } public static void main(String[] args) { List<UserComparable> userList = Lists.newArrayList(); userList.add( new UserComparable( "xiaoxiao" , 22 )); userList.add( new UserComparable( "honghong" , 19 )); userList.add( new UserComparable( "mingming" , 29 )); userList.add( new UserComparable( "shuishui" , 26 )); userList.add( new UserComparable( "yangyang" , 34 )); //排序前 logger.info( "排序前" ); userList.stream().forEach(user -> System.out.println(user.toString())); //排序后 logger.info( "排序后" ); Collections.sort(userList); userList.stream().forEach(user -> System.out.println(user.toString())); } } |
(3)执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 | 17 : 25 : 59.511 [main] INFO com.springboot.base.comparable.UserComparable - 排序前 name = xiaoxiao, age = 22 name = honghong, age = 19 name = mingming, age = 29 name = shuishui, age = 26 name = yangyang, age = 34 17 : 25 : 59.596 [main] INFO com.springboot.base.comparable.UserComparable - 排序后 name = honghong, age = 19 name = xiaoxiao, age = 22 name = shuishui, age = 26 name = mingming, age = 29 name = yangyang, age = 34 |
(4)总结:
1 2 3 | public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort( null ); } |
Collections.sort();如若借助这个方法进行排序,List集合存储的元素必须是实现 Comparable 接口并重写compareTo()方法的对象;要求在此类中实现 compareTo() 接口,耦合性比较高,不建议使用!
二、Comparator比较器
(1)Comparator接口是一个函数式接口,只有一个抽象方法 compare(),compare比较的o1和o2,返回值大于0则o1大于o2,以此类推;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @FunctionalInterface public interface Comparator<T> { /** * 唯一抽象方法 */ int compare(T o1, T o2); /** * 列表逆序 */ default java.util.Comparator<T> reversed() { return Collections.reverseOrder( this ); } /** * 静态方法 */ public static <T, U> java.util.Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor, java.util.Comparator<? super U> keyComparator) { Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyComparator); return (java.util.Comparator<T> & Serializable) (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1), keyExtractor.apply(c2)); } } |
(2)实例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | public class User { private static Logger logger = LoggerFactory.getLogger(User. class ); private String name; private Integer age; public User(String name, Integer age) { this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this .age = age; } @Override public String toString() { return "name = " + this .getName() + ", age = " + this .getAge(); } public static void main(String[] args) { List<User> userList = Lists.newArrayList(); userList.add( new User( "xiaoxiao" , 22 )); userList.add( new User( "honghong" , 19 )); userList.add( new User( "mingming" , 29 )); userList.add( new User( "shuishui" , 26 )); userList.add( new User( "yangyang" , 34 )); //排序前 logger.info( "升序排序前" ); userList.stream().forEach(user -> System.out.println(user.toString())); //排序后 logger.info( "升序排序后" ); Collections.sort(userList, new UserComparator()); userList.stream().forEach(user -> System.out.println(user.toString())); logger.info( "升序再逆序" ); Collections.reverse(userList); userList.stream().forEach(user -> System.out.println(user.toString())); } } class UserComparator implements Comparator<User> { @Override public int compare(User user1, User user2) { if (user1.getAge().compareTo(user2.getAge()) > 0 ) { return 1 ; } else if (user1.getAge().compareTo(user2.getAge()) == 0 ) { return 0 ; } else { return - 1 ; } } } |
(3)执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 17 : 58 : 09.704 [main] INFO com.springboot.base.comparator.User - 升序排序前 name = xiaoxiao, age = 22 name = honghong, age = 19 name = mingming, age = 29 name = shuishui, age = 26 name = yangyang, age = 34 17 : 58 : 09.817 [main] INFO com.springboot.base.comparator.User - 升序排序后 name = honghong, age = 19 name = xiaoxiao, age = 22 name = shuishui, age = 26 name = mingming, age = 29 name = yangyang, age = 34 17 : 58 : 09.819 [main] INFO com.springboot.base.comparator.User - 升序再逆序 name = yangyang, age = 34 name = mingming, age = 29 name = shuishui, age = 26 name = xiaoxiao, age = 22 name = honghong, age = 19 |
(4)总结:
Collections.sort(List list, Comparator comparator)方法,明显可以看出来第二个参数只需要传递一个实现Comparator接口,实现compare()方法的实例对象,实现类会定义排序的逻辑功能;
优点:由于排序逻辑的实现是在要排序类(User)的外部编写,不会在要排序类(User)里面编写,这样就可以解除要排序类(User)和排序逻辑分离,降低耦合性;
(5)由于Java8的新特性,只要满足有 @FunctionalInterface 注解,就能使用Lambda表达式简化排序代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /** * 方式一:使用匿名内部类,创建一个实现Comparable接口的类对象,并重写compare()方法,编写排序逻辑 */ userList.stream().sorted( new Comparator<User>() { @Override public int compare(User user1, User user2) { if (user1.getAge().compareTo(user2.getAge()) > 0 ) { return 1 ; } else if (user1.getAge().compareTo(user2.getAge()) == 0 ) { return 0 ; } else { return - 1 ; } } }).collect(Collectors.toList()); userList.stream().forEach(user -> System.out.println(user.toString())); /** * 方式二:使用Lambda表达式;由于sorted()方法需要一个实现Comparator接口,并重写compare()方法的对象, * compare()方法接收两个参数,Lambda表达式会根据 userList 集合存储的对象类型,自动推导出 user1,user2 的类型, * 并传递到compare()方法中,进行排序操作(省略这些的代码,在编译时期,会自动的推导出源代码) */ userList.stream().sorted((user1, user2) -> user1.getAge().compareTo(user2.getAge())).collect(Collectors.toList()); userList.stream().forEach(user -> System.out.println(user.toString())); /** * 方式三:更加简化的方式二Lambda表达式,由于 Comparator 接口存在 comparing() 静态方法,接收参数, * 比较方式,使用类::方法对其相应对象属性值进行排序 */ userList.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());<br> <code class = "java spaces" > </code><code class = "java plain" >userList.stream().forEach(user -> System.out.println(user.toString()));</code> |
推荐排序一般还是使用 Comparator 接口,结合 Lambda 表达式进行排序,编写简单,简捷;
Comparable 和 Comparator 总结:
两种方法各有优劣, 用Comparable 简单, 只需实现 Comparable 接口的对象重写compareTo()方法,直接就成为一个可以比较的对象,若要修改排序逻辑,必须要修改要排序类的原代码,耦合性太高;
用Comparator 的好处是若排序功能需要修改,不需要修改要排序类的源代码, 但需另写类实现Comparator接口重写compare()方法,可以借用Lambda表达式进行简便操作!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架