hashcode 和equals 及HashSet判断对象相等

hashcode()和equals()及HashSet判断对象相等。
1. 首先equals()和hashcode()这两个方法都是从object类中继承过来的。
public boolean equals(Object obj)
    指示某个其他对象是否与此对象“相等”。
    equals 方法在非空对象引用上实现相等关系:
     * 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
     * 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
     * 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,
      并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
     * 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,
      前提是对象上 equals 比较中所用的信息没有被修改。
     * 对于任何非空引用值 x,x.equals(null) 都应返回 false。 
    注意:以上这五点是重写equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果
    Object 类的 equals 方法实现的是对象上差别可能性最大的相等关系:
    即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,
    此方法才返回 true(x == y 具有值 true)。
  也就是说equals()方法在object类中定义如下:
    public boolean equals(Object obj) {
  return (this == obj);
  }
  对两个对象的地址值进行的比较(即比较引用是否相同)。
    注意1:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,
    该协定声明相等对象必须具有相等的哈希码。
 但是我们必需清楚,当String 、Math、还有Integer、Double。。。。等这些封装类在使用equals()方法时,
 已经覆盖了object类的equals()方法。比如在String类中如下:
 public boolean equals(Object anObject) {
  if (this == anObject) {
   return true;
  }
  if (anObject instanceof String) {
    String anotherString = (String)anObject;
    int n = count;
    if (n == anotherString.count) {
    char v1[] = value;
    char v2[] = anotherString.value;
    int i = offset;
    int j = anotherString.offset;
    while (n-- != 0) {
     if (v1[i++] != v2[j++])
      return false;
    }
    return true;
   }
  }
  return false;
 }
 很明显,这是进行的内容比较,而已经不再是地址的比较。依次类推Double、Integer、Math等等这些类都是重写了equals()方法的,
 从而进行的是内容的比较。当然了基本类型是进行值的比较,这个没有什么好说的。
public int hashCode()
    返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。
    hashCode 的常规协定是:
        * 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,
         前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,
         该整数无需保持一致。
        * 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
        * 以下情况不是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,
         那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。
         但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 
    实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。
    (这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。) 。
    当然我们可以在自己写的类中覆盖hashcode()方法,
    比如String、 Integer、Double。。。。等等这些类都是覆盖了hashcode()方法的。
    例如在String类中定义的hashcode()方法如下:
  public int hashCode() {
   int h = hash;
   if (h == 0) {
    int off = offset;
    char val[] = value;
    int len = count;
        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
       hash = h;
   }
   return h;
  }
 解释一下这个程序(String的API中写到):
 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 使用 int 算法,这里 s 是字符串的第 i 个字符,n 是字符串的长度,^ 表示求幂。(空字符串的哈希码为 0。)
 这里我们根据hashCode的常规协定首先要明白一个问题:
  一,equals()相等的两个对象,hashcode()一定相等.
  二,equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。
   换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。
  三,反过来:hashcode()不等,一定能推出equals()也不等(因为如果equals()相等的话就和第一条矛盾了);
  hashcode()相等,equals()可能相等,也可能不等。
2.hashset是根据什么原理来存取对象的呢?
 在hashset中不允许出现重复对象,元素的位置也是不确定的。
 在hashset中判断两个对象是否重复的规则是:
  1),判断两个对象的hashCode是否相等.

如果不相等,认为两个对象也不相等,完毕;    如果相等,转入2)
  (这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,
   所以我们这里将其做为必需的。后面会重点讲到这个问题。)
  2),判断两个对象用equals运算是否相等
        如果不相等,认为两个对象也不相等;     如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)
 为什么是两条准则,难道用第一条不行吗?不行,因为前面已经说了,hashcode()相等时,equals()方法也可能不等,
 所以必须用第2条准则进行限制,才能保证加入的为非重复元素。
 那么为什么不直接用equals()方法判断呢?
 我想是因为效率的原因。很容易让hashcode()的效率比equals()高些,
 因为它不必每次都重新算hash值,具体参照上面String的hashcode()实现
