Java中 Comparable和Comparator的区别
Comparable
public interface Comparable<T>
Comparable接口对实现它的每个类的对象强加一个整体排序。 这个排序被称为类的自然排序 ,类的compareTo方法被称为其自然比较方法。该接口有且只有一个方法int compareTo(T o)所以继承此接口需要实现该方法。compareTo返回值-1、0、1。
Collections.sort (和Arrays.sort )可以自动对实现此接口的对象进行列表(和数组)排序。
Comparable接口中只有一个方法:
public int compareTo(T o);
调用此方法的对象,也就是this和o进行比较,若返回值大于0则this大于o,返回值等于0则是this等于o,返回值小于0则是this<o,而这个Comparable是直接在我们的自定义类User上实现,因为this是需要一个明确的比较对象的,也就是一般情况下我们会在定义User类的时候有排序的需求,就要实现此接口。
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class UserComparable implements Comparable<UserComparable> { private String name; private int age; public UserComparable(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "UserComparable{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(UserComparable o) { if (this.name.compareTo(o.name)==0){ if (this.age == o.age){ return 0; }else if (this.age >o.age){ return 1; }else { return -1; } }else if (this.name.compareTo(o.name)>0){ return 1; }else { return -1; } } public static void main(String[] args) { List<UserComparable> list = new ArrayList<UserComparable>(); list.add(new UserComparable("gol",21)); list.add(new UserComparable("gol",19)); list.add(new UserComparable("xiao",21)); list.add(new UserComparable("long",21)); System.out.println("排序前:"+list); //排序规则:先按name排序,若name相等则再比较age Collections.sort(list); System.out.println("排序后:"+list); } }
输出结果为:
//排序前: [UserComparable{name='gol', age=21}, UserComparable{name='gol', age=19}, UserComparable{name='xiao', age=21}, UserComparable{name='long', age=21}] //排序后: [UserComparable{name='gol', age=19}, UserComparable{name='gol', age=21}, UserComparable{name='long', age=21}, UserComparable{name='xiao', age=21}]
Comparator
public interface Comparator<T>
比较功能,对一些对象的集合施加了一个整体排序 。 可以将比较器传递给排序方法(如Collections.sort或Arrays.sort ),以便对排序顺序进行精确控制。 一般是在比较器例如: Collections.sort(List<T> list, Comparator<? super T> c) ,Arrays.sort(T[] a, Comparator<? super T> c)根据指定的比较器引起的顺序对指定的列表进行排序。 在Comparator比较器中重写int compare(T o1, T o2) 如果遇到数字的比较,直接在方法内返回两个对象的属性的差值,例如o1.getValue()-o2.getValue() 是升序,o2.getValue()-o1.getValue() 是降序;如果遇到字符形式的比较利用compareTo(T o) 方法(String实现了Comparable接口)进行比较,该方法比较从头开始每一个字符,当前者大于后者返回1,当前者小于后者返回-1。
Comparator接口中方法很多,但是我们只需要实现一个,也是最重要的一个compare,也许有的人会好奇为什么接口中的方法可以不用实现,因为这是JDK8以后的新特性,在接口中用default修饰的方法可以有方法体,在实现接口的时候可以不用重写。
int compare(T o1, T o2);
compare比较的o1和o2,返回值大于0则o1大于o2,依次类推,对于compare;来说this是谁不重要,所比较的两个对象都已经传入到方法中,所有Comparator就是有个外部比较器,在我们设计User初时,并不需要它有比较功能,在后期扩展业务是,Comparator的存在可以使我们在不修改源代码的情况下来完成需求,只需要新定义一个比较器来实现Comparator,重写compare方法并将User对象传进去。
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } public static void main(String[] args) { List<User> list = new ArrayList<User>(); list.add(new User("gol", 21)); list.add(new User("gol", 19)); list.add(new User("xiao", 21)); list.add(new User("long", 21)); System.out.println("排序前:" + list); //排序规则:先按name排序,若name相等则再比较age //创建比较器对象 Comparator comparator = new UserComparator(); Collections.sort(list,comparator); System.out.println("排序后:" + list); } static class UserComparator implements Comparator<User> { @Override public int compare(User o1, User o2) { if (o1.name.compareTo(o2.name) == 0) { if (o1.age == o2.age) { return 0; } else if (o1.age > o2.age) { return 1; } else { return -1; } } else if (o1.name.compareTo(o2.name) > 0) { return 1; } else { return -1; } } } }
输出结果为:
//排序前: [User{name='gol', age=21}, User{name='gol', age=19}, User{name='xiao', age=21}, User{name='long', age=21}] //排序后: [User{name='gol', age=19}, User{name='gol', age=21}, User{name='long', age=21}, User{name='xiao', age=21}]
结合案例
Comparable
OrderBean订单类
继承了Comparable这个借口,有个简单的比较,升序的
//订单 public class OrderBean implements Comparable<OrderBean>{ private int id; //id private String cdate; //创建时间 private String product; //产品名称 private int weight; //重量 private long price; //价格 public OrderBean(int id, String cdate, String product, int weight, long price) { this.id = id; this.cdate = cdate; this.product = product; this.weight = weight; this.price = price; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getCdate() { return cdate; } public void setCdate(String cdate) { this.cdate = cdate; } public String getProduct() { return product; } public void setProduct(String product) { this.product = product; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public long getPrice() { return price; } public void setPrice(long price) { this.price = price; } @Override public String toString() { return "\nOrder_"+id+": [" + "cdate="+cdate+", " + "product="+product+", " + "weight="+weight+" KG, " + "price="+price+" RMB, " + "]"; } /** * 按照weight升序 * sort的话默认调用 * */ public int compareTo(OrderBean o) { return weight - o.getWeight(); } }
Comparable接口使用
如果bean类继承Comparable接口,那么它的集合使用Collections.sort(list);可以默认调用bean类中复写的compareTo这个方法,进行排序
也可以使用compareTo单独比较两个类。
private void testComparable() { System.out.println("\n\n testComparable()>>>"); OrderBean order1 = new OrderBean(1,"2018-01-01","牛肉",10,300); OrderBean order2 = new OrderBean(5,"2018-01-01","怪兽肉",80,400); OrderBean order3 = new OrderBean(2,"2018-02-01","牛肉",100,300); OrderBean order4 = new OrderBean(9,"2018-03-01","唐僧肉",2,600); List<OrderBean> list = new ArrayList<OrderBean>(); list.add(order1); list.add(order2); list.add(order3); list.add(order4); // weight升序排列 Collections.sort(list); System.out.println("按照订单的weight升序排列:" + list); System.out.println("比较1和3:"+order1.compareTo(order3)); }
输出:
按照订单的weight升序排列:[ Order_9: [cdate=2018-03-01, product=唐僧肉, weight=2 KG, price=600 RMB, ], Order_1: [cdate=2018-01-01, product=牛肉, weight=10 KG, price=300 RMB, ], Order_5: [cdate=2018-01-01, product=怪兽肉, weight=80 KG, price=400 RMB, ], Order_2: [cdate=2018-02-01, product=牛肉, weight=100 KG, price=300 RMB, ]] 比较1和3:-90
Comparator比较器的使用
- 排序:大于、小于、等于
- 分组:等于、不等于
private void testComparator() { System.out.println("\n\n testComparator()>>>"); OrderBean order1 = new OrderBean(1,"2018-01-01","牛肉",10,300); OrderBean order2 = new OrderBean(5,"2018-01-01","怪兽肉",80,400); OrderBean order3 = new OrderBean(2,"2018-02-01","牛肉",100,300); OrderBean order4 = new OrderBean(9,"2018-03-01","唐僧肉",2,600); List<OrderBean> list = new ArrayList<OrderBean>(); list.add(order1); list.add(order2); list.add(order3); list.add(order4); /** * ----------------排列----------------- * 大于、小于、等于 * */ //id降序排列 Collections.sort(list, new Comparator<OrderBean>() { public int compare(OrderBean o1, OrderBean o2) { return o2.getId() - o1.getId(); } }); System.out.println("按照订单的id降序排列:"+list); //单价升序排列 Collections.sort(list, new Comparator<OrderBean>() { public int compare(OrderBean o1, OrderBean o2) { return (int)(o1.getPrice()/o1.getWeight() - o2.getPrice()/o2.getWeight()); } }); System.out.println("按照订单的单价升序排列:"+list); System.out.println("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"); /** * ----------------分组----------------- * 等于、不等于 * */ List<List<OrderBean>> byDate = divider(list,new Comparator<OrderBean>() { public int compare(OrderBean o1, OrderBean o2) { return o1.getCdate().equals(o2.getCdate()) ? 0:1; } }); for(int i=0;i<byDate.size();i++) { System.out.println("按照订单的cdate分组【"+i+"】:"+byDate.get(i)); } }
输出:
按照订单的id降序排列:[ Order_9: [cdate=2018-03-01, product=唐僧肉, weight=2 KG, price=600 RMB, ], Order_5: [cdate=2018-01-01, product=怪兽肉, weight=80 KG, price=400 RMB, ], Order_2: [cdate=2018-02-01, product=牛肉, weight=100 KG, price=300 RMB, ], Order_1: [cdate=2018-01-01, product=牛肉, weight=10 KG, price=300 RMB, ]] 按照订单的单价升序排列:[ Order_2: [cdate=2018-02-01, product=牛肉, weight=100 KG, price=300 RMB, ], Order_5: [cdate=2018-01-01, product=怪兽肉, weight=80 KG, price=400 RMB, ], Order_1: [cdate=2018-01-01, product=牛肉, weight=10 KG, price=300 RMB, ], Order_9: [cdate=2018-03-01, product=唐僧肉, weight=2 KG, price=600 RMB, ]] \\\\\\\\\\\\\\\\\\ 按照订单的cdate分组【0】:[ Order_2: [cdate=2018-02-01, product=牛肉, weight=100 KG, price=300 RMB, ]] 按照订单的cdate分组【1】:[ Order_5: [cdate=2018-01-01, product=怪兽肉, weight=80 KG, price=400 RMB, ], Order_1: [cdate=2018-01-01, product=牛肉, weight=10 KG, price=300 RMB, ]] 按照订单的cdate分组【2】:[ Order_9: [cdate=2018-03-01, product=唐僧肉, weight=2 KG, price=600 RMB, ]]
java8简单写法:
//reversed() 反向排序 Collections.sort(list,Comparator.comparing(User::getAge).reversed().thenComparing(User::getName));
总结:
Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
java中大部分我们常用的数据类型的类都实现了Comparable接口,而仅仅只有一个抽象类RuleBasedCollator实现了Comparator接口 ,还是我们不常用的类,这并不是要用Comparab而不要使用Comparator,在设计初时有需求就选择Comparable,若后期需要扩展或增加排序需求是,再增加一个比较器Comparator。
参考文章:
https://www.cnblogs.com/gollong/p/9667789.html
https://blog.csdn.net/yguoelect/article/details/77435073
https://www.jianshu.com/p/53ac9ffb8acb