如何深度克隆哈希表

时间:2013-11-02 15:58:49

标签: java hashtable clone

我有一个项目,我需要非常具体,我需要一些帮助。我基本上到处寻找答案而找不到它,甚至在Stack Overflow上也找不到。它与克隆哈希表有关。 (浅而深。)

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

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

我已经查找了如何通过哈希表进行循环。这仅适用于Object类型,并且由于方法的受保护状态,Object类型无法克隆()。下面是我的代码,从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 + "]";
    }
}

5 个答案:

答案 0 :(得分:0)

在有效的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();
        }
    }

还有一种方法是:

SerializationUtils.clone()序列化和反序列化Map引用的整个对象图,这就是为什么它需要这么长时间。它创建了一个真正的深层拷贝,但是(如果你的课程中没有有趣的序列化内容)。

答案 1 :(得分:0)

最简单的方法是序列化和反序列化HashMap。但是,这需要在所有键和值类上实现Serializable。然而,它的Cloneable或Serializable ......还是有一些工作要做,以便无论如何深度复制对象图

最好使用像http://code.google.com/p/fast-serialization/这样的快速序列化器:-)。这样表现应该是好的

答案 2 :(得分:0)

如果所有元素都是Serializable,则序列化hashMap并按ObjectInputStream反序列化,这将是一个深层副本。

请参阅this

此链接中的一些重点:

  

通过序列化克隆

     

这些问题的一个解决方案是使用序列化进行克隆。   通常,序列化用于将对象发送到某处(进入   文件或通过网络),以便其他人可以重建它们   后来。但你可以滥用它来自己重建对象   立即。如果对象完全可序列化,那么   重建应该是忠实的副本。在正常使用中   序列化,原始物体远在其他地方;它可能在   网络连接远端的世界另一端。那么你   可以肯定的是,更改副本将不会对   原始

     

在继续之前,我应该提醒一下,这种技术不是   轻易使用。首先,序列化非常昂贵。   它很容易比克隆()贵一百倍   方法。其次,并非所有对象都是可序列化的。第三,制作一个   class serializable是棘手的,并不是所有的类都可以依赖   修正它。 (您可以假设系统类是正确的,   虽然。)

答案 3 :(得分:0)

请注意,使用仅根据大小写区分的方法(清除,清除;克隆,克隆)是个不太好的主意。没有必要,因为您可以使用super关键字来调用方法的超类实现。

Hashtable本身是可复制的,因此不需要重写克隆;现有的克隆方法已经完成了浅层克隆所需的操作,但为方便起见,您可以覆盖它以返回类型为EHashtable而不是Object:

public EHashtable clone() {
    return (EHashtable)super.clone();
}

对于深度克隆,你是对的,因为Object.clone受到保护,你无法调用它。 It's a historical design mistake in Java.围绕它有一些混乱的方法(序列化,或反射,或定义一个使该方法公开的ActuallyCloneable接口)但我不认为你需要做这些事情,因为(除非你离开在粘贴问题时为了简洁起见的mutator方法)你在表中存储的所有类型似乎都是不可变的。如果它们无法改变,就无法克隆它们。

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

  

现在,即使是浅层克隆(Clone()方法),也只能在一个Hashtable中更改一个值,而不会更改另一个值中的值。

改变价值观;你正在推新的。改变价值会是这样的:

cr1.radius = 123;

如果你的对象是不可变的,那是不可能的,但它会改变从原始哈希表和浅层克隆中看到的对象。

一些小建议:(1)打印例外toString()只会打印其名称和信息;堆栈跟踪信息将丢失。要获得堆栈跟踪,请使用printStackTrace()方法。这也有利于确保它打印到stderr而不是stdout(这可能使它更加明显;它在Eclipse中是鲜红色的)。如,

catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

或者,如果对特定的已检查异常没有任何帮助,最好将其重新抛出为未经检查的异常,这样即使出现错误,周围的代码也不会发生:

catch (CloneNotSupportedException e) {
    throw new RuntimeException(e);
}

(2)执行eh.add(new String("C1"), cr1);(创建一个新字符串)是一种愚蠢的反模式,没有任何好处,并增加了不必要的对象创建和打字开销。您可以将其更改为eh.add("C1", cr1);

答案 4 :(得分:0)

除了序列化的想法,如果你有现有的代码来保存你的Map到/从文件,数据库,XML / json等,这将是一个“快速和肮脏”的选项。不是快速的性能,而是快速编码。