黑马程序员技术交流社区

标题: 求助:关于哈希值的问题 [打印本页]

作者: 蔡增辉    时间: 2013-5-18 10:20
标题: 求助:关于哈希值的问题
本帖最后由 蔡增辉 于 2013-5-18 21:48 编辑

代码如下,发现str1==str2返回值是false,这个我是理解的;
但是打印它们的哈希值怎么会一致?
哈希值不就是地址值吗?==比较的不就是地址吗?

1.PNG (5.66 KB, 下载次数: 0)

1.PNG

作者: 神之梦    时间: 2013-5-18 10:36
本帖最后由 神之梦 于 2013-5-18 10:40 编辑

String不懂{:soso_e113:}

作者: 袁梦希    时间: 2013-5-18 12:43
如果问题已经解决了,那么大家请把帖子的类型改为“已解决”,在自己帖子的左下角点编辑,然后选择帖子的分类进行改正。{:soso_e163:}

作者: 无妄无涯    时间: 2013-5-18 16:06
String类重写了equals和hashCode方法。用“==”比较str1和str2时,比较的是两者的地址值,所以返回false,这部分你已经知道。由于String类改写了hashCode方法,hashCode实际上是根据字符串的value产生的,由于str1和str2的内容相等,因此这时他们两者的哈希值也就相等了。
作者: 蔡增辉    时间: 2013-5-18 16:13
无妄无涯 发表于 2013-5-18 16:06
String类重写了equals和hashCode方法。用“==”比较str1和str2时,比较的是两者的地址值,所以返回false, ...

现在有点晕了,哈希值和地址值是同一个东西吗?
作者: 无妄无涯    时间: 2013-5-18 16:39
蔡增辉 发表于 2013-5-18 16:13
现在有点晕了,哈希值和地址值是同一个东西吗?

原本哈希值是地址值,但是String类对hashCode方法进行了改写,哈希值不再是地址值而是由字符串的内容计算出来的一个值,既然字符串的内容一样,你调用String下的hashCode方法得到的哈希值自然是一样的。这样懂了吗?
作者: 不胖的胖子    时间: 2013-5-18 16:56

     1hashCode值 和 对象的地址 二者的联系和区别
                 对于自定义类
                 当新建一个自定义类,它会继承hashCode方法。
                 当我们调用hashCode方法时,会返回一个int
                 数据?这个数据是怎么得来的了?它与很多内容
                 相关,与这个对象的内存地址,对象上的成员
                 变量等都有关,这些都参与了哈希值的运算。所以
                 可以明确的是:哈希值不是对象的地址。怎样判断
                 引用是否相同?其实是比较引用指向的地址是否
                 相同。
                 如果简单的认为hashCode返回的值是地址值,那么
                 覆盖此方法,使其返回值都相同,那么所有新建
                 的对象不是都相同吗?显然不是。
2equals方法到底在比什么
   很多地方都提到了 equals方法与 == 的区别 , == 这个操作符
java固有的操作符,它的比较方式是固定的:基本类型数值比较,
引用类型比较。都有自己的规定和计算方法。 但是对于equals方法,
它是一个方法,所有类继承自Object类。由于重载的原因,使其成为
一个很灵活的比较方法,你可以按自己的想法去指定两个对象如何相等。
这时需要关心的是,查看谁的equals方法:谁调用查看谁,看它指定的
比较方式。
看下面一段代码:
public class MyHashSet
{
        public static void main(String args[]){
           E element = new E();
           E element2 = new E();
           String str = new String("这不合逻辑!");
      
           sop(element.hashCode());
           sop(element2.hashCode());
           sop(str.hashCode());//String 覆盖了hashCode方法,有自己计算哈希值的方法
           sop(element == element2);
           sop(element.equals(element));
           //sop(element == str);这句在编译时就无法通过
           sop(element.equals(str));
        }
        public static void sop(Object obj){
                System.out.println(obj);
        }
}
class E
{
        public boolean equals(Object obj){
                //你可以很不负责的让两个对象“相等”
                return true;
        }
        public int hashCode(){
                return 60;
        }
}
---------- 运行java程序 ----------
60
60
-342913705
false
true
True
从运行结果来看:== 检查的是两个对象的地址值是否相同,对于不同类型在编译时就会出错。
而指定的equals方法,理论上可以使任何对象“相等”,不过这种相等并无实际意义。
有了以上内容做铺垫,我们在来看看HashSet是如何保证元素的唯一性,就比较清楚了。
你可以把HashSet集合看成是一个装东西的容器:玻璃瓶,罐子都行。只是在装入东西时,要按这个容器的规则进行。
当我们新建一个对象,以自定义对象为例。因为有hashCode方法,这个对象总会产生一个哈希值,不管你的自定义类覆盖hashCode方法与否。
当我们向HashSet容器放元素(或者对象,其实是对象的引用)时,元素放在哪是一个问题。HashSet这个集合的特点是,每个元素都有哈希值,依据元素的哈希值,决定存放位置。


