【问题标题】:Lists.addAll(Map.get(key)), unintentionally changes the`Map`Lists.addAll(Map.get(key)),无意中改变了`Map`
【发布时间】:2021-09-10 04:43:35
【问题描述】:

MapConstants.java:包含四个MapsMAP1是ReversedMap,MAP2是ReverseMap。 ReverseMap 是根据 answer

创建的
import com.google.common.collect.ImmutableMap;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MapConstants {

    public static final ImmutableMap<String, String> MAP1 = ImmutableMap.<String, String>builder()
            .put("FD1", "FDG")
            .build();

    public static final Map<String, List<String>> REVERSE_MAP1 =
            MAP1.entrySet()
                    .stream()
                    .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));


    public static final ImmutableMap<String, String> MAP2 = ImmutableMap.<String, String>builder()
            .put("FD2", "FDG")
            .build();

    public static final Map<String, List<String>> REVERSE_MAP2 =
            MAP2.entrySet()
                    .stream()
                    .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

}

我使用 addAll() 方法将 both 反向 Map 中的相同键添加到新列表 exampleList

可重现的示例 - 不是我所面临的确切例子,我们不使用 psvm,但这很好地说明了如何重现它。

import ..REVERSE_MAP1;
import ..REVERSE_MAP2;

public class Test {

    public void main(String[] args){

        System.out.println("REVERSE_MAP1 BEFORE list.addAll" + REVERSE_MAP1);
        //Returns {FDG=[FD1]}

        final List<String> exampleList = REVERSE_MAP1.get("FDG");
        exampleList.addAll(REVERSE_MAP2.get("FDG"));

        System.out.println("REVERSE_MAP1 AFTER list.addAll" + REVERSE_MAP1);
        //Returns {FDG=[FD1, FD2]}
        
    }
} 

当我尝试将addAll() 转换为exampleList 时,为什么REVERSE_MAP1 的列表具有从REVERSE_MAP2 的值?我的期望是只有exampleList 应该改变,而不是REVERSE_MAP1FDG 键值。

抱歉,如果您无法重现它,我们在 AWS 上部署了我们的代码,所以我没有像上面复制粘贴的那样,用psvm 创建一个新项目,因为在运行 UTC 时我现有的调用我们方法的方法,我的打印语句显示Map 似乎正在改变。

【问题讨论】:

  • 您正在获取对地图中列表的引用,然后对其进行变异。如果您不希望这样,则需要在检索时复制该列表。
  • 您知道final List&lt;String&gt; exampleList = REVERSE_MAP1.get("FDG"); 不会创建列表的新副本吗?该行字面意思是“exampleList 指的是 REVERSE_MAP1.get("FDG") 相同的对象”。
  • 我明白了。我要删除我的问题吗?

标签: java hashmap guava


【解决方案1】:

这一行:

final List<String> exampleList = REVERSE_MAP1.get("FDG");

获取对包含在 REVERSE_MAP_1 中的列表的引用。这不是一个新对象。它仍然在 REVERSE_MAP_1 中的同一个对象。所以当你打电话时:

exampleList.addAll(REVERSE_MAP2.get("FDG"));

您实际上是在向 REVERSE_MAP_1 中的列表对象添加更多数据。如果您想要一个包含来自 REVERSE_MAP_1 和 REVERSE_MAP_2 的数据而不更改其中任何一个地图中的数据的新列表,则需要创建一个新列表。

final List<String> exampleList = new ArrayList<>();
exampleList.addAll(REVERSE_MAP1.get("FDG"));
exampleList.addAll(REVERSE_MAP2.get("FDG"));

编辑:

我不认为您的反向映射是不可变的。但即使它们是,反向映射中包含的 List 对象也绝对不是不可变的。正如您的示例所证明的那样,您可以轻松地从存储在反向映射中的列表中添加(或删除)数据。

【讨论】:

    猜你喜欢
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-21
    • 2021-01-20
    • 2012-01-10
    • 1970-01-01
    • 2011-09-12
    相关资源
    最近更新 更多