您的位置:首页 > 编程语言 > Java开发

JAVA 比较方法 Euals Comparable Comparator

2014-08-28 10:42 555 查看


1、equals()方法

equals()方法定义在Object类内并进行了简单的实现,如下:

[java]
view plaincopyprint?





public boolean equals(Object obj) {
return (this == obj);
}

比较两个原始类型比较的是内容,而如果比较引用类型的话,可以看到是通过==符号来比较的,所以比较的是引用地址,如果要自定义比较规则的话,可以覆写自己的equals()方法。 String 、Math、还有Integer、Double等封装类重写了Object中的equals()方法,让它不再比较引用,而是比较对象中实际包含内容。在编写equals()方法时需要遵守一些规定:

对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。也就暗示了x与y是同一类型,因为他们之间调用的方法相同,才能时刻保证一返回值的一致性。
反射性:x.equals(x)必须返回是“true”。
类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
还有一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
任何情况下,x.equals(null),永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。

看一下String类的equals()实现,如下:

[java]
view plaincopyprint?





public boolean equals(Object anObject) {
if (this == anObject) { // 反射性
return true;
}
if (anObject instanceof String) { // 只有同类型的才能比较
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true; // 返回true时,表示长度相等,且字符序列中含有的字符相等
}
}
return false;
}

当我们自己要重写equals()方法进行内容的比较时,可以遵守以下几点:

(1)使用instanceof 操作符检查“实参是否为正确的类型”。
(2)对于类中的每一个“关键域”,检查实参中的域与当前对象中对应的域值。

对于非float和double类型的原语类型域,使用==比较;
对于float域,使用Float.floatToIntBits(afloat)转换为int,再使用==比较;
对于double域,使用Double.doubleToLongBits(adouble) 转换为int,再使用==比较;
对于对象引用域,递归调用equals()方法;
对于数组域,调用Arrays.equals()方法。

无论是Arrays工具类中提供了各种常用数据类型的比较方法,还是对象引用域的equals()方法,其实最终都是转换为float、String等的类型,然后遵照前三个规则来进行比较的,而这些类型的比较方法已经得到了实现。来看一下Arrays类中双精度浮点数的比较:

[java]
view plaincopyprint?





public static boolean equals(double[] a, double[] a2) {
if (a==a2)
return true;
if (a==null || a2==null)
return false;

int length = a.length;
if (a2.length != length)
return false;

for (int i=0; i<length; i++)
if (Double.doubleToLongBits(a[i])!=Double.doubleToLongBits(a2[i])) // 调用Double类中的方法进行比较
return false;

return true;
}

有兴趣的可以自己去看其他的实现源代码。

一般来说,如果你要把一个类的对象放入集合中,那么通常要为其重写equals()方法,让他们比较地址值而不是内容值。特别地,如果要把你的类的对象放入散列中,那么还要重写hashCode()方法;要放到有序集合中,还要重写compareTo()方法。
如上说到的在容器实现的部分中要进行详细的讲解,这里不再做过多的解释。

有兴趣的可以去看集合的实现:传送门:Java 7集合源码 http://blog.csdn.net/column/details/java-jh.html

2、Comparable接口

一个类实现了 Camparable 接口表明这个类的对象之间是可以相互比较的。如果用数学语言描述的话就是这个类的对象组成的集合中存在一个全序。这样,这个类对象组成的集合就可以使用 Sort 方法排序了。

这个接口定义在java.lang包下,其源代码如下:

[java]
view plaincopyprint?





public interface Comparable<T> {
public int compareTo(T o);
}

这个接口支持泛型,在实现compareTo()方法时可限定具体的比较类型。下面来举一个例子,如下:

[java]
view plaincopyprint?





