一、hashCode方法与HashSet类
- 如果想查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?
- 通常是逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时
- 则停止继续查找并返回肯定的信息,否则,返回否定的信息
- 如果一个集合中有很多个元素,譬如有一万个元素,并且没有包含要查找的对象时,
- 则意味着你的程序要从该集合中取出一万个元素进行逐一比较才能得到结论
- 有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,
- 每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,
- 根据一个对象的哈希码就可以确定该对象应该存储在哪个区域
- HashSet就是采用哈希算法存取对象的集合,
- 它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域
- Object类中定义了一个hashCode方法来返回每个java对象的哈希码,
- 当从HashSet集合中查找某个对象时
- java系统首先调用对象的hashCode方法获得该对象的哈希码
- 然后根据哈希码找到响应的存储区域
- 最后取出该存储区域内的每个元素与该对象进行equals方法比较,
- 这样不用遍历集合中的所有元素就可以得到结论
- 可见,HashSet集合具有很好的对象检索性能
- 但是,HashSet集合存储对象的效率相对要低些,
- 因为向HashSet集合中添加一个对象时,要先计算出对象的哈希码和根据这个哈希码确定对象在集合中的存放位置
- 为了保证一个类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals方法比较的结果相等时
- 他们的哈希码也必须相等,
- 也就是说,如果obj1.equals(obj2)的结果为true,那么以下表达式的结果也要为true:
- obj1.hashCode() == obj2.hashCode()
- 如果一个类的hashCode方法没有遵循上述要求,
- 那么,当这个类的两个实例对象用equals方法比较了,也就可能被存储进HashSet集合中了,
- Object类中的hashCode方法不能满足对象被存入到HashSet中的要求,
- 因为他的返回值通过对象的内存地址推算出来的
- 同一个对象在程序运行期间的任何时候返回的哈希值都是始终不变的
- 所以,只要是两个不同的实例对象,即使他们的equals方法比较结果相等
- 他们默认的hashCode方法的返回值是不同的
- 通常来说,一个类的两个实例对象用equals方法比较的结果相等时,他们的哈希码必须相等,但反之则不成立
- 即:equals方法比较结果不相等的对象可以有相同的哈希码
- 或者说哈希码相同的两个对象的equals方法比较的结果可以不等
- 例如:字符串"BB"和"Aa"的equals方法比较结果肯定不相等,但他们的hashCode值却相等
- 当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了
- 否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了
- 在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果
- 这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄漏
二、示例
import java.util.*;
public class HashCodeTest {
public static void main(String[] args) {
Collection col = new HashSet<Student>();
Student stu1 = new Student("zhangsan",20);
Student stu2 = new Student("lisi",18);
Student stu3 = new Student("wangwu",25);
Student stu4 = new Student("zhangsan",20);
col.add(stu1);
col.add(stu2);
col.add(stu3);
col.add(stu4);
col.add(stu1);
/**
* 改变参与hashCode运算的属性的值后,hashCode就变了
*/
stu1.age = 15;
System.out.println(col.contains(stu1));//false
col.remove(stu1);//删不了stu1,造成了内存泄漏
System.out.println(col.size());//3
Iterator<Student> it = col.iterator();
while(it.hasNext())
{
Student stu = it.next();
System.out.println(stu);
}
}
}
class Student
{
String name;
int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String toString()
{
return name+":"+age;
}
}