A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 张晋瑜 中级黑马   /  2013-2-22 13:14  /  2003 人查看  /  8 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 zhangjinyu1991 于 2013-2-22 13:56 编辑

大家看看,为什么这两对象的哈兮码竟然相同呀?一个是String对象一个是HashSet对象,这,这是为什么呀?
  1. public static void main(String[] args) {
  2.                 // TODO Auto-generated method stub
  3.                 String s1=new String("zhangjinyu");
  4.                 String s2=new String("zhangjinyu");
  5.                 System.out.println("s1==s2:"+(s1==s2));
  6.                 System.out.println("s1.equals(s2):"+s1.equals(s2));
  7.                 System.out.println("s1.hashCode()==s2.hashCode():"+(s1.hashCode()==s2.hashCode()));
  8.                 System.out.println("s1.hashCode():"+s1.hashCode());// String类重写了hashCode()
  9.                 System.out.println("s2.hashCode():"+s2.hashCode());
  10.                 Set hash_set=new HashSet();
  11.                 hash_set.add(s1);
  12.                 hash_set.add(s2);
  13.                 System.out.println(hash_set);
  14.                 System.out.println("hash_set.hashCode():"+hash_set.hashCode());// hash_set运行的hashCode()是AbstractSet中实现的
  15.         }
复制代码
运行结果:
s1==s2:false
s1.equals(s2):true
s1.hashCode()==s2.hashCode():true
s1.hashCode():1943454207
s2.hashCode():1943454207

[zhangjinyu]
hash_set.hashCode():1943454207

8 个回复

倒序浏览

回帖奖励 +2

本帖最后由 夏振博 于 2013-2-22 13:53 编辑

既然发现一样,你就可以看源码  它们的hashCode()是怎么得到的

回复 使用道具 举报
1.这两个是不同的对象,为什么hancode却一样呢?因为string里面的hashcode是用字符串值计算出来的,确切的说不是地址值。那么也就是说字符串相同,则hashcode值相同
2.这里面一定要弄清楚,string是object的子类,string覆盖了hashcode的方法,专门用来计算字符串的值
3.如果要判断对象是否相同用 s1==s2就行了!
不知道这样说清楚没?
回复 使用道具 举报
s1==s2:false
答:用了new 这个词的就是一个单独的对象,这里是两个对象,对象没有相等的(只能说他们的内容相等或地址相等),就好比两个人,他们什么都一样,你能说那是一个人,你也只能说这个人的什么特点相同吧,你这么写string s1 ="sd";String s2="sd";那么s1==s2为true,
s1.equals(s2):true
答:这里比较的是内容,都是zhangjinyu,所以true;