public class PersonBean implements Comparable<PersonBean> {
public PersonBean(int age, String name) {
this.age = age; // 引用类型
this.name = name; // 基本类型
}
int age = 0;
String name = "";
public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
// 覆写equals和hashCode方法
public boolean equals(Object o) {
if (!(o instanceof PersonBean)) {
return false;
}
PersonBean p = (PersonBean) o;
return (age == p.age) && (name.equals(p.name));
}

public int hashCode() {
int result = 17;
result = 31 * result + age;
result = 31 * result + name.hashCode();
return result;
}
public String toString() {
return (age + "{" + name + "}");
}

public int compareTo(PersonBean person) {
int cop = age - person.getAge();
if (cop != 0)
return cop;
else
return name.compareTo(person.name);
}
}

如果两个人的年龄相同,则通过字符串类中的compareTo()方法进行比较。来看String类的定义如下:

[java]
view plaincopyprint?





public final class String implements java.io.Serializable, Comparable<String>, CharSequence

可以看到实现了Comparable接口并且还实现了接口中定义的方法,源代码如下:

[java]
view plaincopyprint?





public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2); // 返回len1和len2中长度较小的一个
char v1[] = value;
char v2[] = anotherString.value;

int k = 0;
while (k < lim) { // 根据字符序列中所含字符的Unicode编码值来比较
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2; // 如果两个字符串的k个字符相等,则长度较小的在前
}

字符串最后都是转换为字符数组进行比较的。

将代码放到List中进行排序:

[java]
view plaincopyprint?





public void compare() {
List<PersonBean> list = new ArrayList<PersonBean>();
list.add(new PersonBean(20, "Tom"));
list.add(new PersonBean(20, "Jeff"));
list.add(new PersonBean(30, "Mary"));
list.add(new PersonBean(20, "Ada"));
list.add(new PersonBean(61, "Peter"));
list.add(new PersonBean(20, "Bush"));
// 对List进行排序
Collections.sort(list);
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}

public static void main(String[] args) {
TestComparable tc = new TestComparable();
tc.compare();
}

或者也可以将代码放到Array数组中进行排序:

运行如上的代码:

[java]
view plaincopyprint?





public class TestComparable {
public void compare() {
PersonBean[] p = { new PersonBean(20, "Tom"),
new PersonBean(20, "Jeff"), new PersonBean(30, "Mary"),
new PersonBean(20, "Ada"), new PersonBean(40, "Walton"),
new PersonBean(61, "Peter"), new PersonBean(20, "Bush") };

System.out.println("before sort:\n" + Arrays.toString(p));
Arrays.sort(p);
System.out.println("after sort:\n" + Arrays.toString(p));
}

public static void main(String[] args) {
TestComparable tc = new TestComparable();
tc.compare();
}

}

结果如下:
before sort:

[20{Tom}, 20{Jeff}, 30{Mary}, 20{Ada}, 40{Walton}, 61{Peter}, 20{Bush}]

after sort:

[20{Tom}, 20{Jeff}, 20{Bush}, 20{Ada}, 30{Mary}, 40{Walton}, 61{Peter}]

3、Comparator接口

这个接口可以实现如下的两个功能:

如果类的设计师没有考虑到 Compare 的问题而没有实现 Comparable 接口,可以通过 Comparator 来实现比较算法进行排序;
进行自定义排序,例如升序、降序等。

这个接口定义在com.util包下,源代码如下:

[java]
view plaincopyprint?





public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}

在这个接口中定义了两个方法。

[java]
view plaincopyprint?





class PersonBean{ // 没有实现Comarable接口
public PersonBean(int age, String name) {
this.age = age;
this.name = name;
}

int age = 0;
String name = "";

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public String toString() {
return (age + "{" + name + "}");
}
}

可以不用实现equals()方法,因为类默认继承Object对象,Object对象中提供了对equals()方法的实现。

[java]
view plaincopyprint?





public class AlphDesc implements Comparator<PersonBean> { // 自定义排序规则
public int compare(PersonBean personA, PersonBean personB) {
int cop = personA.age - personB.age;
if (cop != 0)
return cop;
else
return personB.getName().compareTo(personA.getName());
}
}

运行如上程序:

[java]
view plaincopyprint?





