学习地址:
1、我们为什么需要重写hashCode()方法和equals()方法
首先equals() 与hashCode()方法是Java父类Object定义的方法;
源代码中是如此定义的两个方法:
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
Java中的超类Object类中定义的equals()方法是用来比较两个引用所指向的对象的内存地址是否一致
Object类中的hashCode()方法,用native关键字修饰,说明这个方法是个原生函数,也就说这个方法的实现不是用java语言实现的,是使用c/c++实现的,并且被编译成了DLL,由java去调用,jdk源码中不包含。对于不同的平台它们是不同的,java在不同的操作系统中调用不同的native方法实现对操作系统的访问,因为java语言不能直接访问操作系统底层,因为它没有指针。
Java的API文档对hashCode()方法做了详细的说明,这也是我们重写hashCode()方法时的原则【Object类】
重点要注意的是:
a. 在java应用程序运行时,无论何时多次调用同一个对象时的hsahCode()方法,这个对象的hashCode()方法的返回值必须是相同的一个int值
b. 如果两个对象equals()返回值为true,则他们的hashCode()也必须返回相同的int值
c. 如果两个对象equals()返回值为false,则他们的hashCode()返回值也必须不同
2、在什么情况下需要重写hashCode()方法和equals()方法
我们在定义类时,我们经常会希望两个不同对象的某些属性值相同时就认为他们相同,所以我们要重写equals()方法,按照原则,我们重写了equals()方法,也要重写hashCode()方法,要保证上面所述的b,c原则;所以java中的很多类都重写了这两个方法,例如String类,包装类
当我们自定义的一个类,想要把它的实例保存在集合中时,我们就需要重写这两个方法;
如果使用自定义的类来作为TreeMap中的key值,且想让TreeMap能够良好的工作,则必须重写自定义类中的equals()方法,TreeMap中判断相等的标准是:两个key通过equals()方法返回为true,并且通过compareTo()方法比较应该返回为0。
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
3、如何重写这两个方法
以集合类TreeMap为例:
package com.basic.util;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap;
public class BasicTreeMap {
public static void main(String[] args) {
Student s1 = new Student("lpn", 27, 61.38);
Student s2 = new Student("tbn", 28, 62.211);
Student s3 = new Student("xyz", 29, 63.345);
Student s4 = new Student("qwe", 30, 64.986);
TreeMap<Student, Integer> tMap = new TreeMap<Student, Integer>(new StudentCmp());
tMap.put(s1, 1);
tMap.put(s4, 4);
tMap.put(s2, 2);
tMap.put(s3, 3);
tMap.put(s1, 100);
tMap.put(s3, null);
tMap.remove(s3);
Iterator<Entry<Student, Integer>> iterr = tMap.entrySet().iterator();
while (iterr.hasNext()) {
Entry<Student, Integer> e = iterr.next();
if (e.getKey().equals(s4)) {
iterr.remove();
}
}
/** Caused Exception java.util.ConcurrentModificationException
for (Entry<Student, Integer> e : tMap.entrySet()) {
if (e.getValue() == 2) {
tMap.remove(e.getKey());
}
}
*/
Iterator<Entry<Student, Integer>> iter = tMap.entrySet().iterator();
while (iter.hasNext()) {
Entry<Student, Integer> e = iter.next();
System.out.println(e.getKey().toString() + " " + e.getValue());
}
for (Entry<Student, Integer> e : tMap.entrySet()) {
System.out.println(e.getKey().toString() + " " + e.getValue());
}
}
}
class Student implements Comparable<Student> {
private String name;
private int age;
private double height;
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
/**
* @return the height
*/
public double getHeight() {
return height;
}
/**
* @param height the height to set
*/
public void setHeight(double height) {
this.height = height;
}
@Override
public int hashCode() {
int result = 1;
final int prime = 31;
result = result*prime + name == null ? 0 : name.hashCode();
result = result*prime + age;
result = result*prime + (int) Double.doubleToLongBits(height);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (null == obj) {
return false;
}
if (!(obj instanceof Student)) {
return false;
}
Student s = (Student) obj;
if (name != null) {
if (s.name == null || !name.equals(s.name)) {
return false;
}
} else {
if (s.name != null) {
return false;
}
}
if (age != s.age) {
return false;
}
if (Double.doubleToLongBits(height) != Double.doubleToLongBits(s.height)) {
return false;
}
return true;
}
@Override
public String toString() {
String str = "";
str = name + " " + age + " " + height;
return str;
}
@Override
public int compareTo(Student o) {
if (this.age < o.age) {
return -1;
} else if (this.age > o.age) {
return 1;
}
return 0;
}
}
class StudentCmp implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
if (o2.getAge() < o1.getAge()) {
return -1;
} else if (o2.getAge() > o1.getAge()) {
return 1;
}
return 0;
}
}
最后某些特殊情况下,可能需要基于value排序,下面的代码实现了基于value排序,并考虑了value为null的情况
List<Entry<Student, Integer>> lEntry = new ArrayList<Entry<Student, Integer>>(tMap.entrySet());
Collections.sort(lEntry, new Comparator<Entry<Student, Integer>> () {
@Override
public int compare(Entry<Student, Integer> o1, Entry<Student, Integer> o2) {
if (o1.getValue() != null && o2.getValue() != null) {
return o2.getValue() - o1.getValue();
} else if (o1.getValue() == null) {
return 1;
} else {
return -1;
}
}
});