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

java中的equals和hashCode方法以及集合的排序

2013-11-07 00:33 751 查看

equals方法

不覆写equals时候

equals() 的作用是 用来判断两个对象是否相等。

equals() 定义在JDK的Object.java中。通过判断两个对象的地址是否相等(即,是否是同一个对象)来区分它们是否相等。源码如下:

public boolean equals(Object obj) {

return (this == obj);

}

既然Object.java中定义了equals()方法,这就意味着所有的Java类都实现了equals()方法,所有的类都可以通过equals()去比较两个对象是否相等。 但是,我们已经说过,使用默认的“equals()”方法,等价于“==”方法。

因此,我们通常会重写equals()方法:若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。

下面根据“类是否覆盖equals()方法”,将它分为2类。

(01) 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“==”去比较这两个对象。

(02) 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象是否相等。若两个对象的内容相等,则equals()方法返回true;否则,返回fasle。

package org.senssic;

public class Seh {
	private  String name;
	private  int age;

	public Seh(String name, int age) {
		this.age = age;
		this.name = name;
	}

	public static void main(String[] args) {

		Seh seh = new Seh("senssic", 20);
		Seh seh2 = new Seh("sensisc", 20);
		Seh seh3 = seh;
		// 没有覆写equals方法下,equals方法和==方法一样都是通过比较地址比较两个对象
		System.out.println(seh.equals(seh2) + "--->" + (seh == seh2));
		// 只能通过比较地址来判断是否同一对象
		System.out.println(seh.equals(seh3) + "--->" + (seh == seh3) + "--->"
				+ (seh2 == seh3));
	}
}


结果:

false--->false

true--->true--->false

因为没有覆写equals方法,所以即便对象中内容一样也是通过判断地址来比较对象的。

覆写equals时候

覆写equals方法的步骤

1.比较地址,如果地址相同,肯定是同一对象

2.如果比较的对象为null直接返回false

3.比较类的字节码

4.向下类型转换比较属性

5.比较属性

如果是基本属性(除去浮点数)直接判断是否相等

如果是double类型通过doubleToLongBits方法转换为long类型再比较

如果是float类型通过floatToIntBits方法转换为int类型再比较

如果是引用对象先判断是否为空,再调用其对应的equals方法

package org.senssic;

public class Seh {
	private  String name;
	private  int age;
	private byte byt;
	private short shot;
	private long lo;
	private char ch;
	private boolean bool;
	private float fl;
	private double dou;

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Seh other = (Seh) obj;
		if (age != other.age)
			return false;
		if (bool != other.bool)
			return false;
		if (byt != other.byt)
			return false;
		if (ch != other.ch)
			return false;
		if (Double.doubleToLongBits(dou) != Double.doubleToLongBits(other.dou))
			return false;
		if (Float.floatToIntBits(fl) != Float.floatToIntBits(other.fl))
			return false;
		if (lo != other.lo)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (shot != other.shot)
			return false;
		return true;
	}

	public Seh(String name, int age) {
		this.age = age;
		this.name = name;
	}

	public static void main(String[] args) {

		Seh seh = new Seh("senssic", 20);
		Seh seh2 = new Seh("senssic", 20);
		Seh seh3 = seh;
		// 覆写equals方法下,equals方法和==方法一样不一样equals比较同一对象,==比较地址
		System.out.println(seh.equals(seh2) + "--->" + (seh == seh2));
		// 只能通过equals方法来比较是否同一对象
		System.out.println(seh.equals(seh3) + "--->" + (seh == seh3) + "--->"
				+ (seh2 == seh3));
	}
}

结果:

true--->false

true--->true--->false

覆写了equals方法后如果两个对象的属性都相等,则返回true

java对equals()的要求。有以下几点:

1. 对称性:如果x.equals(y)返回是"true",那么y.equals(x)也应该返回是"true"。

2. 反射性:x.equals(x)必须返回是"true"。

3. 类推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true"。

4. 一致性:如果x.equals(y)返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是"true"。

5. 非空性,x.equals(null),永远返回是"false";x.equals(和x不同类型的对象)永远返回是"false"。

equals() 与 == 的区别

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不试同一个对象。

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

情况1,类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。

情况2,类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。

hashCode() 方法

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。

虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建并某个“类的散列表”(关于“散列表”见下面说明)时,

该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),