PersonBean[] p = { new PersonBean(20, "Tom"),
new PersonBean(20, "Jeff"), new PersonBean(30, "Mary"),
new PersonBean(20, "Ada"), new PersonBean(40, "Walton"),
new PersonBean(61, "Peter"), new PersonBean(20, "Bush") };

System.out.println("before sort:\n" + Arrays.toString(p));
AlphDesc desc = new AlphDesc();
Arrays.sort(p, desc);
System.out.println("after sort:\n" + Arrays.toString(p));

运行结果如下:

before sort:

[20{Tom}, 20{Jeff}, 30{Mary}, 20{Ada}, 40{Walton}, 61{Peter}, 20{Bush}]

after sort:

[20{Tom}, 20{Jeff}, 20{Bush}, 20{Ada}, 30{Mary}, 40{Walton}, 61{Peter}]

或者还可以选择一些灵活的方法,如下:

[java]
view plaincopyprint?





public static Comparator<PersonBean> AgeComparator = new Comparator<PersonBean>() {
@Override
public int compare(PersonBean e1, PersonBean e2) {
return e1.getAge() - e2.getAge();
}
};

public static Comparator<PersonBean> NameComparator = new Comparator<PersonBean>() {
@Override
public int compare(PersonBean e1, PersonBean e2) {
return e1.getName().compareTo(e2.getName());
}
};

将如上的代码写到PersonBean类中,然后在排序时指定使用哪一种排序,如下使用年龄进行排序:

[java]
view plaincopyprint?





List<PersonBean> list = new ArrayList<PersonBean>();
list.add(new PersonBean(20, "Tom"));
list.add(new PersonBean(20, "Jeff"));
list.add(new PersonBean(30, "Mary"));
list.add(new PersonBean(20, "Ada"));
list.add(new PersonBean(61, "Peter"));
list.add(new PersonBean(20, "Bush"));
// 对List进行排序
Collections.sort(list,PersonBean.AgeComparator);

4、equals()方法、Comarable和Comparator接口之间的比较

1 >> equals()方法与接口的比较

equals()方法定义在Object类中并且提供了简单的实现。而Java中所有的类都继承了equals()方法,类似于Comarable接口,土生土长的,在编写源代码时就定义成这样了。所以你无法改变Object类中定义的equals()方法、或者是不可变类String的compareTo()方法。

为了能够按照自己的规则进行比较,Object类中的equals()方法没有加final关键字,允许进行覆盖重写,类似于Comparator接口,允许自定义比较规则。但是与Comparable接口和Comparator接口比较起来,显得功能不足,专业水准不够。主要体现在如下几个方面:

(1)接口支持范型,可以传入具体的比较类型,而equals()方法以Object类型做为参数,太泛化,比较时容易出问题。如:

[java]
view plaincopyprint?





public class test1 {
public static void main(String args[]) {
AA aa = new AA();
BB bb = new BB();
aa.equals(bb); // 调用aa中的equals()方法
bb.equals(aa); // 调用bb中的equals()方法
}
}

class AA {
}

class BB {
}

不同类型比较容易出问题,如传入错误的类型在编译时无法纠正,不同的实例调用的方法决定了不同的比较规则等等。

(2)比较结果准确性。equals()方法比较结果不准确,如果不相等,只是返回false,而使用接口中定义的方法来比较,可以比较出先后顺序。

(3)equals()是方法,更加注重面向对象的思想(继承覆盖),而接口是类型,面向的是业务和功能。所以说接口范围更大,在Comparable接口中甚至定义了equals()方法来满足其比较的功能。

2 >> 接口之间的比较

Comparable 是通用的接口,用户可以实现它来完成自己特定的比较,而 Comparator 可以看成一种算法的实现,在需要容器集合实现比较功能的时候,来指定这个比较器,这可以看成一种设计模式,将算法和数据分离。

出处:/article/1604633.html

如果想要了解Arrays.sort()方法的源代码,请点击:

/article/4999454.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: