用 Java 复制 HashMap

我试图保留一个包含成员的类的临时容器:

HashMap<Integer,myObject> myobjectHashMap

一个名为 myobjectsList 的类

那我来

myobjectsListA = new myobjectsList();
myobjectsListB = new myobjectsList();

然后: 将一些散列表项添加到 A (比如2)

然后:

myobjectListB = myobjectListA; //B has 2

然后: 将散列表项添加到 A (比如再添加4个)

然后: 将 A 返回到 B 中储存的物品

myobjectListA = myobjectListb;

但是当我这样做时,B 与 A 一起增长,而我将散列表项添加到 A。 A 现在有6个项目,因为 B 有6个。

我希望 A 在上次作业结束后仍然保留原来的2。 在 C + + 中,我会对对象使用 copy,java 等价于什么?

补充: 好吧,我漏掉了一些东西来解释这一点。MyObjectsList不包含 HashMap,它是从具有 HashMap 成员的类 MyBaseOjbectsList派生的,MyObjectsList扩展了 MyBaseOjbectsList。这有什么区别吗?

256313 次浏览

在 Java 中,当你写:

Object objectA = new Object();
Object objectB = objectA;

objectAobjectB是相同的,并且指向相同的引用。改变一个就会改变另一个。所以如果你改变了 objectA(不是它的参考)的状态,objectB也会反映这个改变。

然而,如果你写道:

objectA = new Object()

然后 objectB仍然指向您创建的第一个对象(原始 objectA) ,而 objectA现在指向一个新对象。

区别在于,在 C + + 中,对象位于堆上,而在 Java 中,对象位于堆中。如果 A 和 B 是对象,那么在 Java 中任何时候都可以:

B = A

A 和 B 指向同一个对象,所以你对 A 做的任何事,都会对 B 做,反之亦然。

如果需要两个不同的对象,则使用新的 HashMap()

您可以使用 Map.putAll(...)在两个映射之间复制数据。

将一个对象赋值给另一个对象时,所做的只是将 参考文献复制到该对象,而不是复制该对象的内容。您需要做的是将对象 B 手动复制到对象 A 的内容中。

如果经常这样做,可以考虑在类上实现一个 clone()方法,该方法将创建一个相同类型的新对象,并将其所有内容复制到新对象中。

如果需要 HashMap 的副本,则需要使用。

myobjectListB = new HashMap<Integer,myObject>(myobjectListA);

这将创建映射的(浅)副本。

你也可以用

clone()

方法将所有元素从一个散列映射复制到另一个散列映射

将所有元素从一个散列表复制到另一个散列表的程序

import java.util.HashMap;


public class CloneHashMap {
public static void main(String a[]) {
HashMap hashMap = new HashMap();
HashMap hashMap1 = new HashMap();
hashMap.put(1, "One");
hashMap.put(2, "Two");
hashMap.put(3, "Three");
System.out.println("Original HashMap : " + hashMap);
hashMap1 = (HashMap) hashMap.clone();
System.out.println("Copied HashMap : " + hashMap1);
}
}

来源: http://www.tutorialdata.com/examples/java/collection-framework/hashmap/copy-all-elements-from-one-hashmap-to-another

由于这个问题仍然没有答案,而且我也遇到过类似的问题,所以我会试着回答这个问题。问题(正如其他人已经提到的)是,您只是复制对同一对象的引用,因此对复制的修改也会修改原始对象。因此,您必须复制对象(映射值)本身。最简单的方法是让所有对象实现可序列化接口。然后序列化和反序列化您的映射,以获得一个真正的副本。您可以自己完成这项工作,也可以使用 apache commons SerializationUtils # clone () ,您可以在这里找到: https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/SerializationUtils.html但请注意,这是最简单的方法,但是对大量对象进行序列化和反序列化是一项昂贵的任务。

这里有一个小(巨大)的轻描淡写。如果您想复制具有嵌套结构的 HashMapHashMap.putAll()将通过引用进行复制,因为它不知道如何准确地复制对象。例如:

import java.util.*;
class Playground {
public static void main(String[ ] args) {
Map<Integer, Map<Integer,List<Float>>> dataA = new HashMap<>();
Map<Integer, Map<Integer,List<Float>>> dataB = new HashMap<>();


dataA.put(1, new HashMap<>());
dataB.putAll(dataA);


assert(dataB.get(1).size() == 0);


dataA.get(1).put(2, new ArrayList<>());


if (dataB.get(1).size() == 1) { // true
System.out.println(
"Sorry object reference was copied - not the values");
}
}
}

所以基本上你需要像这样自己拷贝这些字段

List <Float> aX = new ArrayList<>(accelerometerReadingsX);
List <Float> aY = new ArrayList<>(accelerometerReadingsY);


List <Float> gX = new ArrayList<>(gyroscopeReadingsX);
List <Float> gY = new ArrayList<>(gyroscopeReadingsY);


Map<Integer, Map<Integer, Float>> readings = new HashMap<>();


Map<Integer,List<Float>> accelerometerReadings = new HashMap<>();
accelerometerReadings.put(X_axis, aX);
accelerometerReadings.put(Y_axis, aY);
readings.put(Sensor.TYPE_ACCELEROMETER, accelerometerReadings);


Map<Integer,List<Float>> gyroscopeReadings = new HashMap<>();
gyroscopeReadings.put(X_axis, gX);
gyroscopeReadings.put(Y_axis, gY);
readings.put(Sensor.TYPE_GYROSCOPE, gyroscopeReadings);

由于 OP 提到他没有访问基类的权限,基类中存在一个 HashMap ——恐怕只有很少的选项可用。

