在java里的数据类型中,number数据类型,比如int,double还有字符串类型Stirng等等,都是能够比较大小的,为什么?原因就是他们都实现了一个接口就是Comparable接口,现实的应用中我们可能会遇到想让自定义的类的对象可以进行排序,前提就是需要这里类的对象能比较大小,怎么办?
接口Comparable
我们知道数字是可以用> < == 等运算符进行比较大小的,其实在字符串中也有CompareTo方法,这个方法可以用于比较字符串大小的,根据字典顺序进行排序。
Str1.compareTo(Str2);
其返回的是一个int类型值。若Str1等于参数字符串Str2字符串,则返回0;若该Str1按字典顺序小于参数字符串Str2,则返回值小于0;若Str1按字典顺序大于参数字符串Str2,则返回值大于0。
Java中很多类也都有CompareTo方法,甚至于排序算法的底层也是依赖于比较的,而这个比较就是依赖于各种数据类型的CompareTo或者Compare方法。Java中所有的compareTo方法都源于一个接口,那就是Comparable。这个接口只有一个方法,那就是CompareTo。所有想要具有比较功能的类,都建议实现这个接口,而非是自己定义这个功能,这是面向对象的概念(将具有相同功能的事物抽象到一个共同的类或接口),并且为了多态也建议通过实现接口来进行向上转型,通过接口来操作具体实现,这也是面向接口编程要求我们做的。下面我们来具体了解一下Comparable接口。
先分析一下Strnig类的compareTo和equals方法:
package coursetest; public class Person implements Comparable<Person> { private Integer id;//建议做成id的包装类 private String name;//定义字符串类型变量 变量名为name private int age;//定义int整型变量 变量名为age //为三个私有变量构造get set方法 使数据可以在私有的情况赋值和取出 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //重写compareTo方法 @Override public int compareTo(Person o) {//根据id比大小 // p1.compartTo(p2); //p1>p2:正整数,p1==p2:0;p1<p2:负整数 // if(this.id>o.id) return 1; // if(this.id==o.id) return 0; // return 1; return this.id - o.id; //一句话搞定上面三行代码 } //重写hashCode方法 @Override public int hashCode() {//重写hashCode // TODO Auto-generated method stub return id.hashCode(); } //重写equals方法,提供自定义的相等标准 参数名为obj @Override public boolean equals(Object obj) { if(obj instanceof Person) { //是否obj变量类型可以转换为Person变量类型 Person other =(Person)obj;//强制转换,就是将obj类型强制转换成Person类型 if (this.id == other.id) { return true; }else { return false; } } return false; } //重写toString方法 @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + "]"; } //重写toString() 可以理解为是对对象在打印输出时候的一种格式化。这样做符合业务逻辑,显示结果人性化 }
package coursetest; import java.util.Arrays; public class Demo { public static void main(String[] args) { Person p1 = new Person();//对象1 p1.setId(1); Person p2 = new Person();//对象2 p2.setId(2); Person p3 = new Person();//对象3 p3.setId(3); Person p4 = new Person();//对象4 p4.setId(4); Person [] arr = new Person [] {p4,p3,p1,p2};//定义一个数组,将对象1234放入 System.out.println(Arrays.toString(arr));//打印数组 Arrays.sort(arr);//调用array.sort方法为数组排序 System.out.println(Arrays.toString(arr));//再次打印数组 } }
注意:一般情况下,compareTo和equals方法应该是统一的,equals返回true,compareTo应用返回0;所以实现了compareTo方法就要覆盖equals方法,让两者保持一致!而且前面又讲了覆盖equals方法,就一定要重写hashCode方法。
最后compareable接口,我们自定义一个Person类来实践一下!
定义要给Person类,实例化5个Person类的对象,放到数组里,然后用Arrays.sort(数组);工具对数组进行排序!
接口Comparator
1. 我们需要控制某个类的次序,而该类不支持排序(即没有实现Comparable接口)。那么,我们可以创建一个该类的比较器,这个比较器只需要实现Comparator。若一个类实现了Comparator接口,则它一定要实现compare(T o1,T o2)方法,但不一定要实现equals(Object obj)方法;
2. 如果类的设计师没有考虑到Compare的问题而没有实现Comparable接口,可以通过Comparator来实现比较算法进行排序;为了使用不同的排序标准做准备,比如:升序、降序
在上面的例子基础上,使用一些接口Comparator,然后用Arrays.sort(数组,比较器);对数组排序输出一下!
public class Dog { private Integer id;//建议做成id的包装类 private String name;//定义字符串类型变量 变量名为name private int age;//定义int整型变量 变量名为age //下面为三个私有变量的get set方法 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } //覆盖toString方法 @Override public String toString() { return "Dog [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
package coursetest; import java.util.Comparator; public class DogComparetor2 implements Comparator<Dog>{ //覆盖compare方法 @Override public int compare(Dog o1, Dog o2) { return o1.getAge() - o2.getAge();//两个对象的年龄差 } }
package coursetest; import java.util.Comparator; public class DogComparetor2 implements Comparator<Dog>{ //覆盖compare方法 @Override public int compare(Dog o1, Dog o2) { return o1.getAge() - o2.getAge();//两个对象的年龄差 } }
package coursetest; import java.util.Arrays; public class Demo { public static void main(String[] args) { Dog dog1 = new Dog(); dog1.setId(1); dog1.setAge(2); Dog dog2 = new Dog(); dog2.setId(2); dog2.setAge(5); Dog dog3 = new Dog(); dog3.setId(3); dog3.setAge(1); Dog[] arr = new Dog[] { dog2, dog1, dog3 };// 定义一个数组,将对象123放入 System.out.println(Arrays.toString(arr));// 打印数组 //进行Id排序 Arrays.sort(arr,new DogComparetor());//调用自己创建的DogComparetor比较器 //Arrays.sort(arr,new DogComparetor2()); //调用自己创建的DogComparetor2比较器进行年龄排序 System.out.println(Arrays.toString(arr));// 打印数组 } }
Compareable和Comparator两者比较总结:
1. 前者应该比较固定,和一个具体类相绑定,而后者比较灵活,它可以被用于各个需要比较功能的类使用
2. Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”;而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
3. Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
4. Comparable是通用的接口,用户可以实现它来完成自己特定的比较,而Comparator可以看成是一种算法的实现,在需要容器集合实现比较功能的时候,来指定这个比较器,这可以看成是一种设计模式,将算法和数据分离。
java7里面新增的Objects类,该类的主要用途是将操作对象的一切常用操作进行的封装。包括hashCode、equals等。 java8在7的基础上又新增一些方法。
知识点一:Objects类简介
本类由一些操作对象的静态工具方法构成,这些工具方法包括了非空检查、方法的非空参数检查、比较对象的hashCode、为对象返回一个字符串、比较两个对象。
知识点二:Objects特点
1.该类是使用final修饰,不能被继承 2.该类的构造方法被私有化(使用private修饰),不能直接创建对象 3.该类中所有的方法都是静态方法,可以使用类型直接调用(对应2,该类不需要创建对象)
知识点三:Objects内方法介绍
//比较两个对象是否相等(首先比较内存地址,然后比较a.equals(b),只要符合其中之一返回true)
public static boolean equals(Object a, Object b);
//深度比较两个对象是否相等(首先比较内存地址,相同返回true;如果传入的是数组,则比较数组内的对应下标的值是否相同)
public static boolean deepEquals(Object a, Object b);
import java.util.Arrays; import java.util.Objects; public class Demo { public static void main(String[] args) { Integer [] a1 = new Integer [] {1,2,3}; Integer [] a2 = new Integer [] {1,2,3}; Integer [] a3 = new Integer [] {1,3,3}; System.out.println(Objects.deepEquals(a1, a2));//true System.out.println(Objects.deepEquals(a1, a3));//false } }
//返回对象的hashCode,若传入的为null,返回0
public static int hashCode(Object o);
import java.util.Objects; public class Demo { public static void main(String[] args) { String string = "a"; String s1 = null; System.out.println(string.hashCode());//97 //System.out.println(s1.hashCode());//java.lang.NullPointerException报异常 System.out.println(Objects.hashCode(s1));//0 }
//返回传入可变参数的所有值的hashCode的(加工后)总和(这里说总和有点牵强,具体参考Arrays.hashCode()方法)
public static int hash(Object... values);
public class Demo { public static void main(String[] args) { System.out.println("a".hashCode());//97 System.out.println(Objects.hashCode("a"));//97 System.out.println(Objects.hash("a"));//128 } }
1.
//返回对象的String表示,若传入null,返回null字符串
public static String toString(Object o)
import java.util.Objects; public class Demo { public static void main(String[] args) { String s = "aaa"; System.out.println(s.toString());//aaa System.out.println(Objects.toString(s));//aaa String s1 = null; //System.out.println(s1.toString());//报错 System.out.println(Objects.toString(s1));//null } }
//返回对象的String表示,若传入null,返回默认值nullDefault
public static String toString(Object o, String nullDefault)
import java.util.Objects; public class Demo { public static void main(String[] args) { String s = null; System.out.println(Objects.toString(s,"default"));//default } }
//使用指定的比较器c 比较参数a和参数b的大小(相等返回0,a大于b返回整数,a小于b返回负数)
public static <T> int compare(T a, T b, Comparator<? super T> c)
//如果传入的obj为null抛出NullPointerException,否者返回obj
public static <T> T requireNonNull(T obj)
//如果传入的obj为null抛出NullPointerException并可以指定错误信息message,否者返回obj
public static <T> T requireNonNull(T obj, String message)
import java.util.Objects; public class Demo { public static void main(String[] args) { String s = null; Objects.requireNonNull(s,"不能为空"); } }
//判断传入的obj是否为null,是返回true,否者返回false
public static boolean isNull(Object obj)
//判断传入的obj是否不为null,不为空返回true,为空返回false(和isNull()方法相反)
public static boolean nonNull(Object obj)
//如果传入的obj为null抛出NullPointerException并且使用参数messageSupplier指定错误信息,否者返回obj
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)
知识点五:总结
Objects类给我们提供了一些常用的操作对象的方法,我们可以直接使用,这是非常方便的。尤其是requireNonNull()方法, 在我们写方法时需要判断传入的参数是否为null可以使用该方法及其方便。
在java里的数据类型中,number数据类型,比如int,double还有字符串类型Stirng等等,都是能够比较大小的,为什么?原因就是他们都实现了一个接口就是Comparable接口,现实的应用中我们可能会遇到想让自定义的类的对象可以进行排序,前提就是需要这里类的对象能比较大小,怎么办?
接口Comparable
我们知道数字是可以用> < == 等运算符进行比较大小的,其实在字符串中也有CompareTo方法,这个方法可以用于比较字符串大小的,根据字典顺序进行排序。
Str1.compareTo(Str2);
其返回的是一个int类型值。若Str1等于参数字符串Str2字符串,则返回0;若该Str1按字典顺序小于参数字符串Str2,则返回值小于0;若Str1按字典顺序大于参数字符串Str2,则返回值大于0。
Java中很多类也都有CompareTo方法,甚至于排序算法的底层也是依赖于比较的,而这个比较就是依赖于各种数据类型的CompareTo或者Compare方法。Java中所有的compareTo方法都源于一个接口,那就是Comparable。这个接口只有一个方法,那就是CompareTo。所有想要具有比较功能的类,都建议实现这个接口,而非是自己定义这个功能,这是面向对象的概念(将具有相同功能的事物抽象到一个共同的类或接口),并且为了多态也建议通过实现接口来进行向上转型,通过接口来操作具体实现,这也是面向接口编程要求我们做的。下面我们来具体了解一下Comparable接口。
先分析一下Strnig类的compareTo和equals方法:
注意:一般情况下,compareTo和equals方法应该是统一的,equals返回true,compareTo应用返回0;所以实现了compareTo方法就要覆盖equals方法,让两者保持一致!而且前面又讲了覆盖equals方法,就一定要重写hashCode方法。
最后compareable接口,我们自定义一个Person类来实践一下!
定义要给Person类,实例化5个Person类的对象,放到数组里,然后用Arrays.sort(数组);工具对数组进行排序!
接口Comparator
1. 我们需要控制某个类的次序,而该类不支持排序(即没有实现Comparable接口)。那么,我们可以创建一个该类的比较器,这个比较器只需要实现Comparator。若一个类实现了Comparator接口,则它一定要实现compare(T o1,T o2)方法,但不一定要实现equals(Object obj)方法;
2. 如果类的设计师没有考虑到Compare的问题而没有实现Comparable接口,可以通过Comparator来实现比较算法进行排序;为了使用不同的排序标准做准备,比如:升序、降序
在上面的例子基础上,使用一些接口Comparator,然后用Arrays.sort(数组,比较器);对数组排序输出一下!
Compareable和Comparator两者比较总结:
1. 前者应该比较固定,和一个具体类相绑定,而后者比较灵活,它可以被用于各个需要比较功能的类使用
2. Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”;而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
3. Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
4. Comparable是通用的接口,用户可以实现它来完成自己特定的比较,而Comparator可以看成是一种算法的实现,在需要容器集合实现比较功能的时候,来指定这个比较器,这可以看成是一种设计模式,将算法和数据分离。