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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 创出一片辉煌 中级黑马   /  2012-8-7 07:13  /  2550 人查看  /  10 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

package org.cc.map;
class Person {
        private int age;
        private String name;
        public int getAge() {
                return age;
        }
        public void setAge(int age) {
                this.age = age;
        }
        public String getName() {
                return name;
        }
        public void setName(String name) {
                this.name = name;
        }
        public Person(int age, String name) {
                this.age = age;
                this.name = name;
        }
        public String toString() {
                return this.name+"的年龄是"+this.age;
        }
        public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + age;
                result = prime * result + ((name == null) ? 0 : name.hashCode());
                return result;
        }
        public boolean equals(Object obj) {
                if (this == obj)
                if (!(obj instanceof Person))
                        return false;
                Person other = (Person) obj;
                if (age != other.age)
                        return false;
                if (name == null) {
                        if (other.name != null)
                                return false;
                } else if (!name.equals(other.name))
                        return false;
                return true;
        }


}
复制代码package org.cc.map;

import java.util.HashMap;
import java.util.Map;
public class HashMapdemo {
        public static void main(String[] args) {
                Map<Person,String> m=null;
                m=new HashMap<Person,String>();
                Person per=new Person(13,"张三");
                m.put(per,"1");
                System.out.println(m.get(per));
                System.out.println(m.get(new Person(13,"张三")));
                per.setAge(14);
                System.out.println(m.get(per));
                System.out.println(m.get(new Person(13,"张三")));        
                System.out.println(m.get(new Person(14,"张三")));        
        }
}
复制代码/*
修改了key之后就找不到value了
1
1
null
null
null
*/
为什么修改Person后 用匿名对象也会找不到...........希望能讲解下 ..是不是和堆内存内per的值被修改了有关?....

评分

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

查看全部评分

10 个回复

倒序浏览
这个是肯定找不到的,因为你修改了生成hashCode的其中一个变量,所以再找就不是同一个了
像你这种,就会出现内存泄露,关于Hashcode(),如果一个元素被放进了HashSet或HashMap的集合中以后,我们最好不要去修改那个元素参与hashCode运算的值,如果修改过,那么以后想要移除这个元素的时候,是移除不掉的,因为存进去时的hashCode值和移除时的hashCode值是不同的,所以java会认为没有这个元素,所以就造成了内存泄露

下面是简单的实验,

import java.util.HashSet;
import java.util.Set;

public class Demo {

        public static void main(String[] args) {
                Person p = new Person(20,"jack");
                Set set = new HashSet();
               
                set.add(p);
                System.out.println(set);
               
                //更改了年龄以后,移除不掉
                p.setAge(30);
                set.remove(p);
                System.out.println(set);
               
                //年龄改回来以后,可以移除
                p.setAge(20);
                set.remove(p);
                System.out.println(set);
        }
}

class Person {
        private int age;
        private String name;
        @Override
        public String toString() {
                return age + "  " + name;
        }
        @Override
        public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + age;
                result = prime * result + ((name == null) ? 0 : name.hashCode());
                return result;
        }
        @Override
        public boolean equals(Object obj) {
                if (this == obj)
                        return true;
                if (obj == null)
                        return false;
                if (getClass() != obj.getClass())
                        return false;
                final Person other = (Person) obj;
                if (age != other.age)
                        return false;
                if (name == null) {
                        if (other.name != null)
                                return false;
                } else if (!name.equals(other.name))
                        return false;
                return true;
        }
        public int getAge() {
                return age;
        }
        public void setAge(int age) {
                this.age = age;
        }
        public String getName() {
                return name;
        }
        public void setName(String name) {
                this.name = name;
        }
        public Person(int age, String name) {
                super();
                this.age = age;
                this.name = name;
        }
}

希望可以帮到你

11111.jpg (9.31 KB, 下载次数: 51)

11111.jpg

评分

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

查看全部评分

回复 使用道具 举报
HashMap集合的底层是哈希表,数值存进去之后首先会根据数据的哈希值来存放到堆内存中的一个区域。
                Person per=new Person(13,"张三");
m.put(per,"1");//这里先把这对键值对存进了map集合,其实也就是分别把per与"1"这两个值在内存中的地址赋值给了HashMap的第一个元素

per.setAge(14);//改变了per这个键中的值,导致per这个键的哈希值也同时改变了,JVM会根据改变后的哈希值将这个per元素重新在内存中开辟一个空间来放置,所以per在内存中的地址改变了,但是HashMap集合并不知道,所以m中第一个元素指向的地址没变,但是那里实际上已经是空了,所以结果就是null了。
回复 使用道具 举报
类似于m就是派出所,per就是你家,1就是你家里的人数,这些都在派出所有登记,然后你搬家了,但是派出所并不知道,登记资料没有改变,下次派出所去找你的时候当然没人了。
回复 使用道具 举报
本帖最后由 李龙276596456 于 2012-8-7 08:28 编辑