作者: 不胖的胖子    时间: 2013-5-18 16:58
由于字数限制,还有一部分在这里(这段总结本来是写在HashSet集合中的),看到楼主在这里提到这个问题,我就把整个的贴上来了。
在进行比较时,先调用的是hashCode方法。
当向HashSet集合中存储元素时,新添加的元素会与已存入的元素做一一比较:
   如果比较的两个元素通过自身hashCode方法算出的哈希值相同,进一步调用    自身的equals方法,如果equals方法返回值为真,那么这两个元素被判定为    同一元素,如果equals方法返回假,两个元素判定为不同。只是在集合中存    储位置上有一个关联。
   如果比较的两个元素通过自身的hashCode方法算出的哈希值不同,那么不调    用equals方法,也就判定这两个元素不同。
所以可以进行实验:
自定义一个类,不覆盖hashCode方法和equals 方法。那么它会沿用Object类中计算哈希值的方法和equals方法。由于hashCode方法的实现很复杂(目前仍未看懂),但是其计算方式可以最大程度防止出现相同哈希值相同。
至于equals方法就比较简单,我们可以看看:
public boolean equals (Object obj){
      return this == obj;
}也就是检查其实否为同一对象。
看代码如何实现:
import java.util.HashSet;
public class MyHashSet2
{
public static void main(String args[]) throws Exception{
HashSet con = new HashSet();
con.add(new Student(21,"Niki"));
con.add(new Student(24,"Caesar"));
con.add(new Student(22,"HanMeiMei"));
con.add(new Student(24,"Caesar"));
sop(con.size());
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
}
class Student
{
private int age;
private String name;
public Student(int age,String name){
super();
this.age = age;
this.name = name;
}
}
---------- 运行java程序 ----------
4
通过结果可以看出,四个Student对象都存入到集合中了。
现在的需求变了,当学生的姓名和年龄相同时,就判定为相同对象(肯定不能用 == ,它是在比较地址值,当新建对象时,就会为其开辟存储空间,这种物理空间肯定不相同)。如何改写hashCode方法了??原则上hashCode的计算公式中要包含作为比较标准的那些参数:此处是年龄和姓名。
看看代码是如何实现的:
import java.util.HashSet;
public class MyHashSet2
{
public static void main(String args[]) throws Exception{
HashSet con = new HashSet();
con.add(new Student(21,"Niki"));
con.add(new Student(24,"Caesar"));
con.add(new Student(22,"HanMeiMei"));
con.add(new Student(24,"Caesar"));
sop(con.size());
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
}
class Student
{
private int age;
private String name;
public Student(int age,String name){
super();
this.age = age;
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public boolean equals(Object obj){ //指定比较方式
if(!(obj instanceof Student))
return false;
Student st = (Student)obj;
return this.name.equals(st.name) && this.age == st.age;
}
public int hashCode(){ //用年龄和姓名做参数计算哈希值
        return (name.hashCode()+age*39);
}
}
---------- 运行java程序 ----------
3
输出完成 (耗时 0 秒) - 正常终止
看到输出结果是3,证明按照指定的方式实现了元素的存储。
作者: 不胖的胖子    时间: 2013-5-18 17:01
写的可能不是很好  但是重点希望楼主看到 地址值和hashCode方法算出的值得联系和区别。
作者: 殇_心。    时间: 2013-5-18 21:19
如果问题已解决,请及时修改分类,否则继续提问,谢谢合作!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2