类的hashCode() 没有作用。

上面的散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。

也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

比如HashSet是Set集合,为了快速的定位里面的元素,就需要用到散列码确定,然后进行操作。当然如果发现其对应的hash表中有相同的hash值,只保存一个。

set判断不同对象的顺序:1>通过hashCode()的值快速比较,如果hash值不相等,判定他们不同;

2>如果hashCode相等,就比较equals()

如果equals()相等,就判定这两个对象相等,否则不相等

若两个元素相等,它们的散列码一定相等;但反过来确不一定。在散列表中,

1、如果两个对象相等,那么它们的hashCode()值一定要相同;

2、如果两个对象hashCode()相等,它们并不一定相等。

package org.senssic;

public class Seh {
	private final String name;
	private final int age;
	private byte byt;
	private short shot;
	private long lo;
	private char ch;
	private boolean bool;
	private float fl;
	private double dou;

	// Objec中的hashCode()方法是native方法,由本地代码自动生成,如果不覆写hashCode则由native代码生成一个hash值
	@Override
	public int hashCode() {
		// 之所以选择31,是因为它是个奇素数,如果乘数是偶数,并且乘法溢出的话,
		// 信息就会丢失,因为与2相乘等价于移位运算。使用素数的好处并不是很明显
		// ,但是习惯上都使用素数来计算散列结果。31有个很好的特性,就是用移位和减法来代替乘法,
		// 可以得到更好的性能:31*i==(i<<5)-i。现在的VM可以自动 //完成这种优化。
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + (bool ? 1231 : 1237);// 1231 1237都是素数
		result = prime * result + byt;
		result = prime * result + ch;
		long temp;
		temp = Double.doubleToLongBits(dou);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		result = prime * result + Float.floatToIntBits(fl);
		result = prime * result + (int) (lo ^ (lo >>> 32));
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + shot;
		return result;
	}

	public Seh(String name, int age) {
		this.age = age;
		this.name = name;
	}

	public static void main(String[] args) {

		Seh seh = new Seh("senssic", 21);
		Seh seh2 = new Seh("senssic", 21);
		// 虽然两个对象的hash值相等但此对象并不相等,所以:对象相等hash值一定相同,hash值相等对象不一定相等
		System.out.println(seh.hashCode() + "--->" + seh2.hashCode());
	}
}


集合类型排序

Java API针对集合类型排序提供了两种支持:

第一个方法要求所排序的元素类必须实现java.lang.Comparable接口。

第二个方法要求实现一个java.util.Comparator接口,需要写个额外的排序类还指定排序方法。

1.实现Comparable接口

package org.senssic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class Person implements Comparable<Person> {
	private  String name;
	private  int age;
	private  int score;
	private  int english;

	public Person(String name, int age, int score, int english) {
		this.name = name;
		this.age = age;
		this.score = score;
		this.english = english;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + english;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + score;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (english != other.english)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (score != other.score)
			return false;
		return true;
	}

	// 1表示大于0表示等于-1表示小于
	@Override
	public int compareTo(Person p) {
		if (this.age > p.age) {// 如果年龄大于直接排序按年龄
			return -1;
		} else if (this.score > p.score) {// 如果年龄小于或等于再比较分数按分数高低排
			return 1;
		} else {// 如果年龄小于且分数小于等于按英语成绩排
			if (this.english > p.english) {
				return 1;
			} else if (this.english < p.english) {
				return -1;
			} else {
				return 0;
			}

		}
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "名字:" + this.name;
	}

	public static void main(String[] args) {
		Person param = new Person("senssic", 12, 23, 45);
		Person person = new Person("qiyu", 12, 23, 41);
		Person perso = new Person("zhangsan", 12, 21, 41);
		Person pers = new Person("zhaosi", 13, 21, 41);
		// 使用list
		List<Person> list = new ArrayList<>();
		list.add(param);
		list.add(pers);
		list.add(perso);
		list.add(person);
		Collections.sort(list);// 集合工具类排序list
		for (Person per : list) {
			System.out.println(per.toString());
		}
		// 使用set
		Set<Person> set = new TreeSet<>();// 因为treeSet是唯一实现SortedSet接口,可以自动实现排序,但是效率低些
		set.add(param);
		set.add(pers);
		set.add(perso);
		set.add(person);
		System.out.println(set.size() + "--->" + perso.equals(param)
				+ perso.equals(pers) + perso.equals(person));
		for (Person p : set) {
			System.out.println(p.toString());
			// 此处只有三个输出因为:TreeSet判断两个对象不相等的标准是:两个对象通过equals方法比较返回false,
			// 或通过compareTo(Object
			// obj)比较没有返回0——即使两个对象时同一个对象,TreeSet也会把它们当成两个对象进行处理。
			// 因为上面的param和perso比较时候没有返回0所以认为是同一个对象,如果使用hashSet就会有四个输出。
		}
		// 使用map
		Map<Person, String> map = new TreeMap<Person, String>();// 通过二叉树算法,可以自动实现排序,但是效率低些
		map.put(pers, "中国");
		map.put(perso, "安徽");
		map.put(param, "池州");
		map.put(person, "阜阳");
		for (Entry<Person, String> mapEntry : map.entrySet()) {
			System.out
					.println(mapEntry.getKey() + "--->" + mapEntry.getValue());
			// 此处输出的也为三个,比较对象相同方法同TreeSet一样
		}
	}
}


