为啥我无法获得对地图中某个值的引用? [复制]

Posted

技术标签:

【中文标题】为啥我无法获得对地图中某个值的引用? [复制]【英文标题】:Why cant I get a reference to a value in a map? [duplicate]为什么我无法获得对地图中某个值的引用? [复制] 【发布时间】:2018-11-08 06:26:59 【问题描述】:

对于在大多数情况下(但有时不是)作为参考的所有内容以及容器,我感到非常困惑。考虑这段代码:

HashMap<Integer,String> x_map = new HashMap<>();
x_map.put(42,"foo");
String g = x_map.get(42);
g = "bar";
System.out.println(g.equalsIgnoreCase(x_map.get(42)));

为什么会打印false

我找到了正确的方法: How to update a value, given a key in a java hashmap?,但我完全迷失了为什么上面的内容不能按预期工作。此外,当地图中的对象相当大时,我希望能够只修改地图中一个值的单个成员,目前我不知道如何在不创建新实例并将其放入地图。

【问题讨论】:

因为您不会覆盖地图中的值。 get 为您提供对该值的引用,然后您只需将该引用重新分配给其他值。如果您想在地图中添加新值,请使用x_map.put(42, "newValue"); 变量在 Java 中是按值分配的。 g = x_map.get(42) 只是将映射中的 分配给变量 g。如果您重新分配 g,那么您将 g 重新指向不同的值。 @f1sh 如何覆盖地图中的值? put 相同的值会有不同的东西。 IE。 x_map.put(42, "bar");. @user463035818 String 对象在 Java 中是不可变的。如果你想拥有一个可变字符串,你必须编写自己的类。 【参考方案1】:

@khelwood 在评论中给出了原因:

Java 中的变量是按值分配的。 g = x_map.get(42) 只是 将映射中的值赋给变量 g.如果你重新分配 g, 那么你将 g 重新指向一个不同的值。

如果不是String,我在地图中放置了一些实际上可以修改的东西,那么它就可以正常工作:

  public static class Test
      public int x = 5;
  

然后……

HashMap<Integer,Test> x_map = new HashMap<>();
x_map.put(42,new Test());
Test g = x_map.get(42);
g.x = 12;
System.out.println((g.x == x_map.get(42).x) + "  " + g.x + " " + x_map.get(42).x);

打印true 12 12

【讨论】:

【参考方案2】:

按值传递

Java 是按值传递(请参阅Is Java “pass-by-reference” or “pass-by-value”?)。

这意味着当你这样做时

Person first = new Person("John");
Person second = first;

你有两个不同的变量,都指向内存中的实例new Person("John")。但同样,这两个变量不同

first  ---|
          |---> new Person("John")
second ---|

如果您操纵对象本身,则更改会反映到两个变量

second.setName("Jane");
System.out.println(first.getName()); // Jane

现在的图表是:

first  ---|
          |---> new Person("John").setName("Jane")
second ---|

但是如果你改变一个变量指向的位置,它显然不会影响另一个变量,因为你没有触摸它们指向的对象:

second = new Person("Jane");
System.out.println(first.getName()); // John

在该语句之后,您有两个变量指向不同的对象:

first  ---> new Person("John")
second ---> new Person("Jane")

说明

让我们将这些知识转移到您的代码中。你写道:

String g = x_map.get(42);
g = "bar";

并询问为什么 g = "bar" 没有不影响地图存储的内容。

如前所述,您只需更改变量g 指向的位置。您不操纵地图内的对象。先上图:

     |---> x_map.get(42)
g ---|
           "bar"

然后

            x_map.get(42)
g ---|
     |---> "bar"

解决方案

如果你想改变对象,你需要直接操作它而不是仅仅改变变量引用。喜欢first.setName("Jane")。然而,在 Java 中Strings 是不可变的。这意味着无法操纵StringString#replace 之类的方法不会操作原始字符串,它们总是会创建一个新的 String 对象并保持旧的对象不变。

因此,为了更改地图存储的内容,他们键入 42,您只需使用 Map#put 方法:

x_map.put(42, "bar");

这会将 x_map 为密钥 42 存储的内容更改为 "bar"

【讨论】:

我实际上是在修改了存储在一般地图中的对象之后,字符串有点特殊是一个非常不幸的巧合。所以我想如果我有一个HashMap&lt;Integer,SomeClass&gt; map; 那么SomeClass g = map.get(42); g.setSomeProperty("asdf"); 会按预期工作。 @user463035818 是的,没错。因为setSomeProperty直接修改了对象。而不是仅仅改变g 指向的位置。 感谢您的解释。我不得不承认,我对收到我的问题的方式感到有些惊讶。我一直认为 C++ 角对新手来说很难,但在这里我得到了很多反对意见,却不知道这个问题有什么问题(除了像很多新手一样,这是反对反对意见的理由吗?)。没关系,再次感谢您的详细解释 @user463035818 好吧。否决票可能来自您没有进行足够的研究。由于this SO 线程回答了这个问题。所以从技术上讲,它是重复的。但是,如果您问我,如果不知道正确的术语以及您观察的原因,您将无法真正找到该线程。因此,由于minimal reproducible example 的详细解释,你有我的赞成票。【参考方案3】:

因为您没有更新 x_map 中 42 键的值。 为此你应该这样做

X_map.put(42, g)

【讨论】:

以上是关于为啥我无法获得对地图中某个值的引用? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何获得对右值的引用?

为啥即使我能够获得对我试图滚动到的元素的引用,scrollIntoView 在 Vue 3 中也不起作用

为啥我在同一位置获得不同的 GPS 点

为啥对 Python 值的引用(即函数参数)存储在 CPython 的堆栈(帧)中?

我可以在 html 敏捷包中获得完整的站点地图吗? [复制]

无法获得显示对象引用未设置为对象实例的结果