Java 如何深度克隆哈希表

Java 如何深度克隆哈希表,java,hashtable,clone,Java,Hashtable,Clone,我有一个项目,我需要做非常具体,我需要一些帮助。我基本上到处寻找答案,但都找不到,甚至在堆栈溢出上也找不到。这与克隆哈希表有关。(既浅又深。) 我已经粘贴了下面的代码,但简而言之,我有一个名为EHashtable的类,它扩展了Hashtable。然后添加一些字符串键和各种自定义类类型的值。在EHashtable类中,有一些方法称为Clone()和dClone()。Clone()应该创建其哈希表的浅层克隆-这意味着哈希表已克隆,但其值未克隆。dClone()应该创建一个新的哈希表,并将每个原始哈希

我有一个项目,我需要做非常具体,我需要一些帮助。我基本上到处寻找答案,但都找不到,甚至在堆栈溢出上也找不到。这与克隆哈希表有关。(既浅又深。)

我已经粘贴了下面的代码,但简而言之,我有一个名为EHashtable的类,它扩展了Hashtable。然后添加一些字符串键和各种自定义类类型的值。在EHashtable类中,有一些方法称为Clone()和dClone()。Clone()应该创建其哈希表的浅层克隆-这意味着哈希表已克隆,但其值未克隆。dClone()应该创建一个新的哈希表,并将每个原始哈希表的值克隆到该哈希表中(这意味着每个值指向不同于第一个值的内存引用)。如果定制对象不可克隆,它也应该抛出异常

现在,即使在浅层克隆(clone()方法)上,也会更改一个哈希表中的一个值,而不会更改另一个哈希表中的值。似乎每个值都指向不同的引用。我不明白如何让Clone()和dClone()方法完成我希望它们完成的两件不同的事情。另外,哈希表不能有泛型。它需要是
Hashtable
,而不是
Hashtable

我查阅了如何通过哈希表进行for循环。这仅适用于对象类型,并且由于方法的受保护状态,对象类型无法克隆()。下面是我的代码,从main方法开始。我意识到这是非常具体的,非常感谢所有的帮助

import java.util.Hashtable;
import java.util.Iterator;

public class _Test {
    public static void main(String[] arguments) {
        Circle cr1 = new Circle(1);
        Circle cr2 = new Circle(2);
        Point po1 = new Point(10, 10);
        Point po2 = new Point(20, 20);
        PlaneCircle pcr1 = new PlaneCircle(po1, 11f);
        PlaneCircle pcr2 = new PlaneCircle(po2, 12f);

        EHashtable eh = new EHashtable(20);
        eh.add(new String("C1"), cr1);
        eh.add(new String("C2"), cr2);
        eh.add(new String("PC1"), pcr1);
        eh.add(new String("PC2"), pcr2);

        try {
            EHashtable ehCloned = (EHashtable) eh.Clone();

            System.out.println("/***--Before Alteration--***/");
            System.out.println("eh:");
            System.out.println(eh);
            System.out.println();
            System.out.println("ehCloned:");
            System.out.println(ehCloned);
            System.out.println();

            Circle cr3 = new Circle(99);
            Point po3 = new Point(99, 99);
            PlaneCircle pcr3 = new PlaneCircle(po3, 9999f);

            eh.add(new String("C1"), cr3);
            eh.add(new String("PC1"), pcr3);

            System.out.println("/***--After Alteration--***/");
            System.out.println("eh:");
            System.out.println(eh);
            System.out.println();
            System.out.println("ehCloned:");
            System.out.println(ehCloned);
            System.out.println();
        }

        catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        } 

        catch (ClassCastException e) {
            System.out.println(e.toString());
        }

        catch (Exception e) {
            System.out.println("\nError Message:" + e.getMessage());
        }
    }
}


import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

public class EHashtable extends Hashtable {
    public EHashtable() {

    }

    public EHashtable(int capacity) {

    }

    // Done
    public boolean add(Object key, Object value) {
        if (!(containsKey(key) && containsValue(value))) {
            put(key, value);
            return true;
        }

        else {
            return false;
        }
    }

    // Done
    public void Clear() {
        clear();
    }

    // Done
    public Object Clone() throws CloneNotSupportedException {
        EHashtable eh = (EHashtable) this.clone();
        return eh;
    }

    public Object dClone() {
        EHashtable eh = new EHashtable();
        for (Object key : keySet())
            eh.put(key, get(key));
        return eh;
    }

    // Done
    public boolean isNotEmpty() {
        return !isEmpty();
    }

    // Done
    public Iterator iterate() {
        return entrySet().iterator();
    }
}


