在Java中复制HashMap

问题描述 投票:104回答:9

我试图保持包含成员的类的临时容器:

HashMap<Integer,myObject> myobjectHashMap

一个名为myobjectsList的类

然后我做

myojbectsListA = new myojbectsList();
myojbectsListB = new myobjectsList();

然后:将一些hashmap项添加到A(like2)

然后

myobjectListB = myobjectListA; //B has 2

然后:将hashmap项添加到A; (还有4个)

然后将A返回到存储在B中的项目;

myobjectListA = myobjectListb;

但是当我这样做的时候,B在A中增长,而我正在向A添加hashmap项目.A现在有6个项目,因为B有6个。

我希望A在C ++的最后一次分配之后仍然保持原始的2,我会使用copy with objects,java等价物是什么?

补充:好的我没有解释这个问题.MyObjectsList不包含HashMap,它来自一个具有HashMap成员的MyBaseOjbectsList类,MyObjectsList扩展了MyBaseOjbectsList。这有什么不同吗?

java copy hashmap clone
9个回答
212
投票

如果你想要一个HashMap的副本,你需要构建一个新的。

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

这将创建地图的(浅)副本。


0
投票

从Java 10开始就可以使用

Map.copyOf

用于创建浅层副本,这也是不可变的。 (这是它的Javadoc)。对于深层复制,如本answer you中所述,需要某种值映射器来制作值的安全副本。您不需要复制密钥,因为它们必须是不可变的。


14
投票

你也可以使用

clone()

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

用于将所有元素从一个hashmap复制到另一个hashmap的程序

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


11
投票

不同之处在于,在C ++中,您的对象位于堆栈中,而在Java中,您的对象位于堆中。如果A和B是对象,那么在Java中的任何时候你都可以:

B = A

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

如果你想要两个不同的对象,请使用新的HashMap()

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


6
投票

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

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);

4
投票

在Java中,当你写:

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

objectAobjectB是相同的,并指向相同的参考。改变一个将改变另一个。因此,如果你改变objectA的状态(不是它的参考),objectB也会反映出这种变化。

但是,如果你写:

objectA = new Object()

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


2
投票

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


2
投票

如果我们想用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;
    }
}

0
投票

你将一个对象分配给另一个对象,你所做的只是复制对象的引用,而不是它的内容。您需要做的是获取对象B并手动将对象A的内容复制到其中。

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


0
投票

由于OP已经提到他无法访问其中存在HashMap的基类 - 我担心可用的选项很少。

在Java中执行对象的深层副本的一种(非常缓慢且资源密集)的方法是滥用“Serializable”接口,许多类有意或无意地扩展这些接口,然后利用它将类序列化为ByteStream。反序列化后,您将获得相关对象的深层副本。

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

© www.soinside.com 2019 - 2024. All rights reserved.