在 Java 中执行一个对象的深度拷贝的一种(非常缓慢和资源密集型)方法是滥用“ Serializable”接口,许多类有意或无意地扩展了这个接口,然后利用这个接口把你的类序列化到 ByteStream。在反序列化后,您将拥有所讨论对象的深度副本。

这方面的指南可以在这里找到: https://www.avajava.com/tutorials/lessons/how-do-i-perform-a-deep-clone-using-serializable.html

如果我们想在 Java 中复制一个对象,有两种可能性需要考虑: 肤浅的复制品深度拷贝

当我们只复制字段值时,使用 肤浅的复制品。因此,副本可能依赖于原始对象。在 深度拷贝方法中,我们确保树中的所有对象都被深度复制,因此复制不依赖于任何可能发生更改的早期现有对象。

这个问题是应用 深度拷贝方法的完美定义。

首先,如果您有一个简单的 HashMap<Integer, List<T>>映射,那么我们只需创建一个像这样的工作区。创建 List<T>的新实例。

public static <T> HashMap<Integer, List<T>> deepCopyWorkAround(HashMap<Integer, List<T>> original)
{
HashMap<Integer, List<T>> copy = new HashMap<>();
for (Map.Entry<Integer, List<T>> entry : original.entrySet()) {
copy.put(entry.getKey(), new ArrayList<>(entry.getValue()));
}
return copy;
}

这个方法使用 Stream.collect()方法来创建克隆映射,但是使用了与前一个方法相同的思想。

public static <T> Map<Integer, List<T>> deepCopyStreamWorkAround(Map<Integer, List<T>> original)
{
return original
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, valueMapper -> new ArrayList<>(valueMapper.getValue())));
}

但是,如果 T中的实例也是 可变对象,我们就有一个大问题。在这种情况下,一个真正的深度副本是解决这个问题的替代方案。它的优点是至少递归地复制对象图中的每个 可变对象可变对象。由于该副本不依赖于前面创建的任何 可变对象可变对象,因此它不会像我们在浅表副本中看到的那样偶然被修改。

要解决这个问题,需要使用这个深度复制实现。

public class DeepClone
{
public static void main(String[] args)
{
Map<Long, Item> itemMap = Stream.of(
entry(0L, new Item(2558584)),
entry(1L, new Item(254243232)),
entry(2L, new Item(986786)),
entry(3L, new Item(672542)),
entry(4L, new Item(4846)),
entry(5L, new Item(76867467)),
entry(6L, new Item(986786)),
entry(7L, new Item(7969768)),
entry(8L, new Item(68868486)),
entry(9L, new Item(923)),
entry(10L, new Item(986786)),
entry(11L, new Item(549768)),
entry(12L, new Item(796168)),
entry(13L, new Item(868421)),
entry(14L, new Item(923)),
entry(15L, new Item(986786)),
entry(16L, new Item(549768)),
entry(17L, new Item(4846)),
entry(18L, new Item(4846)),
entry(19L, new Item(76867467)),
entry(20L, new Item(986786)),
entry(21L, new Item(7969768)),
entry(22L, new Item(923)),
entry(23L, new Item(4846)),
entry(24L, new Item(986786)),
entry(25L, new Item(549768))
).collect(entriesToMap());




Map<Long, Item> clone = DeepClone.deepClone(itemMap);
clone.remove(1L);
clone.remove(2L);


System.out.println(itemMap);
System.out.println(clone);
}


private DeepClone() {}


public static <T> T deepClone(final T input)
{
if (input == null) return null;


if (input instanceof Map<?, ?>) {
return (T) deepCloneMap((Map<?, ?>) input);
} else if (input instanceof Collection<?>) {
return (T) deepCloneCollection((Collection<?>) input);
} else if (input instanceof Object[]) {
return (T) deepCloneObjectArray((Object[]) input);
} else if (input.getClass().isArray()) {
return (T) clonePrimitiveArray((Object) input);
}


return input;
}


private static Object clonePrimitiveArray(final Object input)
{
final int length = Array.getLength(input);
final Object output = Array.newInstance(input.getClass().getComponentType(), length);
System.arraycopy(input, 0, output, 0, length);
return output;
}


private static <E> E[] deepCloneObjectArray(final E[] input)
{
final E[] clone = (E[]) Array.newInstance(input.getClass().getComponentType(), input.length);
for (int i = 0; i < input.length; i++) {
clone[i] = deepClone(input[i]);
}


return clone;
}


private static <E> Collection<E> deepCloneCollection(final Collection<E> input)
{
Collection<E> clone;
if (input instanceof LinkedList<?>) {
clone = new LinkedList<>();
} else if (input instanceof SortedSet<?>) {
clone = new TreeSet<>();
} else if (input instanceof Set) {
clone = new HashSet<>();
} else {
clone = new ArrayList<>();
}


for (E item : input) {
clone.add(deepClone(item));
}


return clone;
}


private static <K, V> Map<K, V> deepCloneMap(final Map<K, V> map)
{
Map<K, V> clone;
if (map instanceof LinkedHashMap<?, ?>) {
clone = new LinkedHashMap<>();
} else if (map instanceof TreeMap<?, ?>) {
clone = new TreeMap<>();
} else {
clone = new HashMap<>();
}


for (Map.Entry<K, V> entry : map.entrySet()) {
clone.put(deepClone(entry.getKey()), deepClone(entry.getValue()));
}


return clone;
}
}

因为 Java10可以使用

Map.copyOf

用于创建 肤浅的复制品,它也是不可变的。(这是它的 贾瓦多克)。对于 深度拷贝,正如本 回答你中提到的,需要某种类型的值映射器来创建值的安全副本。但是你不需要复制 钥匙,因为它们必须是 永恒不变

Java 支持 肤浅(非深度)复制概念

你可以使用以下方法存档:

  • 构造函数
  • clone()
  • putAll()