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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© xuemeng 中级黑马   /  2013-5-20 23:20  /  2048 人查看  /  14 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 xuemeng 于 2013-5-21 08:17 编辑

import java.util.HashSet;

public class Demo {
        public static void main(String[] args) {
                HashSet<Person> hs = new HashSet<Person>();
                Person p1 = new Person(5);
                Person p2 = new Person(5);
               //我们都知道, 当hashCode还有equals相同时, 使用HashSet添加元素, 那么只能成功添加一个, 那么按照我之前的理解,既然是p1先添加进去, 那么p2就没有添加进去, 那么当我用
remove( )方法移除p2元素时, 那么应该要么就是空指针异常, 要么即使不是空指针异常, 那集合的长度也应该是1才对, 然而事实上是, 不论是移除p1元素还是p2元素, 最终打印集合长度都是0, 请问这是为什么? 求解释, 求分析??
                hs.add(p1);
                hs.add(p2);
              //  hs.remove(p1);
                  hs.remove(p2);
                System.out.println(hs.size());
        }
}

class Person {
        private int age;

        Person(int age) {
                this.age = age;
        }

        @Override
        public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + age;
                return result;
        }

        @Override
        public boolean equals(Object obj) {
                if (this == obj)
                        return true;
                if (obj == null)
                        return false;
                if (getClass() != obj.getClass())
                        return false;
                Person other = (Person) obj;
                if (age != other.age)
                        return false;
                return true;
        }
}

评分

参与人数 1技术分 +1 收起 理由
Sword + 1

查看全部评分

14 个回复

倒序浏览
没想到,坐等大神解答,今晚一定要解答了呀
回复 使用道具 举报
本帖最后由 刘勇强 于 2013-5-21 00:15 编辑

boolean a= hs.add(p2);
System.out.println(a);

加入上面这两句语句 会输出false,说明set中已存在,没有再添加该元素. set中只有一个元素

boolean b=hs.remove(p2);
System.out.println(b);

加入这两句,会输出true,说明romove了该元素  size就变成0了




                hs.add(p1);
                boolean a= hs.add(p2);
                                System.out.println(a);
                boolean b= hs.remove(p1);
                                System.out.println(b);
                boolean c= hs.remove(p2);
                                System.out.println(c);
                System.out.println(hs.size());

把add 和remove改成上面的代码  输出为:false true false  0   再看看add 和 remove方法的定义,就很容易理解了. api里写的的确有些不足.

评分

参与人数 1技术分 +1 收起 理由
Sword + 1

查看全部评分

回复 使用道具 举报
我的理解是,既然你设置了p1和p2的hashcode相同,那么它们就是同一指针,因为hashset里面不能有重复的元素,所以你添加p2会失败。同理因为他们是同一指针,你remove(p1)或者remove(p2),都是移动的同一个对象,你觉得呢?
回复 使用道具 举报
黑马-许鹏 发表于 2013-5-21 00:17
我的理解是,既然你设置了p1和p2的hashcode相同,那么它们就是同一指针,因为hashset里面不能有重复的元素 ...

这么理解, 对是对, 但是不是正解, 还是未知数!!;P
回复 使用道具 举报
刘勇强 发表于 2013-5-20 23:59
boolean a= hs.add(p2);
System.out.println(a);

让我疑惑的就是既然p2元素都没有添加成功, 那么为什么删除p2元素又能成功了?
回复 使用道具 举报
xuemeng 发表于 2013-5-21 00:26
让我疑惑的就是既然p2元素都没有添加成功, 那么为什么删除p2元素又能成功了? ...

                hs.add(p1);
                boolean a= hs.add(p2);
                                System.out.println(a);  //输出false   p2没有添加成功
                boolean b= hs.remove(p1);
                                System.out.println(b); //输出true   唯一的元素p1移除成功
                boolean c= hs.remove(p2);
                                System.out.println(c);//输出false   因为 里面已经没有元素了
                System.out.println(hs.size());  // 输出 0


这下该懂了吧
回复 使用道具 举报
再来看看API。
addpublic boolean add(E e)如果此 set 中尚未包含指定元素,则添加指定元素。更确切地讲,如果此 set 没有包含满足 (e==null ? e2==null : e.equals(e2)) 的元素 e2,则向此 set 添加指定的元素 e。如果此 set 已包含该元素,则该调用不更改 set 并返回 false。
public boolean remove(Object o)如果指定元素存在于此 set 中,则将其移除。更确切地讲,如果此 set 包含一个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则将其移除。如果此 set 已包含该元素,则返回 true(或者:如果此 set 因调用而发生更改,则返回 true)。(一旦调用返回,则此 set 不再包含该元素)。
Person p1 = new Person(5);
Person p2 = new Person(5);
我承认我第四次回答这样的问题了,还专门发了共享贴,可是还是没人看到!这句可以不看。
下面来看分析:
        我特意把两个方法的API说明都放上去了。
