所以说以后的自定义的类型,如果说要进行比较大小,那么必须要让这个类具有比较的功能
一)Compable接口:
Comparable是排序接口,若一个类实现了Comparable接口,就意味着该类支持排序。
实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序
注意:Compable接口里面的compareTo方法是一个空方法
1)继承comparable接口,里面的泛型参数要写里面的比较的类型,重写CompareTo方法,里面加上泛型参数,泛型参数里面加上要比较的类型,这种写法,对类的侵入性比较强,如果一个类实现了这个Comparable接口,那么就意味着该类支持排序,或者说对象的列表或者数组可以进行自动排序,一旦写好之后,不敢轻易改动
2)一个类型实现了Compareable接口,表明了这个类具有了可排序的功能或者说标准,两个对象通过Compareable接口中的compareTo方法的返回值来比较大小
Comparable接口的源码:
public interface Comparable<T>{ public int compareTo(T o); }
为什么要实现Comparable接口?
一个类型实现了Comparator接口,表明这个类具有了可排序的功能或者标准,两个对象可以通过Compareable接口中的compareTo方法的返回值来进行比较大小static class Student implements Comparable<Student> { public String name; public int age; public Double score; public Student(String name, int age,Double score) { this.name = name; this.age = age; this.score=score; } @Override public int compareTo(Student o) {//谁调用CompareTo谁就是this if(this.age>o.age) return 1;//在这里也可以同构比较年龄 else if(this.age==o.age) return 0; else{ return -1; } } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } } public static void main(String[] args) { Student student1=new Student("李佳伟",15,23.9); Student student2=new Student("李佳鑫",34,89.9); Student student3=new Student("周云刚",89,10.9); Student[] arr1={student1,student2,student3}; Arrays.sort(arr1); System.out.println(Arrays.toString(arr1)); //如果我们不实现这个接口,那么直接排序会造成报错 //两个对象进行比较 if(student1.compareTo(student2)>0) { System.out.println("student1的年龄比student2的年龄大"); } }
我们不进行实现这个接口,那么一定会出现报错,不知道按照什么样的规则来进行比较,会出现ClassCastException,类型转换异常,不实现这个接口,都不知道按照什么来进行比较,也就是说你自己定义的类型,要具有可比较的功能
1)首先定义一个学生对象,再定义一个学生对象数组,我们将对象数组中的元素继续进行排序,按照年龄来进行排序,比如说按照年龄进行升序排序,直接传入数组名就可以了
@Data class Student{ public String username; public String password; public int age; public Student(String username, String password, int age) { this.username = username; this.password = password; this.age = age; } } public class HelloWorld { public static void main(String[] args) { Student student1=new Student("李佳伟","12503487",12); Student student2=new Student("李佳鑫","778891",89); Student student3=new Student("张钟俊","9090",86); Student[] students=new Student[3]; Arrays.sort(students); } }
2)当我们调用Arrays.sort(students)的时候,可以发现我们的源码类型可以将数组元素强制转换成Comparable类型,而我们自定义的类和我们的Comparable毫不相关,Student类是没有compareTo方法的,此时去跳转到异常提示的位置查看,可以发现源码中是将数组元素强制转换为Comparable类型,再去调用其中的compareTo方法,而此时我们自定义类型Student与Comparable毫不相干
package com.example.demo; import lombok.Data; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @Data class Student implements Comparable<Student>{ public String username; public String password; public int age; public Student(String username, String password, int age) { this.username = username; this.password = password; this.age = age; } @Override public int compareTo(Student o) { return this.age-o.age; } } public class HelloWorld { public static void main(String[] args) { Student student1=new Student("李佳伟","12503487",12); Student student2=new Student("李佳鑫","778891",89); Student student3=new Student("张钟俊","9090",86); Student[] students=new Student[3]; students[0]=student1; students[1]=student2; students[2]=student3; // Arrays.sort(students); List<Student> list=new ArrayList<>(); list.add(student1); list.add(student2); list.add(student2); Collections.sort(list); System.out.println(Arrays.toString(students)); System.out.println(list); } }
1)Arrays.sort()也可以传入一个Comparator接口
谁调用compareTo方法,谁就是this
2)再去观察String类的源码,发现我们的String类也实现了Comparable接口重写了里面的compareTo方法
3)但是如果假设说此时如果说我们的需求发生了变化,此时我们根据年龄来进行比较,此时就需要在这个学生类中就需要进行修改内部的代码,这个时候就不好改了
package com.example.demo; import java.util.Arrays; public class HelloWorld{ public static void main(String[] args) { String[] strings={"a","aa","aaa","aaaa"}; Arrays.sort(strings); System.out.println(Arrays.toString(strings)); } }
class Student implements Comparable<Student>{ public String username; public int age; public String password; public Student(String username, int age, String password) { this.username = username; this.age = age; this.password = password; } @Override public int compareTo(Student o) { return this.age-o.age; } @Override public String toString() { return "Student{" + "username='" + username + '\'' + ", age=" + age + ", password='" + password + '\'' + '}'; } } public class UserController{ public static void main(String[] args) { Student student1=new Student("A",19,"12345"); Student student2=new Student("B",20,"115256"); Student student3=new Student("C",21,"9090"); Student student4=new Student("D",22,"8080"); Student[] students=new Student[]{student1,student2,student3,student4}; ArraySort(students); System.out.println(Arrays.toString(students)); } private static void ArraySort(Comparable[] array) { for(int i=0;i<array.length-1;i++){ boolean flag=false; for(int j=0;j<array.length-1-i;j++){ if(array[i].compareTo(array[j])>0){ Comparable temp=array[i]; array[i]=array[j]; array[j]=temp; flag=true; } if(flag==false){ return; } } } } }
二)Comparator接口
1)定义一个新的类,让他继承Comparator接口,里面要有一个泛型参数,放比较的类型,之后在我们的main方法里面直接可以new一个比较器,里面的参数就是我们想要比较的类型
咱们的Comparator的接口的源码:
public interface Comparator<T>{ int compare(T o1,T o2); boolean equals(Object obj) }
2)Comparator是一个可比较接口,我们如果说想要控制某一个类的次序,但是这个类不支持排序,没有实现Comparable接口,那么我们在这个类外面创建一个类的比较器来进行排序,实现Comparator接口
3)实现Comparator的接口的实际应用:
4)Arrays.sort()里面有下面的给出的重载,可以用来排序自定义类型
Arrays.sort(T[] arr1,Comparator<? super T> c) 第一个参数传入数组,第二个参数传入一个Comparator接口
5)也可以对实现Comparable的类实现比较,comparable接口对类的侵入性比较强,但是使用起来非常灵活,用Comparator实现比较器,当某一个自定义对象需要进行比较的时候,把比较器和对象一起传入过去就大小了
static class AgeComparator implements Comparator<Student> { public int compare(Student o1, Student o2) { return o1.age- o2.age; } } static class Student { public String name; public int age; public Double score; public Student(String name, int age, Double score) { this.name = name; this.age = age; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", score=" + score + '}'; } } public static void main(String[] args) { Student student1 = new Student("李佳伟", 15, 23.9); Student student2 = new Student("李佳鑫", 4, 89.9); Student student3 = new Student("周云刚", 89, 10.9); Student[] arr1 = {student1, student2, student3}; Arrays.sort(arr1,new AgeComparator()); System.out.println(Arrays.toString(arr1)); //或者也可以这么进行比较 AgeComparator ageComparator=new AgeComparator(); if(ageComparator.compare(student1,student2)>0) { System.out.println(1); } }
三)Comparable(对内实现)和Comparator(对外实现)的区别
1)Comparable翻译成中文是比较的意思,而Comparator翻译成中文是比较器的意思
2)实现Comparable接口并重写compareTo方法就可以实现某一个类来进行排序了,支持Collections.sort()和Arrays.sort()的排序,如果说自定义对象没有实现Comparable接口,在Arrays.sort()方法的底层,都把数组每一个元素转化成了Compareable元素,或者传入一个比较器
3)Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”;而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序
4)Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
5)Comparable 对类的侵入性非常强, 一但投入使用便不方便再做修改,用起来比较简单,只要实现Comparable 接口的对象直接就成为一个可以比较的对象,需要重写comparTo方法,所以如果想要更换比较方式,就需要对comparTo “大动干戈”。
6)Comparator 对类的侵入性比较弱, 使用起来非常灵活,用Comparator实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 使用Comparator比较,如果想要更换比较方式,只需要在原来的基础上再增加一个比较器即可
package Demo; import java.util.ArrayList; import java.util.Comparator; import java.util.List; class Task{ public String name; public int age; public Task(String name,int age){ this.name=name; this.age=age; } @Override public String toString() { return "Task{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class DemoKail{ public static void main(String[] args) { List<Task> list=new ArrayList<>(); Task task1=new Task("李佳伟",19); Task task2=new Task("李嘉欣",20); Task task3=new Task("及时雨",88); list.add(task1); list.add(task2); list.add(task3); list.sort(new Comparator<Task>() { @Override public int compare(Task o1, Task o2) { return o1.age-o2.age; } }); System.out.println(list); } }
default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c);
Arrays.sort()和list.sort()都可以对Compareable和Compareator接口的类实现自定义比较
public static <T> void sort(List<T> list, Comparator<? super T> c) { list.sort(c); }
上面是Collections.sort()
四)标准库常见的类实现Comparable接口或者是Compartor接口
1)String实现comparable接口,重写了compareTo方法
1)从前依次向后进行比较,字符不同,则返回两字符的ASCII 码的差值
2)如果两个字符相同,则返回两字符长度差值
public int compareTo(String anotherString) { int len1 = value.length;//第一个字符串的长度 int len2 = anotherString.value.length;//第二个字符串的长度 int lim = Math.min(len1, len2);//取两个字符串长度的较小值 char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) {//进行遍历那个较小的字符串 char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2;//如果不相同返回ascil码的差值 } k++; } return len1 - len2;//较小的字符串遍历完成直接返回他们的差值 }
2)Integer实现compareable接口,重写了compareTo方法
两个包装类型要通过compareTo方法来进行比较的两个类中的value属性来进行比较
Integer装箱操作:在编译期将Integer a = 3;
这种格式的代码,会编译成Integer a = Integer.valueOf(3),这就是装箱操作。
Integer拆箱操作:在编译期将Integer转换成int类型,会调用int z = a.intValue()方法
拆箱操作在int类型与Integer类型进行==比较,此时Integer会进行拆箱操作
Integer a = new Integer(11); Integer b = 11; int c = 11; System.out.println(a == c); System.out.println(a == b);
1)基本类型和包装类型进行==比较的时候,包装类型会自动进行拆箱操作,也就是包装类型会自动调用Integer.intValue方法,会转化成基本数据类型进行比较
public int intValue() { return value; }//这里面的intValue方法就是返回Integer中的value属性
当我们进行比较a和c的之的时候,a会自动拆箱成11,然后此时就是基本数据类型的比较了,基本数据类型比较的就是值,所以是true
2)当我们的包装类型和包装数据类型进行比较的时候,会将基本数据类型自动进行装箱
2.1)当我们的b和a进行比较的时候,会先将b进行装箱操作,底层会默认调用ValueOf方法
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
这个时候在-127到128之间返回的是一个缓存数组,但是此时a是在堆new出来的新对象,所以说他们的地址不相同,所以结果是false
Integer a = 11; Integer b = 11; Integer c = 129; Integer d = 129; System.out.println(a == b);//此时是true System.out.println(c == d);//此时是false
因为上述类型的比较都调用了valueOf来进行比较