这里注意:分组问题是关键。
比较好的总结:
参考文章,点击这里
*1.对于集合比较使用Collections.sort();
*2.对于集合中的对象比较,需要指定比较逻辑,指定比较逻辑需要实现 Comparable接口并重写compareTo方法自定义逻辑。
*3.对于需要临时改变比较规则,需要使用Collections.sort(List,Comparator),采用回调方式重写Comparator接口的compare方法自定义逻辑。
一、概述
1.
comparable 和 Comparator 都是用来实现集合中的排序的
只是Comparable是在集合内部定义的方法实现的排序
Comparator是在集合外部实现的排序
所以,如想实现排序,就需要在集合外定义Comparator接口的方法compare()或在集合内实现Comparable接口的方法compareTo()
Comparable是一个对象本身就已经支持自比较所需要实现的接口(如String Integer自己就可以完成比较大小操作)
而Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。
实现这个接口comparable里的函数compareTo,可以自定义比较的方法,就是想怎么比较;
但是comparator是一个比较器,可以实现很多的比较器,比如说要比较一头猪,你可以实现一个按照重要比较的比较器,也可以实现一个按照别的东西比较的比较器,如果你要按照不同方式比较的话,可以让那个猪实现comparable接口,然后再compareTo函数里调用不同的比较器
2.
其实非常简单的,想想一下,你有一个类Student.
Comparable是你想比较的类实现的接口。例如学生(Student)和学生(Sutdent)之间需要比较,那么你可以Student类实现Comparable接口,那么Student类中要实现CompareTo方法。那么对于Student类来说,用Comparable接口只能实现一种比较方式。
但是可能会有这样的需求——①我想Student之中按照学号排序(比较)。②我想Sutdent之中按照GPA(成绩)排序(比较)。③我想Student按照身高为性别为第一关键字,姓名的字母顺序为第二关键字排序……
不同的具体应用场景,可能需要不同的比较方法。这样Comparable就捉襟见肘了。
而Comparator是你自己定义一个比较器类实现对两个对象的比较。你有一个需求就实现该接口定义一个比较器就好了。比如三面的三个需求,你只需要分别定义三个类。具体要用那种比较方式,就用哪个类就好了。
因此更加灵活。
Comparable和Comparator都是用来实现集合中元素的比较、排序的。
参考文章,点击这里,comparable的重要性
Comparable是在集合内部定义的方法实现的排序,位于java.lang下。
Comparator是在集合外部实现的排序,位于java.util下。
Comparable
是一个对象本身就已经支持自比较所需要实现的接口,如String、Integer自己就实现了Comparable接口,可完成比较大小操作。自定义类要在加入list容器中后能够排序,也可以实现Comparable接口,在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。
Comparator
是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。Comparator体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
总而言之Comparable是自已完成比较,Comparator是外部程序实现比较。
注意:
1.
Integer x = 2; // 装箱 调用了 Integer.valueOf(2)
int y = x; // 拆箱 调用了 X.intValue()
Arrays.List()操作,返回的虽然是List,但是底层是一个数组,不能进行增删等操作。
二、例子
①Comparator的绝对值比较器
public class AbsComparator<T> implements Comparator<T>
{
public int compare(Object o1, Object o2)
{
int ovalue1 = Math.abs(((Integer) o1).intValue()); //这里使用了拆箱
int ovalue2 = Math.abs(((Integer) o2).intValue());
return (ovalue1 > ovalue2) ? 1 : (ovalue1 == ovalue2 ? 0 : -1);
}
}
import java.util.Arrays;
import java.util.Random;
public class AbsComparatorTest
{
public static void main(String[] args)
{
// 使用方法1
Random rn = new Random();
Integer[] integerArray = new Integer[20];
for (int i = 0; i < integerArray.length; i++)
{
integerArray[i] = new Integer(rn.nextInt(100) * (rn.nextBoolean() ? 1 : -1));
}
System.out.println("用Integer内置方法排序:");
Arrays.sort(integerArray);
System.out.println(Arrays.asList(integerArray));
System.out.println("用AbsComparator排序:");
Arrays.sort(integerArray, new AbsComparator<Integer>());
System.out.println(Arrays.asList(integerArray));
// 使用方法2
System.out.println("用AbsComparator比较-100和10的绝对值大小结果是:");
AbsComparator<Integer> absComparator = new AbsComparator<Integer>();
int result = absComparator.compare(new Integer(-100), new Integer(10));
System.out.println(result);
}
}
若不是调用sort方法,相要直接比较两个对象的大小,Comparator定义了俩个方法,分别是int compare(T o1,T o2)和boolean equals(Object obj)。有时在实现Comparator接口时,并没有实现equals方法,可程序并没有报错,原因是实现该接口的类也是Object类的子类,而Object类已经实现了equals方法。
②comparable和comparator综合例子
// 实现Comparable接口
@Data
public class Person implements Comparable<Object>
{
private int num;
private String name;
private int age;
public Person(int num, String name, int age)
{
super();
this.num = num;
this.name = name;
this.age = age;
}
public int compareTo(Object o)
{
return this.age - ((Person) o).getAge(); //这样默认的是升序,如果需要降序的话return ((Person) o).getAge()-this.age;
}
}
// 实现Comparator接口
public class PersonComparator implements Comparator<Object>
{
public int compare(Object o1, Object o2)
{
int num1 = ((Person) o1).getNum();
int num2 = ((Person) o2).getNum();
//return num1 > num2 ? 1 : (num1 == num2 ? 0 : -1);
return Integer.compare(num1,num2); //因为在java7,版本中,已经在所有的装箱基本类型的类中添加了静态的compare方法。所以尽量避免使用< 和 > .
}
}
// 测试
public class PersonTest {
public static void main(String[] args) {
{
Person p1 = new Person(1, "xy1", 22);
Person p2 = new Person(2, "xy1", 21);
Person p3 = new Person(2, "xy1", 24);
Person p4 = new Person(2, "xy1", 28);
System.out.println("实现Comparable接口方法结果(按年龄比较):"); // 结果为1
System.out.println(p1.compareTo(p2));
System.out.println("实现Comparator接口方法结果(按学号比较):"); // 结果为-1
PersonComparator pc = new PersonComparator();
System.out.println(pc.compare(p1, p4));
// 也可用声明一个Person[] ps,通过Array.sort(ps)进行排序
Person[] ps = {p4,p3,p2,p1};
Arrays.sort(ps);
for (int i = 0; i <ps.length ; i++) {
System.out.println(ps[i].toString());
}
}
}
}
测试结果如下所示:
3.Comparator的用法
1.为什么写?
comparator 是javase中的接口,位于java.util包下,该接口抽象度极高,有必要掌握该接口的使用
大多数文章告诉大家comparator是用来排序,但我想说排序是comparator能实现的功能之一,他不仅限于排序
2.接口功能
该接口代表一个比较器,比较器具有可比性!大多数文章都写如何用comparator排序,是因为javase数组工具类和集合工具类中提供的sort方法sort就是使用Comparator接口来处理排序的,大家见久了都认为Comparator接口是用来排序的,按照java抽象的尿性来看,该接口如果为排序而生,应该叫Sortable,Sortor之类的名字吧!下面是javase一些使用到Comparator接口的地方:
Arrays.sort(T[],Comparator<? super T> c);
Collections.sort(List<T> list,Comparator<? super T> c);
3.使用场景
什么场景需要做比较,那么什么场景就是Comparator接口的用武之地,我总结的两个场景:
- 排序,需要比较两个对象谁排在前谁排在后(排序也可以让类实现Comparable接口,实现后该类的实例也具有排序能力)。
- 分组,需要比较两个对象是否是属于同一组。
- 待补充
4.举个栗子
1.排序
在List或数组中的对象如果没有实现Comparable接口时,那么就需要调用者为需要排序的数组或List设置一个Compartor,Compartor的compare方法用来告诉代码应该怎么去比较两个实例,然后根据比较结果进行排序
talk is cheap show me the code
package com.java.demo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @author puyf
*/
public class SortTest {
class Dog{
public int age;
public String name;
public Dog(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Dog [age=" + age + ", name=" + name + "]";
}
}
public static void main(String[] args) {
List<Dog> list= new ArrayList<>();
list.add(new SortTest().new Dog(5, "DogA"));
list.add(new SortTest().new Dog(6, "DogB"));
list.add(new SortTest().new Dog(7, "DogC"));
Collections.sort(list, new Comparator<Dog>() {
@Override
public int compare(Dog o1, Dog o2) {
return o2.age - o1.age;
}
});
System.out.println("给狗狗按照年龄倒序:"+list);
Collections.sort(list, new Comparator<Dog>() {
@Override
public int compare(Dog o1, Dog o2) {
return o1.name.compareTo(o2.name);
}
});
System.out.println("给狗狗按名字字母顺序排序:"+list);
}
}
2.分组
注意:Comparator<? super T> c中为什么使用super,是因为:这种方法准许为所有的子类使用相同的比较器。
使用Comparator和for循环处理列表,来进行分类;通过调用者实现Comparator接口的比较逻辑,来告诉程序应该怎么比较,通过比较之后得结果来进行分组。比如生活中的拳击比赛,会有公斤级的概念,那么程序中应该实现的处理逻辑是只要两个人的体重在同一个区间则为同一组公斤级的选手。下面例子中分别按照狗狗的颜色和体重级别两个维度来进行分组,因此分组的核心逻辑其实就是比较逻辑。相面我抽了一个工具方法:dividerList,第一个参数为需要处理的数据源,第二参数是分组时的比较逻辑。
package com.java.demo;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* @author puyf
*/
public class GroupTest {
class Apple {
public String color;
public int weight;
public Apple(String color, int weight) {
super();
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
/**
* @author puyf
* @Description:按条件分组
* @param datas
* @param c
* 是否为同一组的判断标准
* @return
*/
public static <T> List<List<T>> divider(Collection<T> datas, Comparator<? super T> c) {
List<List<T>> result = new ArrayList<List<T>>();
for (T t : datas) {
boolean isSameGroup = false;
for (int j = 0; j < result.size(); j++) {
if (c.compare(t, result.get(j).get(0)) == 0) {
isSameGroup = true;
result.get(j).add(t);
break;
}
}
//如果之前的组中没有可以加入的组,那么就另外在创建一个组。
if (!isSameGroup) {
// 创建
List<T> innerList = new ArrayList<T>();
result.add(innerList);
innerList.add(t);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> list = new ArrayList<>();
list.add(new GroupTest().new Apple("红", 205));
list.add(new GroupTest().new Apple("红", 131));
list.add(new GroupTest().new Apple("绿", 248));
list.add(new GroupTest().new Apple("绿", 153));
list.add(new GroupTest().new Apple("黄", 119));
list.add(new GroupTest().new Apple("黄", 224));
List<List<Apple>> byColors = divider(list, new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
// 按颜色分组,这里调用的是String类重写过后的compareTo()方法。
return o1.color.compareTo(o2.color);
}
});
System.out.println("按颜色分组" + byColors);
List<List<Apple>> byWeight = divider(list, new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
// 按重量级
return (o1.weight / 100 == o2.weight / 100) ? 0 : 1;
}
});
System.out.println("按重量级分组" + byWeight);
}
}
结果如下(为了方便看,手动回车换行格式化了下):
按颜色分组
[
[
Apple [color=红, weight=205],
Apple [color=红, weight=131]
],
[
Apple [color=绿, weight=248],
Apple [color=绿, weight=153]
],
[
Apple [color=黄, weight=119],
Apple [color=黄, weight=224]
]
]
按重量级分组
[
[
Apple [color=红, weight=205],
Apple [color=绿, weight=248],
Apple [color=黄, weight=224]
],
[
Apple [color=红, weight=131],
Apple [color=绿, weight=153],
Apple [color=黄, weight=119]
]
]
5.总结
一般需要做比较的逻辑都可以使用的上Comparator,最常用的场景就是排序和分组,排序常使用Arrays和Collections的sort方法,而分组则可以使用上面提供的divider方法。
排序和分组的区别在于:
排序时,两个对象比较的结果有三种:大于,等于,小于。
分组时,两个对象比较的结果只有两种:等于(两个对象属于同一组),不等于(两个对象属于不同组)