来看 remove的底层逻辑: (o==null ? e==null : o.equals(e)) ,针对这题,remove(p2); o就是p2, o == null? p2有明确的指向,不为空,执行三目表达式的最后一句话,
即 o.equals(e);  针对你的代码即: p2.equals(p1)。由于你改写了Person 类的 equals方法。p2.equals(p1),返回的是true。那么这个元素就被删除了。
仔细把那件话读读:如果此 set 包含一个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则将其移除。HashSet集合中,你满足我的表达式的元素有吗?有,好删除。
问题出在equals方法上了。
如果你把Person的equals方法注释掉,让它继承Object的equals方法。下面是Object类equals方法源码:
public boolean equals(Object obj) {        return (this == obj);    }你会看到 它用 == 去比较两个对象,简单粗暴。这又回到两个原始的问题上了: == 和 equals的区别,hashCode值和对象的地址(内存地址)的区别(下面的链接有)
在这里我帮你去掉,去掉已后,通过add方法也就能添加进去 p1 和 p2 两个对像了。这也就是为什么 equals 要和 hashCode要同时改写。
hashCode 和 equals 的个人总结http://bbs.itheima.com/thread-50885-1-1.html
对于HashSet equals和hashCode是组合形式出现的。



评分

参与人数 1技术分 +1 收起 理由
刘胜寒 + 1

查看全部评分

回复 使用道具 举报
对于你说的空指针异常,你想想 remove是接收一个参数,当接收的参数指向的是一个null的引用时才会空指针异常,但是你remove(p2),p2这个引用由明确的指向,它是没有被添加进HashSet集合,但是它有对象的。这个你可以测试下。我没给你做测试,是因为我的总结帖里也写到了。
回复 使用道具 举报
刘勇强 发表于 2013-5-21 00:36
hs.add(p1);
                boolean a= hs.add(p2);
                                 ...

  如果先移除p2, 那么也是true
回复 使用道具 举报
不胖的胖子 发表于 2013-5-21 05:57
再来看看API。
addpublic boolean add(E e)如果此 set 中尚未包含指定元素,则添加指定元素。更确切地讲, ...

把你分享的链接给我,我给你提升,设置高亮。。
主要是帖子太多了。。帖子沉的也快。。。
回复 使用道具 举报
不胖的胖子 发表于 2013-5-21 06:01
对于你说的空指针异常,你想想 remove是接收一个参数,当接收的参数指向的是一个null的引用时才会空指针异 ...

嗯! 不过关于你的 o == null ? e == null : o.equals(e) 这个代码, 翻源码看了, o关于o这个相关代码比较复杂, 没有看懂, 不过看你后面的解释, 大概意思就是 p2.equals(p1)所以就能好删除元素, 这个明白了!!至于添加元素那些我都明白!!
回复 使用道具 举报
xuemeng 发表于 2013-5-21 08:02
如果先移除p2, 那么也是true

是的  就如楼上那个哥们说的  p2和p1指向同一对象   用remove(p2)  或remove(p1) 移除的是同一对象
回复 使用道具 举报
刘胜寒 发表于 2013-5-21 08:09
把你分享的链接给我,我给你提升,设置高亮。。
主要是帖子太多了。。帖子沉的也快。。。 ...

http://bbs.itheima.com/thread-50885-1-1.html
谢谢。我又看了下其他人的回复。
还是有人没真的弄明白。希望能帮到大家。
这两天一直在总结泛型。这估计也是最难的一块了。终于找到两本书介绍泛型比较好的。我在
泛型上已经停留两天了。
也会总结一个帖子。希望到时候能帮忙分享下。希望帮到大家。
当然也有写的不好和理解偏颇的地方。希望大家带着思考的去看,并能最终实践,变成自己的知识。
回复 使用道具 举报
炉海佳 来自手机 高级黑马 2013-5-21 14:54:43
15#
刘勇强 发表于 2013-5-21 00:36
hs.add(p1);
                boolean a= hs.add(p2);
                                 ...

正解吸收了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马