《Effective Java》中clone()实现是否实为浅拷贝的技术疑问
《Effective Java》中SimpleHashtable克隆实现的疑问解析
问题描述
在《Effective Java》第三版中,Joshua Bloch讲解复杂对象的clone方法实现时,给出了如下示例代码:
public class SimpleHashtable<K, V> implements Cloneable { private Entry<K, V>[] buckets; private int size; private static class Entry<K, V> { final K key; V value; Entry<K, V> next; Entry(K key, V value, Entry<K, V> next) { this.key = key; this.value = value; this.next = next; } // Recursively clone the linked list of entries Entry<K, V> deepCopy() { return new Entry<>(key, value, next == null ? null : next.deepCopy()); } } public SimpleHashtable() { buckets = new Entry[16]; // Default size } @Override public SimpleHashtable<K, V> clone() { try { @SuppressWarnings("unchecked") SimpleHashtable<K, V> result = (SimpleHashtable<K, V>) super.clone(); result.buckets = new Entry[buckets.length]; for (int i = 0; i < buckets.length; i++) { result.buckets[i] = buckets[i] != null ? buckets[i].deepCopy() : null; } return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } }
我发现桶中的Entry对象是与原对象不同的引用,但key和value仍指向原对象的引用。若克隆后的哈希表修改某个key对应的value,该修改会同步反映到原哈希表中,因此这实际上是浅拷贝。请问我是否忽略了某些细节,还是该示例存在错误?
解答
你观察得很准确,这个实现并非完全的深拷贝,而是哈希表结构的深拷贝+键值对象的浅拷贝,这是刻意的设计而非错误,背后是《Effective Java》强调的“克隆要兼顾效率与合理性”的原则:
- 首先,哈希表的核心结构(Entry数组、链表节点)是完全深拷贝的:原表和克隆表的Entry数组是独立的,链表的每个节点也是新创建的,所以修改克隆表的链表结构(比如新增、删除节点)不会影响原表,这保证了哈希表本身的独立性。
- 对于key和value的处理是有意为之:
key被声明为final,而哈希表的设计通常要求key是不可变对象(否则会导致哈希值变化,破坏哈希表的一致性),所以直接复用key的引用是安全的——就算key是可变的,这也不属于哈希表克隆需要负责的问题,而是调用者使用时的规范问题。value的复用是为了效率:如果value本身是不可变对象,或者调用者不需要隔离value的修改,这种实现足够高效;如果需要完全隔离value的修改,调用者可以自行扩展deepCopy方法,对value进行克隆(比如调用value.clone(),前提是value实现了Cloneable)。
这个示例的核心是保证哈希表自身结构的独立性,同时把“是否深拷贝业务对象”的选择权交给使用者,这是一种平衡且合理的设计。
内容的提问来源于stack exchange,提问作者Davyd
相关产品推荐
相关产品推荐