public class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public String toString() {
        return "[x=" + x + ", y=" + y + "]";
    }
}


public class PlaneCircle {
    private Point p;
    private float radius;

    public PlaneCircle (Point p, float radius) {
        this.p = p;
        this.radius = radius;
    }

    public String toString() {
        return "[p=" + p.toString() + ", radius=" + radius + "]";
    }
}


public class Circle {
    private float radius;

    public Circle(float radius) {
        this.radius = radius;
    }

    public String toString() {
        return "[radius=" + radius + "]";
    }
}

在有效的java中发现了这一点

public class HashTable implements Cloneable {
    private Entry[] buckets = ...;

    private static class Entry {
        final Object key;
        Object value;
        Entry next;
        Entry(Object key, Object value, Entry next) {
            this.key = key;
            this.value = value;
            this.next = next;
        }

        // Recursively copy the linked list headed by this Entry
        Entry deepCopy() {
            return new Entry(key, value,
                    next == null ? null : next.deepCopy());
        }
    }
    @Override public HashTable clone() {
        try {
            HashTable result = (HashTable) super.clone();
            result.buckets = new Entry[buckets.length];
            for (int i = 0; i < buckets.length; i++)
                if (buckets[i] != null)
                    result.buckets[i] = buckets[i].deepCopy();
            return result;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
公共类哈希表实现可克隆{
私有条目[]桶=。。。;
私有静态类条目{
最终对象键;
目标价值;
进入下一步;
条目(对象键、对象值、下一个条目){
this.key=key;
这个值=值;
this.next=next;
}
//递归复制以该条目为首的链接列表
条目副本(){
返回新条目(键、值、,
next==null?null:next.deepCopy());
}
}
@重写公共哈希表克隆(){
试一试{
HashTable result=(HashTable)super.clone();
result.bucket=新条目[bucket.length];
对于(int i=0;i
另一种方法是:


SerializationUtils.clone()序列化和反序列化映射引用的整个对象图,这就是它花费如此长时间的原因。但是,它会创建一个真正的深度副本(前提是类中没有有趣的序列化内容).

最简单的方法是序列化和反序列化HashMap。然而,这需要在所有键和值类上实现可序列化。不管它是可克隆的还是可序列化的。为了深度复制对象图,仍有一些工作要做


最好使用像:-)这样的快速序列化程序。这样,如果所有元素都是可序列化的,那么性能应该是正常的,而不是序列化hashMap并通过
ObjectInputStream进行反序列化,这将是一个深度副本

请参阅

此链接中的一些要点:

通过序列化进行克隆

解决这些问题的一个方法是使用序列化进行克隆。 通常,序列化用于将对象发送到某个位置(到 文件或通过网络)以便其他人可以重建它们 稍后。但是你可以滥用它来自己重建对象 立即。如果对象完全可序列化,则 重建应该是一个忠实的副本。在正常使用 序列化时,原始对象不在附近;它可能位于 世界的另一端在网络连接的远端。所以你 可以确保更改副本不会对 原创的

在进一步讨论之前,我应该提醒大家,这种技术并不适用 首先,序列化非常昂贵。 它的价格很可能是克隆的一百倍 方法。第二,并非所有对象都是可序列化的。第三,生成 类可序列化是一个棘手的问题,并不是所有的类都可以依赖于 正确。(你可以假设系统类是正确的, 不过。)


请注意,使用仅按大小写不同的方法(clear,clear;clone,clone)是一个糟糕的主意。这不是必需的,因为您可以使用关键字来调用方法的超类实现

哈希表本身是可克隆的,因此不需要重写克隆;现有的克隆方法已经满足了浅层克隆的需要,不过为了方便起见,您可以重写它以提供返回类型EHashtable而不是Object:

public EHashtable clone() {
    return (EHashtable)super.clone();
}
对于深度克隆,你是对的,因为Object.clone是受保护的,所以你不能调用它。它有很多混乱的方法(序列化、反射,或者定义一个使方法公开的
ActuallyCloneable
接口),但我认为你不需要做这些事情,因为(除非你在粘贴问题时为了简洁起见省略了mutator方法)你存储在表中的所有类型似乎都是不可变的。如果它们不能更改,克隆它们是没有用的

如果它们在实际代码中不是不可变的,那么对于这些简单的值类型来说,使它们不可变实际上是一个非常好的主意。它使您能够简化其他代码,而不必担心何时需要复制它们

现在,即使在浅层克隆(clone()方法)上,也会更改一个哈希表中的一个值,而不会更改另一个哈希表中的值

你没有改变价值
cr1.radius = 123;
catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
catch (CloneNotSupportedException e) {
    throw new RuntimeException(e);
}