玩转 Comparator 和 Comparable 两接口
最近项目中有排序的需求,就查看了一下Java文档,发现有两个接口都可以进行排序,Comparable 和 Comparator 两接口到底有啥区别?何时用?怎么用?使用场景我都在底下一一研究分享出来:
一、Comparable 比较器
(1)Comparable 是接口,可以认为是一个内比较器,实现了Comparable 接口的类有一个特点,就是这些类可以和自己进行比较,比较逻辑依赖于 comparaTo() 方法。如果借用Collections.sort() 方法来进行排序,那么这个类必须实现 Comparable 接口并实现 compareTo() 方法,java的很多类都实现了Comparable接口,比如 String、Integer 等类
public interface Comparable<T> {
public int compareTo(T o);
}
调用此方法,也就是同一个List中的同类型元素进行比较,即this和o比较;若返回值大于0则this > o,返回值等于0则是this = o,返回值小于0则是this < o;
(2)实例代码:
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)执行结果:
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)总结:
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,以此类推;
@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)实例代码:
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)执行结果:
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表达式简化排序代码:
/**
* 方式一:使用匿名内部类,创建一个实现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());
userList.stream().forEach(user -> System.out.println(user.toString()));
推荐排序一般还是使用 Comparator 接口,结合 Lambda 表达式进行排序,编写简单,简捷;
Comparable 和 Comparator 总结:
两种方法各有优劣, 用Comparable 简单, 只需实现 Comparable 接口的对象重写compareTo()方法,直接就成为一个可以比较的对象,若要修改排序逻辑,必须要修改要排序类的原代码,耦合性太高;
用Comparator 的好处是若排序功能需要修改,不需要修改要排序类的源代码, 但需另写类实现Comparator接口重写compare()方法,可以借用Lambda表达式进行简便操作!
玩转 Comparator 和 Comparable 两接口的更多相关文章
- Java集合中Comparator和Comparable接口的使用
在Java集合中,如果要比较引用类型泛型的List,我们使用Comparator和Comparable两个接口. Comparable接口 -- 默认比较规则,可比较的 实现该接口表示:这个类的实例可 ...
- 接口Comparator和Comparable的区别和联系
1. Comparator 和 Comparable 相同的地方 他们都是java的一个接口, 并且是用来对自定义的class比较大小的. 什么是自定义class: 如 public class Pe ...
- Map容器——TreeMap及常用API,Comparator和Comparable接口
TreeMap及常用API ① TreeMap类通过使用红黑树实现Map接口; ② TreeMap提供按排序顺序存储键/值对的有效手段,同时允许快速检索; ③ 不像散列(HashMap), ...
- Comparator和Comparable
java.util 接口 Comparator<T> compare int compare(T o1, T o2) 比较用来排序的两个参数.根据第一个参数小于.等于或大于第二个参数分 ...
- 【原】Comparator和Comparable的联系与区别
1.知识点了解 Comparator和Comparable都是用用来实现集合中元素的比较.排序的,所以,经常在集合外定义Comparator接口的方法和集合内实现Comparable接口的方法中实现排 ...
- java之Comparator与Comparable
转自:http://blog.csdn.net/zhangerqing 当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定 ...
- java的Comparator和Comparable
java的Comparator和Comparable 当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序. ...
- Comparator与Comparable用法与区别
一.概述. Comparator和Comparable两者都属于集合框架的一部分,都是用来在对象之间进行比较的,但两者又有些许的不同,我们先通过一个例子来看一下他们的区别,然后再分别学习下它们的源 ...
- 关于Comparator和Comparable的理解
我们都知道,实现Comparator和Comparable这两个接口,可以实现对对象比较大小.那这个两个又有什么区别呢? comparator 1.接口类在java.util包里面,实现接口时需要导入 ...
随机推荐
- Tensorflow学习笔记No.8
使用VGG16网络进行迁移学习 使用在ImageNet数据上预训练的VGG16网络模型对猫狗数据集进行分类识别. 1.预训练网络 预训练网络是一个保存好的,已经在大型数据集上训练好的卷积神经网络. 如 ...
- 如何使用dos,以及一些简单的dos命令
一. 如何打开dos窗口 1. 按下 windos键 alt ctrl 之间的键 或者打开开始 菜单 输入 cmd 名令 2. win + r 键 输入 cmd 打开 d ...
- 010_Java基础语法
目录 Java基础语法 注释 单行注释 // 多行注释 /* */ 文档注释 /** */ 标识符 关键字 标识符注意点 数据类型 强类型语言 弱类型语言 Java基础语法 注释 单行注释 // 多行 ...
- 阿里云服务器SQLSERVER 2019 远程服务器环境搭建【原创】【转载请注明出处】
之前做过本地服务器SQLSERVER环境搭建.局域网环境SQLSERVER搭建.一直没有尝试自己完成一个云端服务器的环境搭建.今天就根据一个成功的例子给大家分享一下. 一.云端数据库安装与搭建 我的服 ...
- java安全编码指南之:文件IO操作
目录 简介 创建文件的时候指定合适的权限 注意检查文件操作的返回值 删除使用过后的临时文件 释放不再被使用的资源 注意Buffer的安全性 注意 Process 的标准输入输出 InputStream ...
- 一款强大的双色球走势图,助你500W梦想,js+mvc+html
序言 估计每个人都有中500W的梦想,我关注双色球也有一定年数了,可最多中也只有10块钱,这已经算是最大的奖,最近闲来无事,研究下怎么去开发双色球的走势图,觉得还是蛮有意思的,用MVC+JS+HTMl ...
- 《JavaScript高级程序设计》——第三章 基本概念
这章讲了JavaScript的语法.数据类型.流控制语句和函数.理解还是挺好理解的,但有很多和C.C++.Java不同的地方需要记忆.比如, JavaScript标识符可以由Unicode字符字符组成 ...
- Java基础循环结构
int x=10; do { System.out.println("value of x:"+x); x++; } while(x<20); //do while循环 1 ...
- 使用 Dockerfile 文件但是不使用缓存生成镜像
前一段时候使用 Dockerfile 重新部署 NetCore3.1 项目的时候很顺利,由来由于一些原因,我把以前的镜像删除,如果我们大家继续使用 docker build 命令去生成镜像的话就会报 ...
- python实现登录验证系统(搭建MVC框架)
小型登录注册验证系统 关注公众号"轻松学编程"了解更多. 一.概述 使用Redis+MySQL数据库实现一个小型的登录注册验证系统.在这个系统中初步了解认识MVC框架. 具 ...