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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

直接上代码,此处省略1000字,疑问全在注释中。
  1. import java.util.HashSet;

  2. class Demo {
  3.    
  4.     public static void main(String[] args) {
  5.         
  6.         //===================这是我能理解=========================
  7.         
  8.         String str1 = "100";               // str1指向了常量区的100
  9.         String str2 = "100";               // str2指向了常量区的100
  10.         String str3 = new String("100");   // str3指向了堆区的对象
  11.         String str4 = new String("100");   // str4指向了堆区的对象
  12.         StringBuilder str5 = new StringBuilder("100");   // str5指向了堆区的对象
  13.         StringBuilder str6 = new StringBuilder("100");   // str6指向了堆区的对象
  14.         System.out.println(str1 == str2);  // true, 地址值相同
  15.         System.out.println(str3 == str4);  // false, 地址值不同
  16.         System.out.println(str5 == str6);  // false, 地址值不同
  17.         
  18.         //=====================下面的这些不能理解=============================
  19.         
  20.         HashSet<String> hs1 = new HashSet<String>();
  21.         hs1.add(str1);
  22.         hs1.add(str2);
  23.         System.out.println(hs1);            // 这里打印出[100],str1和str2指向同一个对象只能添加一个,这个很好理解
  24.         
  25.         HashSet<String> hs2 = new HashSet<String>();
  26.         hs2.add(str3);
  27.         hs2.add(str4);
  28.         System.out.println(hs2);            // 这里打印出[100]就很奇怪了,str3和str4明明是不同的地址,却只成功添加一个?
  29.         
  30.         HashSet<StringBuilder> hs3 = new HashSet<StringBuilder>();
  31.         hs3.add(str5);
  32.         hs3.add(str6);
  33.         System.out.println(hs3);            // [100, 100], 这里能够理解,因为我觉得地址值不同的2个对象都可以被添加进HashSet。
  34.         
  35.         // 问题:为什么堆区的2个String对象只成功添加了1个,而StringBuffer和StringBuilder都可以添加2个?
  36.     }
  37. }
  38.    
复制代码
求解释。

6 个回复

倒序浏览
我也是新手,回答不一定准确(我甚至都没见过什么 StringBuilder~~),但是我知道有一点你理解错了,HashSet录入的时候并不是根据的地址值来判断数据唯一性,而是录入对象的“哈希值”。

看下你这段代码在Eclipse里debug的变量属性,可以看到str3和str4的hash实际上是相同的(虽然value的地址值不同),这是Sting本身复写了
hashCode()
的结果(你可以去看看String的源码),使得相同内容的两个String对象返回的hash值相同。
回复 使用道具 举报 1 0
debuggerx01 发表于 2016-3-17 04:31
我也是新手,回答不一定准确(我甚至都没见过什么 StringBuilder~~),但是我知道有一点你理解错了,HashSe ...

你也可以自己debug一下试试看,看看最后hs2和hs3的内容,然后你会发现,hs2里存的实际是String对象对应的字符数组,而hs3存的则是StringBuilder对象本身~~
回复 使用道具 举报
HashSet判断是不是重复元素的方法是依靠hashCode和equals方法
因为StringBuilder并没有重写这两个方法所以会使用Object类的hashCode和equals方法
Object类的hashCode方法可以理解为是由内存地址值生成的一串东西,地址不同不般不同
Object类的equals方法原码直是接返回两个对象是否相等即return (this == obj);

比较时先走hashCode方法比较是不是指向同一块内存,相同时会走equals比较内容是否相同

这里的StringBuilder因为new了两个不同的对象,所以走hashCode方法时就会判断为是两个不同的对象,所以不会认为是重复元素
回复 使用道具 举报
原来如此,我按照你说的debug调试之后,发现str3和str4的hash值果然是一样的:

查看String.class中的hashCode源码如下:
  1. public int hashCode() {
  2.     int h = hash;
  3.     if (h == 0 && value.length > 0) {
  4.         char val[] = value;

  5.         for (int i = 0; i < value.length; i++) {
  6.             h = 31 * h + val[i];
  7.         }
  8.         hash = h;
  9.     }
  10.     return h;
  11. }
复制代码
可是不知道怎么算出48625这个值的。
不过根据HashSet的添加规则——当hashCode相同的时候再使用equals比较2个对象是否相等。然后我又去查看equals的源码:
  1. public boolean equals(Object anObject) {
  2.     if (this == anObject) {
  3.         return true;
  4.     }
  5.     if (anObject instanceof String) {
  6.         String anotherString = (String) anObject;
  7.         int n = value.length;
  8.         if (n == anotherString.value.length) {
  9.             char v1[] = value;
  10.             char v2[] = anotherString.value;
  11.             int i = 0;
  12.             while (n-- != 0) {
  13.                 if (v1[i] != v2[i])
  14.                         return false;
  15.                 i++;
  16.             }
  17.             return true;
  18.         }
  19.     }
  20.     return false;
  21. }
复制代码
equals方法果然是将字符串拆分为字符数组——char[],然后使用!=逐个比较字符值是否不同,很显然str3和str4完全相同,最后equals方法return true。Hashset添加str4失败原来在equals方法这里。
太谢谢你了,我以前都不怎么使用eclipse的调试,完全凭猜想,今天受教了。









回复 使用道具 举报
好贴要顶啊!
回复 使用道具 举报
zx7660 中级黑马 2016-3-23 08:51:19
7#
学习了....
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马