楼上说的还有一些不清晰,我也补充一下关于底层是这样实现的:
HashMap里的源码是这样的:
public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                return e.value;
        }
        return null;
    }
能否取出值,主要看这个IF条件if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
其中e.hash 就是这个KEY的HASHCODE
这在Entry<K,V>里是一个final int hash; 无法改变的,
它的值只在PUT里改变
public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
希望对你有帮助
回复 使用道具 举报
王程 发表于 2012-8-7 08:16
HashMap集合的底层是哈希表,数值存进去之后首先会根据数据的哈希值来存放到堆内存中的一个区域。
         ...

“JVM会根据改变后的哈希值将这个per元素重新在内存中开辟一个空间来放置,所以per在内存中的地址改变了”
应该不是这样的,不会重新开空间来存的,内存地址也不会改变的
回复 使用道具 举报
我想楼主的问题应该是在
System.out.println(m.get(new Person(14,"张三")));
new了一个新的对象,和之前的对象没什么关系,但是根据年龄和姓名(14,”张三“)算出来的hash值和根据修改后
的那个对象的年龄和姓名(14,”张三“)算出的hash值是一样的。应该被认为是同一个对象,为什么会返回null。
关键是new了一个新的对象,对此我也不太了解,还请各位大侠指点、
回复 使用道具 举报
本帖最后由 李龙276596456 于 2012-8-7 09:37 编辑

好吧,我逐句解释下吧:
首先:Person per=new Person(13,"张三");
                m.put(per,"1");
                System.out.println(m.get(per));       打印出 1
应该好理解,没什么解释的,
然后System.out.println(m.get(new Person(13,"张三")));
上面回复提到的源码的这行:if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
hash值和现在new Person(13,"张三")计算出来的hash值是一样的,key.equals(k) 因为重写了equals方法,所以也是相等的,所以也打印出1
接着per.setAge(14);
                System.out.println(m.get(per));
这时,per里的年龄改变了,但是在hashmap里面,hash 属性为Entry<K,V>里的一个final int hash 属性 ,意思就是在你PUT时,放进去的时候,元素的hash值已经被记录了,而且不会再被改变,还是这行代码if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
e.hash 还是年龄为13时的hash值,不会发生改变,但是per修改完年龄再计算出来的hash值“hash ”是14岁的hash值,和13岁的hash值是不同的,即
e.hash != hash    所以取出值是null,打印出的也是null,
接着
System.out.println(m.get(new Person(13,"张三")));
还是那行代码if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
都是13岁的hash值,所以e.hash == hash  但是这时的per年龄是14岁的,new Person(13,"张三") 年龄是13岁的,
所以key.equals(k)返回是false,所以取不到值,所以打印还是null,
接着
System.out.println(m.get(new Person(14,"张三")));        
还是那行代码if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
e.hash还是13岁的hash值,但new Person(14,"张三") 的是14岁的hash值,
所以e.hash != hash  ,还是取不到值,还是打印null,
希望我的解释能有所帮助

评分

参与人数 1技术分 +2 收起 理由
职业规划-刘倩老师 + 2 你很细心,好的习惯继续保持哈,赞一个!.

查看全部评分

回复 使用道具 举报
李龙276596456 发表于 2012-8-7 09:34
好吧,我逐句解释下吧:
首先:Person per=new Person(13,"张三");
                m.put(per,"1");

懂了。总结下来,你的意思就是

首先通过计算对象(13,“张三”)的哈希值来进行分配内存区域,内存已经分配,当修改对象时后变为(14,“张三”),
它的哈希值会改变,但已经分配的内存不会改变。

当new一个新的对象(13,“张三”)时。虽然计算所得到的哈希值一样,但是
之后通过equals方法进行比较时,年龄不同,得到false,所以结果为null。

当new一个新的对象(14,“张三”)时,计算所得到的哈希值就已经不同,就不会分配到同一片内存区域,所以结果也为null。

关键是在修改对象后的内存不会改变,只是hash值变了。
谢谢你的解答,这次对哈希集合的理解更透彻了
回复 使用道具 举报
李龙276596456 发表于 2012-8-7 08:24
楼上说的还有一些不清晰,我也补充一下关于底层是这样实现的:
HashMap里的源码是这样的:
public V get(Ob ...

希望继续保持看源码的习惯!能看源码的孩子都是好孩子!
回复 使用道具 举报
李龙276596456 发表于 2012-8-7 09:34
好吧,我逐句解释下吧:
首先:Person per=new Person(13,"张三");
                m.put(per,"1");

已解决···
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马