HashMap对象引用混乱

时间:2014-06-30 08:54:47

标签: java reference hashmap

您好我会直接解决问题。我知道在java中的HashMaps中,valueSet存储了REFERENCE,这意味着如果我更改了该引用,那么该键的特定值也应该改变,因为它们都引用同一个对象。

但是我的应用程序遇到了一些问题,所以我尝试调试它以了解HashMap的实际工作原理。所以我编写了这个迷你程序:

class Test
{
    private final static HashMap<Integer, String> test = new HashMap<>();

    public static void main(String[] args)
    {
        String s = "String";
        test.put(1, s);
        test.put(2, "2");
        test.put(3, "3");
        s = "1";
        for (Integer i : test.keySet())
            System.out.println("Key: " + i + ", Value: " + test.get(i));
        }
    }
}

输出结果为:

Key: 1, Value: String
Key: 2, Value: 2
Key: 3, Value: 3

这里有什么不对?我在迭代HashMap之前改变了String的值,它仍然显示旧的值。这向我显示HashMap不能与对象引用一起使用吗?

我在哪里错了?

感谢您的时间。

编辑:

尝试使用测试对象,结果更加令人困惑,因为它似乎可以作为参考而不是Immutable对象,因为我们的伙伴说:

class Test 
{
    private final static HashMap<Integer, TestObject> test = new HashMap<>();

    public static void main(String[] args)
    {
        TestObject to = new TestObject();
        test.put(1, to);
        test.put(2, new TestObject());
        test.put(3, new TestObject());
        to.setValue(16);
        for (Integer i : test.keySet())
            System.out.println("Key: " + i + ", Value: " + test.get(i).getValue());
    }

    public static class TestObject
    {
        private int value;

        public void setValue(int val)
        {
            value = val;
        }

        public int getValue()
        {
            return value;
        }
    }
}

打印

Key: 1, Value: 16
Key: 2, Value: 0
Key: 3, Value: 0

4 个答案:

答案 0 :(得分:10)

  

我改变了String s的值

是的,您更改了s的值。那个所有你改变了。地图仍包含变量的之前的值。你还没有改变那个价值的含义。

我们可以将您的示例简化为纯字符串变量:

String x = "hello";
String y = x;
x = "goodbye";
System.out.println(y); // "hello"

作业(y = x)只会将x的当前值复制到y。该值是参考,但它并不知道&#34;它来自哪里 - 它与变量x的唯一关系是它恰好是赋值时x的值。如果您稍后更改x的值,则不会更改y的值。

Map.put中发生完全相同的事情 - 变量的当前值用作键。稍后更改变量的值不会影响地图的内容。

关于可变性

上述陈述均不取决于相关类型的可变性。例如,您可以使用StringBuilder看到完全相同的行为:

StringBuilder x = new StringBuilder("hello");
StringBuilder y = x;
x = new StringBuilder("goodbye");
System.out.println(y); // "hello"

如果更改代码,您只会看到行为的变化:

StringBuilder x = new StringBuilder("hello");
StringBuilder y = x;
x.append(" world");
System.out.println(y); // "hello world"

比较这些陈述:

// First version
x = new StringBuilder("goodbye");

// Second version
x.append(" world");

第一个版本更改了x的值。赋值后,x具有不同的值:引用不同对象的引用。这就像搬到另一所房子:我的家庭住址和现在的地址不一样。

第二个版本不会更改x 的值。它更改x的值所引用的对象的内容。这就像画我房子的前门一样:地址仍然是一样的(它指的是同一个房子),只是房子的某些方面已经改变了。

答案 1 :(得分:7)

他们对HashMap没有任何问题。你错误地理解了String对象的概念。

字符串是不可变的。 这意味着您在字符串对象中进行更改的次数将创建一个新对象并提供对变量的引用。

试试这个小代码

String s="String";
System.out.println("Old-"+s.hashCode());
s="New String";
System.out.println("New-"+s.hashCode());

<强>输出

Old--1808118735
New-1745736401

在这里您可以看到,当您对String变量进行更改时,它的hascode已更改,这意味着在String Pool中创建了一个新对象,并对该变量进行了新的引用。

我的情况

HashMap<Integer, String> test=new HashMap<Integer, String>();
String s = "String";
System.out.println("Adding with -"+s.hashCode());
test.put(1, s);
test.put(2, "2");
test.put(3, "3");
s = "1";
System.out.println("Modified s-"+s.hashCode());
for (Integer i : test.keySet())
    System.out.println("Key: " + i+ ", Value: " + test.get(i)+"  "+test.get(i).hashCode());
}

像这样写,看看神奇的输出。

Adding with --1808118735
Modified s-49
Key: 1, Value: String  -1808118735
Key: 2, Value: 2  50
Key: 3, Value: 3  51

HashMap仍然包含旧的字符串引用。

答案 2 :(得分:2)

让我举个例子:这里的真实物体就像任何地方说主页,参考是他们的地址。现在您正在创建一个列表的地址,它可以是HashMap或任何列表。

假设您上面写有一张纸和 Home1 的地址。现在你把这篇论文交给你的朋友制作一份清单,并将这个地址复制到他的清单中。现在你扔掉你,然后挑选另一张包含 Home2 地址的论文。现在,如果您提供的内容将转到 Home2 ,如果您的朋友根据列表提供内容,则会转到 Home1

这就是为什么当您更改s

的值时
 s = "1";

它会更改s的值,但不会更改列表中的引用值。

**实际上,您并未更改s所指向的对象的值,而是指示s指向另一个对象。**

答案 3 :(得分:0)

根据您的代码 S 引用指向对象&#34; 字符串&#34;。和hashMap键,即&#34; 1 &#34;也指向对象&#34; String&#34;一旦 test.put(1,s); 执行,现在你更改 s 指向对象,意味着在你将s的值更改为1之后,现在s指向对象&#34; 1&#34;和关键&#34; 1&#34; hashmap仍然指向object&#34; String&#34;。