HashMap对象引用混乱

use*_*587 5 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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

Key: 1, Value: String
Key: 2, Value: 2
Key: 3, Value: 3
Run Code Online (Sandbox Code Playgroud)

这里有什么不对?我在迭代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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这打印

Key: 1, Value: 16
Key: 2, Value: 0
Key: 3, Value: 0
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 10

我改变了String的值

是的,你改变了它的价值s.这就是你改变的一切.地图仍包含变量的先前值.您没有更改该值的含义.

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

String x = "hello";
String y = x;
x = "goodbye";
System.out.println(y); // "hello"
Run Code Online (Sandbox Code Playgroud)

assignment(y = x)只复制xinto 的当前值y.该值是一个引用,但它并不"知道"它来自何处 - 它与变量的唯一关系x是它恰好是x赋值时的值.当您稍后更改值时x,不会更改值y.

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

关于可变性

上述陈述均不取决于所讨论类型的可变性.例如,您可以看到完全相同的行为StringBuilder:

StringBuilder x = new StringBuilder("hello");
StringBuilder y = x;
x = new StringBuilder("goodbye");
System.out.println(y); // "hello"
Run Code Online (Sandbox Code Playgroud)

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

StringBuilder x = new StringBuilder("hello");
StringBuilder y = x;
x.append(" world");
System.out.println(y); // "hello world"
Run Code Online (Sandbox Code Playgroud)

比较这些陈述:

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

// Second version
x.append(" world");
Run Code Online (Sandbox Code Playgroud)

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

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


Ank*_*yar 7

他们的HashMap没有错.您错误理解String对象的概念.

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

试试这个小代码吧

String s="String";
System.out.println("Old-"+s.hashCode());
s="New String";
System.out.println("New-"+s.hashCode());
Run Code Online (Sandbox Code Playgroud)

产量

Old--1808118735
New-1745736401
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到,当你对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());
}
Run Code Online (Sandbox Code Playgroud)

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

Adding with --1808118735
Modified s-49
Key: 1, Value: String  -1808118735
Key: 2, Value: 2  50
Key: 3, Value: 3  51
Run Code Online (Sandbox Code Playgroud)

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

  • @user3789587:Marwin 的答案和这个答案都过于关注可变性 - 它们听起来像是基于可变性的语言变化行为。特别是,“您在字符串对象中进行多少次更改,它将创建一个新对象”这句话是没有意义的......您*不能*对字符串对象进行更改,正是因为它是不可变的。您可以更改字符串*变量*以使其引用不同的字符串,但这是另一回事。 (2认同)