s1.hashCode()==s2.hashCode():true
答:这里比较的是内存地址值,所以相同
s1.hashCode():1943454207
s2.hashCode():1943454207
[zhangjinyu]
答:hashSet集合判断两个对象是否相等,是通过Hashcode()和equals()来判断的,那么这里就可以判断他们是相等的
hash_set.hashCode():1943454207
回复 使用道具 举报
胥文 发表于 2013-2-22 13:49
s1==s2:false
答:用了new 这个词的就是一个单独的对象,这里是两个对象,对象没有相等的(只能说他们的内 ...

额,我问的是为什么一个是String对象,一个HashSet对象,他们的hashCode()相同
回复 使用道具 举报

看了jdk源码,就是有些没看懂

本帖最后由 zhangjinyu1991 于 2013-2-22 14:03 编辑
夏振博 发表于 2013-2-22 13:43
既然发现一样,你就可以看源码  它们的hashCode()是怎么得到的

/** HashMap.class **/
final int hash(Object k) {
        int h = 0;
        if (useAltHashing) {
            if (k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }
            h = hashSeed;
        }

        h ^= k.hashCode();        // 竟然执行了这里,你看哈,k.hashCode()不就已近和s1的相同了吗,为什么还要后面的算法呢?

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
回复 使用道具 举报
我是这么理解的,不知道对不对,作为参考
HashSet
它依赖元素类型hashCode()和equals()来保证唯一性,如果hashCode()相等,再使用equals()比较,如果hashCode()不相等,就不再使用equals()比较。
你存进去的是字符串,字符串重写了hashCode()和equals(), 就是说你的集合里只存进去了一个"zhangjinyu"
HashSet从类 java.util.AbstractSet 继承了hashCode(),它是每个元素hashCode()的叠加,正好你里面只有一个,如果再向集合添加不同元素 它们的hashCode()就不一样了
AbstractSet源码:
  1. public int hashCode() {
  2.         int h = 0;
  3.         Iterator<E> i = iterator();
  4.         while (i.hasNext()) {
  5.             E obj = i.next();
  6.             if (obj != null)
  7.                 h += obj.hashCode();
  8.         }
  9.         return h;
  10.     }
复制代码

点评

主要是不知道这个算法叫什么名字,所以没办法查资料,只有个公式没办法  发表于 2013-2-22 15:13
你的理解其实很对,我看了几遍后终于看懂这大部分相关源码,现在就是那个劳什子算法不知道怎么回事 就是这个:h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h..   发表于 2013-2-22 15:11

评分

参与人数 1黑马币 +15 收起 理由
张晋瑜 + 15 很给力!

查看全部评分

回复 使用道具 举报
这是我整理出来的结果,大家要是有兴趣可以看一看:
  1. public class TestHashSet {

  2.         /**
  3.          * @param args
  4.          */
  5.         public static void main(String[] args) {
  6.                 // TODO Auto-generated method stub
  7.                 String s1=new String("zhangjinyu");
  8.                 String s2=new String("zhangjin");
  9.                 System.out.println("s1==s2:"+(s1==s2));
  10.                 System.out.println("s1.equals(s2):"+s1.equals(s2));
  11.                 System.out.println("s1.hashCode()==s2.hashCode():"+(s1.hashCode()==s2.hashCode()));
  12.                 System.out.println("s1.hashCode():"+s1.hashCode());// String类重写了hashCode()
  13.                 System.out.println("s2.hashCode():"+s2.hashCode());
  14.                 Set hash_set=new HashSet();
  15.                 hash_set.add(s1);
  16.                 hash_set.add(s2);
  17.                 System.out.println(hash_set);
  18.                 System.out.println("hash_set.hashCode():"+hash_set.hashCode());// hash_set运行的hashCode()是AbstractSet中实现的
  19.         }
  20. /**
  21. * 来浅析一下为什么hash_set.hashCode()==s1.hashCode()==s2.hashCode()?
  22. * 答案: 我改了下s2,发现就不会相等了,原来是这样:HashMap中的hashCode的hash算法与他的每个元素有关,上面之所以相同是因为hash_set中只有一个元素
  23. * //HashSet.class
  24. * private transient HashMap<E,Object> map;
  25. * private static final Object PRESENT = new Object();        // 额,是个常量,并且是final型的,这是jdk为了方便将value设为常量PRESET,因为value是可以重复的。
  26. * public boolean add(E e) {
  27.         return map.put(e, PRESENT)==null;        // 所以说,调用一下HashSet的add方法,实际上就是想HashMap中增加一行(key-value)记录;
  28.     }
  29.     ** HashMap.class **
  30.    public V put(K key, V value) {
  31.         if (key == null)
  32.             return putForNullKey(value);
  33.         int hash = hash(key);        // 这里调用hash()方法计算
  34.         int i = indexFor(hash, table.length);        // 用key的hash码的int值和table的长度(也就是key-value对数)进行“与”计算
  35.         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  36.         // 这个table有讲究,他是transient Entry<K,V>[] table;
  37.          * 而Entry是实际上真正存放key-value对的地方
  38.             Object k;
  39.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 如果存在相同的对象,则不添加,而是进行替换工作
  40.                 V oldValue = e.value;
  41.                 e.value = value;
  42.                 e.recordAccess(this);
  43.                 return oldValue;
  44.             }
  45.         }
  46.                 // 添加对象
  47.         modCount++;
  48.         addEntry(hash, key, value, i);
  49.         return null;
  50.     }
  51.     ** 计算HashMap中的Key值的hash码 **
  52.     *hash方法就是我们在数据结构中讲的散列函数。它是经过放进HashSet里面的对象作为key得到hashCode码,在进行散列得到的一个整数。        
  53.     final int hash(Object k) {
  54.         int h = 0;
  55.         if (useAltHashing) {
  56.             if (k instanceof String) {
  57.                 return sun.misc.Hashing.stringHash32((String) k);
  58.             }
  59.             h = hashSeed;
  60.         }

  61.         h ^= k.hashCode();        // k.hashCode()用k对象的hashCode()得到hash码

  62.         // This function ensures that hashCodes that differ only by
  63.         // constant multiples at each bit position have a bounded
  64.         // number of collisions (approximately 8 at default load factor).
  65.         h ^= (h >>> 20) ^ (h >>> 12);
  66.         return h ^ (h >>> 7) ^ (h >>> 4);
  67.     }
  68.     **** hash_set.hashCode() ****
  69.     public int hashCode() {        // 这里,说明hash_set.hashCode()的值为hash_set中每个对象的hashCode的和
  70.         int h = 0;
  71.         Iterator<E> i = iterator();
  72.         while (i.hasNext()) {
  73.             E obj = i.next();
  74.             if (obj != null)
  75.                 h += obj.hashCode();
  76.         }
  77.         return h;
  78.     }
  79. * */
  80. }
复制代码
回复 使用道具 举报
李桐 高级黑马 2013-2-22 16:08:49
9#
首先你要知道你的hash_set集合里只存了一个字符串,因为你定义的两个字符串是相同的:::s1.equals(s2):true
api文档1::
其次就是set计算hashcode的方法了
api文档2::
这就能解释为什么string的hashcode和set的hashcode是相同的了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马