2.实现Comparator接口

package org.senssic;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

class PersonComper implements Comparator<Person> {

	@Override
	public int compare(Person p, Person per) {
		if (p.getAge() > per.getAge()) {// 如果年龄大于直接排序按年龄
			return -1;
		} else if (p.getScore() > per.getScore()) {// 如果年龄小于或等于再比较分数按分数高低排
			return 1;
		} else {// 如果年龄小于且分数小于等于按英语成绩排
			if (p.getEnglish() > per.getEnglish()) {
				return 1;
			} else if (p.getEnglish() < per.getEnglish()) {
				return -1;
			} else {
				return 0;
			}

		}
	}
}

public class Person {
	private String name;
	private int age;
	private int score;
	private int english;

	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;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	public int getEnglish() {
		return english;
	}

	public void setEnglish(int english) {
		this.english = english;
	}

	public Person(String name, int age, int score, int english) {
		this.name = name;
		this.age = age;
		this.score = score;
		this.english = english;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + english;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + score;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (english != other.english)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (score != other.score)
			return false;
		return true;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "名字:" + this.name;
	}

	public static void main(String[] args) {
		Person param = new Person("senssic", 12, 23, 45);
		Person person = new Person("qiyu", 12, 23, 41);
		Person perso = new Person("zhangsan", 12, 21, 41);
		Person pers = new Person("zhaosi", 13, 21, 41);
		// 使用list
		List<Person> list = new ArrayList<>();
		list.add(param);
		list.add(pers);
		list.add(perso);
		list.add(person);
		PersonComper pComper = new PersonComper();
		Collections.sort(list, pComper);// 集合工具类排序list
		for (Person per : list) {
			System.out.println(per.toString());
		}
		// 使用set
		Set<Person> set = new TreeSet<>(pComper);// 使用TreeSet的构造方法传入Comparator的接口实现,因为treeSet是唯一实现SortedSet接口,可以自动实现排序,但是效率低些
		set.add(param);
		set.add(pers);
		set.add(perso);
		set.add(person);
		System.out.println(set.size() + "--->" + perso.equals(param)
				+ perso.equals(pers) + perso.equals(person));
		for (Person p : set) {
			System.out.println(p.toString());
			// 此处只有三个输出因为:TreeSet判断两个对象不相等的标准是:两个对象通过equals方法比较返回false,
			// 或通过compareTo(Object
			// obj)比较没有返回0——即使两个对象时同一个对象,TreeSet也会把它们当成两个对象进行处理。
			// 因为上面的param和perso比较时候没有返回0所以认为是同一个对象,如果使用hashSet就会有四个输出。
		}
		// 使用 map
		Map<Person, String> map = new TreeMap<>(pComper);// 使用TreeSet的构造方法传入Comparator的接口实现,可以自动实现排序,但是效率低些
		map.put(pers, "中国");
		map.put(perso, "安徽");
		map.put(param, "池州");
		map.put(person, "阜阳");
		for (Entry<Person, String> mapenEntry : map.entrySet()) {
			System.out.println(mapenEntry.getKey() + "--->"
					+ mapenEntry.getValue());
			// 此处输出的也为三个,比较对象相同方法同TreeSet一样
		}

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