如何在Java中复制HashMap(不是浅层复制)

如何在Java中复制HashMap(不是浅层复制),java,hashmap,copy,Java,Hashmap,Copy,我需要制作一份HashMap的副本,但当我更改副本中的某些内容时,我希望原始内容保持不变。i、 e当我从副本的列表中删除某个内容时,它将保留在原件的列表中 如果我理解正确,这两种方法只创建浅拷贝,这不是我想要的: mapCopy = new HashMap<>(originalMap); mapCopy = (HashMap) originalMap.clone(); mapCopy=newhashmap(originalMap); mapCopy=(HashMap)origina

我需要制作一份
HashMap
的副本,但当我更改副本中的某些内容时,我希望原始内容保持不变。i、 e当我从副本的
列表
中删除某个内容时,它将保留在原件的
列表中

如果我理解正确,这两种方法只创建浅拷贝,这不是我想要的:

mapCopy = new HashMap<>(originalMap);
mapCopy = (HashMap) originalMap.clone();
mapCopy=newhashmap(originalMap);
mapCopy=(HashMap)originalMap.clone();
我说得对吗


除了迭代所有键和所有列表项并手动复制外,还有更好的方法吗?

您正在复制HashMap本身,因此更改HashMap副本不会更改原始HashMap(即添加或删除条目),但因为您存储的对象不是基元类型,使用给定键检索的列表将与从第一个映射或第二个映射检索的列表相同

因此,该列表仍然只有一个副本,由两个映射引用:更改列表会更改它,无论您使用哪个引用来访问它

如果您希望实际列表是一个单独的副本,那么必须按照您所说的做:迭代HashMap的条目集,手动创建每个列表的副本,并在运行时将其添加到新映射中


如果有比这更好的方法,我不知道它是什么。

你是对的,浅拷贝不能满足你的要求。它将具有原始映射中的
列表
的副本,但是那些
列表
将引用相同的
列表
对象,因此从一个
哈希映射
列表
的修改将出现在另一个
哈希映射
对应的
列表

Java中没有为
HashMap
提供深度复制,因此您仍然需要遍历所有条目并
将它们放入新的
HashMap
。但您也应该每次都复制一份
列表。大概是这样的:

public static HashMap<Integer, List<MySpecialClass>> copy(
    HashMap<Integer, List<MySpecialClass>> original)
{
    HashMap<Integer, List<MySpecialClass>> copy = new HashMap<Integer, List<MySpecialClass>>();
    for (Map.Entry<Integer, List<MySpecialClass>> entry : original.entrySet())
    {
        copy.put(entry.getKey(),
           // Or whatever List implementation you'd like here.
           new ArrayList<MySpecialClass>(entry.getValue()));
    }
    return copy;
}
公共静态哈希映射副本(
HashMap(原始)
{
HashMap copy=新的HashMap();
对于(Map.Entry:original.entrySet())
{
copy.put(entry.getKey(),
//或者您希望在这里使用的任何列表实现。
新的ArrayList(entry.getValue());
}
返回副本;
}

如果您想修改您的个人
MySpecialClass
对象,并且在复制的
HashMap
列表中不反映这些更改,那么您也需要为它们制作新的副本。

不幸的是,这需要迭代。但对于Java 8流来说,它非常简单:

mapCopy = map.entrySet().stream()
    .collect(Collectors.toMap(e -> e.getKey(), e -> List.copyOf(e.getValue())))

序列化为json,然后反序列化:

Map<String, Object> originalMap = new HashMap<>();
String json = new Gson().toJson(originalMap);
Map<String, Object> mapCopy = new Gson().fromJson(
    json, new TypeToken<Map<String, Object>>() {}.getType());
Map originalMap=newhashmap();
字符串json=new Gson().toJson(originalMap);
Map mapCopy=new Gson().fromJson(
json,新的TypeToken(){}.getType());

对于特殊类,您可能需要。

只需使用apachecommons Lang-SerializationUtils.clone()

类似这样的:

    Map<String, List<MySpecialClass>> originalMap = new HashMap();
    HashMap<String, List<MySpecialClass>> deepCopy = SerializationUtils.clone(new HashMap<>(originalMap));

在for循环声明中,不应该有original.entrySet()而不是copy.entrySet()?@user3394494是的,你是对的。我想当我快速输入代码时就会发生这种情况。已修改。请注意,如果
映射中的多个
引用了相同的
对象,则此解决方案将无法按预期工作!使用上述方法,克隆将为每个
创建新对象。事实上,我来这里是为了寻找一个更好的选择,而不是在这种情况下,在克隆过程中使用第二个
映射
,从原始
到克隆
。我对流不太熟悉。当我尝试您的解决方案时,它给出了一个错误:无法从静态上下文引用非静态方法getKey(),与getValue()相同,如何编辑它使其工作?谢天谢地,幸运的是,我目前还没有在编译器工作,所以我还没有机会测试它。试试这个,如果它不起作用,我会删除答案,直到我有机会测试它。在一般情况下,你的解决方案是有效的。非静态方法错误似乎与OP问题的方法的静态上下文有关。
getValue()
是否只是传递一个引用?
List.copyOf()返回一个不可修改的列表。如果是副本,应首选
新建ArrayList()
。使用克隆库为我节省了时间!我猜否决票是关于这项技术的性能损失,但在我看来,这是一个合法的答案,尽管它会带来性能成本。非常适合我的测试代码(我不关心性能),当然,您可以使用任何json序列化程序,如
com.fasterxml.jackson.databind.ObjectMapper
Map-mappcopy=mapper.readValue(mapper.writeValueAsString(originalMap),new-TypeReference(){})
这在数据被深度引用的情况下非常有用。这是一个真正的深度副本,保证所有嵌入对象都不会指向同一引用
public class MySpecialClass implements Serializable{}