例1:
 public static void main(String args[]){
 String s1=new String("zhaoxudong");
 String s2=new String("zhaoxudong");
 System.out.println(s1==s2);//false
 System.out.println(s1.equals(s2));//true
 System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()
 System.out.println(s2.hashCode());
 Set hashset=new HashSet();
 hashset.add(s1);
 hashset.add(s2);
 Iterator it=hashset.iterator();
  while(it.hasNext())
  {
    System.out.println(it.next());
  }
最后在while循环的时候只打印出了一个”zhaoxudong”。
输出结果为:

false
true
-967303459
-967303459

zhaoxudong
这是因为String类已经重写了equals()方法和hashcode()方法,所以在根据上面的第1.2条原则判定时,
hashset认为它们是相等的对象,是重复添加。
例2:
 import java.util.*;
 public class HashSetTest
 {
  public static void main(String[] args)
  {
   HashSet hs=new HashSet();
   hs.add(new Student(1,"zhangsan"));
   hs.add(new Student(2,"lisi"));
   hs.add(new Student(3,"wangwu"));
   hs.add(new Student(1,"zhangsan"));
  
   Iterator it=hs.iterator();
   while(it.hasNext())
   {
    System.out.println(it.next());
   }
  }
 }
 class Student
 {
  int num;
  String name;
  Student(int num,String name)
  {
   this.num=num;
   this.name=name;
  }
  public String toString()
  {
    return num+":"+name;
  }
 }     
输出结果为:
 1:zhangsan
 1:zhangsan
 3:wangwu
 2:lisi
问题出现了:为什么hashset添加了相等的元素呢,这是不是和hashset的原则违背了呢?回答是:没有
 因为在根据hashcode()对两次建立的new Student(1,"zhangsan")对象进行比较时,生成的是不同的哈希码值,
 所以hashset把他当作不同的对象对待了,当然此时的 equals()方法返回的值也不等(这个不用解释了吧)。
 那么为什么会生成不同的哈希码值呢?原因就在于我们自己写的Student类并没有重写自己的hashcode()和equals()方法,
 所以在比较时,是继承的object类中的 hashcode()方法,它返回的是和对象地址(引用地址)对应的数,
 使用new方法创建对象,两次生成的当然是不同的对象了,造成的结果就是两个对象的hashcode()返回的值不一样。
 所以根据第一个准则,hashset会把它们当作不同的对象对待,自然也用不着第二个准则进行判定了。
那么怎么解决这个问题呢?
答案是:在Student类中重新hashcode()和equals()方法。
例3:
 class Student
 {
  int num;
  String name;
  Student(int num,String name)
  {
   this.num=num;
   this.name=name;
  }
  public int hashCode()
  {
   return num*name.hashCode();
  }
  public boolean equals(Object o)
  {
   Student s=(Student)o;
   return num==s.num && name.equals(s.name);
  }
  public String toString()
  {
   return num+":"+name;
  }
 }
根据重写的方法,即便两次调用了new Student(1,"zhangsan"),我们在获得对象的哈希码时,获得的哈希码肯定是一样的。
当然根据equals()方法我们也可判断是相同的。所以在向hashset集合中添加时把它们当作重复元素看待了。
所以运行修改后的程序时,我们会发现运行结果是:
1:zhangsan
3:wangwu
2:lisi
可以看到重复元素的问题已经消除。
例4:
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class Test {
 /**
  * @param args
  */
 public static void main(String[] args) {
  HashSet<People> set=new HashSet();
  set.add(new People("robin",1,21));
  set.add(new People("hb",2,20));
  set.add(new People("harry",3,30));
  set.add(null);
  People p4=new People("robin",4,25);
  set.add(p4);
  set.add(new People("yp",5,28));
  for(People p:set)
   System.out.println(p);
 }
}
class People{
 String name;
 int id;
 int age;
 public People(String name,int id)
 {
  this(name,id,0);
 }
 public People(String name,int id,int age)
 {
  this.name=name;
  this.id=id;
  this.age=age;
 }
 public String toString()
 {
  return id+name+age;
 }
 public boolean equals(Object o)
 {
  if(o==null)
   return false;
  if(!(o instanceof People))
   return false;
  People p=(People)o;
  boolean res=name.equals(p.name);
  if(res)
   System.out.println("name "+name+" is double");
  else
   System.out.println(name+" vS "+p.name);
  return res;
 }
 public int hashCode()
 {
  return name.hashCode();
 }
}
结果:

name robin is double
null
1robin21
3harry30
5yp28
2hb20
注意1:当试图添加一个重复元素到hashset时,新元素并不会把旧元素替换掉,
而只是新元素不会添加到hashset(不会抛异常。)

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net

原文地址:https://www.cnblogs.com/siwnchh/p/10465848.html

时间: 2024-10-14 19:18:09

hashcode 和equals 及HashSet判断对象相等的相关文章

!!转!!hashCode与equals的区别与联系

这篇文章写得很好!!! 原文链接:http://blog.csdn.net/afgasdg/article/details/6889383 一.equals方法的作用 1.默认情况(没有覆盖equals方法)下equals方法都是调用Object类的equals方法,而Object的equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象). 2 .要是类中覆盖了equals方法,那么就要根据具体的代码来确定equals方法的作用了,覆盖后一般都是通过对象的内容是否相等来

hashcode()与equals

http://blog.csdn.net/afgasdg/article/details/6889383 一.equals方法的作用 1.默认情况(没有覆盖equals方法)下equals方法都是调用Object类的equals方法,而Object的equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象). 2 .要是类中覆盖了equals方法,那么就要根据具体的代码来确定equals方法的作用了,覆盖后一般都是通过对象的内容是否相等来判断对象是否相等. 没有覆盖equ

【Java】Map杂谈,hashcode()、equals()、HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap

参考的优秀文章: <Java编程思想>第四版 <Effective Java>第二版 Map接口是映射表的结构,维护键对象与值对象的对应关系,称键值对. > hashcode()和equals() hashcode()和equals()即用于识别对象的身份. 在HashMap或类似的实现中,查找一个对象,是通过hashcode()返回的散列值映射到一个范围内的下标,在通过equals()比较此下标连接的链表是否存在相同的对象. 简单来说,hashcode()用于参考.快速定位

scala 判断对象相等/equals

1 package scala_enhance.scalaextends 2 3 import scala.collection.mutable.HashMap 4 5 /** 6 * scala中判断对象相等 7 * 原则: 8 * 如果两个对象相等,那么其hashcode必定相同,所以重写equals方法,要重写hashcode(默认情况下hashcode是根据内存地址计算出来的值) 9 * 但如果hashcode相同(即使用 == 比较为true),却不能证明他们相等 10 * 关于重写e

Object对象详解(三)之hashCode与equals

从学习Java开始,就从各个师兄.各种书籍.各大网站听到.看到,重写equals方法必须重写hashCode方法.重写equals方法必须重写hashCode方法.重写equals方法必须重写hashCode方法. 那么为什么呢?今天就详细剖析一下equals和hashCode! equals方法是比较两个对象实例是否相等.Object中equals方法的描述为: public boolean equals(Object obj) { return (this == obj); } 也就是默认情

对象的比较及hashCode、equals方法的使用

如何进行对象的值的比较呢?如String类型的变量,是靠调用equals方法来比较的,而其它的类似数组或普通的对象直接调用equals方法一般是不可以的,这是因为String类型的变量通过调用equals方法来比较变量是因为String类覆盖了Object的HashCode方法和equals方法.正如String调用equals方法可以比较String对象变量的值是否相等,一般的对象也可以通过覆盖Object类的方法来达到同样的目的的.如下面代码 所示: import java.util.Has

hashCode与equals

很多东西都是大处显积累,小处见功力,来点功力. hashCode跟equals 相伴相生,所以要一起讨论才有意义. 在java中,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,就是说当集合中插入对象时,怎么分辨该对象是否已经存在.按照正常思路,应该是依次进行equals比较,但是其实效率不高,java中的做法是先比较hashCode,如果相同在equals比较,到这里,疑问就出来了,为什么会比较hashCode,hashCode相同的情况下为什么equals为什么还会不同?

关于hashCode与equals

首先我得说明,在我们自己写得类中你可以复写这两个方法,此时从语法的角度来说,他们没关系. 在object中 public native int hashCode(); public boolean equals(Object obj) { return (this == obj); } 两个准则 在java集合中 判定两个对象是否相等需要以下两步; 1 hashCode的值是否相等, 如果不相等,那么不用说,两个对象肯定不相等;如果hashCode的值相等,那么看第二步; 2 equals的值是

Java hashCode() 和 equals()的若干问题

原文:http://www.cnblogs.com/skywang12345/p/3324958.html 本章的内容主要解决下面几个问题: 1 equals() 的作用是什么? 2 equals() 与 == 的区别是什么? 3 hashCode() 的作用是什么? 4 hashCode() 和 equals() 之间有什么联系? 第1部分 equals() 的作用 equals() 的作用是 用来判断两个对象是否相等. equals() 定义在JDK的Object.java中.通过判断两个对