关于HashMap的拷贝问题

今天在看hashtable源码时看到clone()函数,官方文档说hashtable实现的是浅拷贝,但是,粗看之下,其的确对每一个Entry都调用了clone函数,怎么会是浅拷贝呢?

如下:

public synchronized Object clone() {
	try {
	    Hashtable<K,V> t = (Hashtable<K,V>) super.clone();
	    t.table = new Entry[table.length];
	    for (int i = table.length ; i-- > 0 ; ) {
		t.table[i] = (table[i] != null)
		    ? (Entry<K,V>) table[i].clone() : null;
	    }
	    t.keySet = null;
	    t.entrySet = null;
            t.values = null;
	    t.modCount = 0;
	    return t;
	} catch (CloneNotSupportedException e) {
	    // this shouldn't happen, since we are Cloneable
	    throw new InternalError();
	}
    }

回过头去看HashMap的clone函数,发现也是实现了对每个Entry本身的clone,如下:

public Object clone() {
        HashMap<K,V> result = null;
	try {
	    result = (HashMap<K,V>)super.clone();
	} catch (CloneNotSupportedException e) {
	    // assert false;
	}
        result.table = new Entry[table.length];
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);

        return result;
    }

那么,为什么会是浅拷贝呢?‘

仔细思考了下,并看了Entry的克隆函数,就可以明白,虽然两者均实现了对Entry数组本身的拷贝,但是,对于Entry的键和值,都是使用的简单地浅拷贝,因此,说它是浅拷贝。

Entry中的clone函数代码如下:

protected Object clone() {
	    return new Entry<K,V>(hash, key, value,
				  (next==null ? null : (Entry<K,V>) next.clone()));
	}

到这里算完了么?那么,为什么源码不实现deep clone,达到使用时的便捷性呢?在思考下,我认为,这是由于范型本身的擦除特性引起的,由于java语言本身范型机制的不强大,使得无法完成在不知道具体类型的情况下,对键和值实现深度拷贝。



到这里算完了么?其实,我实验了HashMap的拷贝功能,代码如下:

public static void main(String args[]){
		HashMap<Integer,Integer> map1=new HashMap<Integer,Integer>();
		map1.put(1,2);
		map1.put(2,3);
		
		HashMap<Integer,Integer> map2=(HashMap<Integer,Integer>)map1.clone();
		map2.put(2,5);
		
		System.out.println(map1.get(2));
	}

结果是会像上面我blabla说的一样,得到的拷贝源的值也会改变呢?No,No,No。实际上,这里是十分特殊的,由于Integer在处理时实际上是要作为基本类型来处理的,所以,你会看到结果其实不会改变。而下面的例子,就是正常的情况下的拷贝函数:

HashMap source = new HashMap();
        source.put("key1","value1");
        source.put("key2","value2");
        
        for(Iterator keyItr = source.keySet().iterator();keyItr.hasNext();){
            Object key = keyItr.next();
            System.out.println(key + " : "+source.get(key));
        }
        
        System.out.println("----------------- 1 ----------------");
        
        Map targetMap = (HashMap)source.clone();
        
        for(Iterator keyItr = targetMap.keySet().iterator();keyItr.hasNext();){
            Object key = keyItr.next();
            System.out.println(key + " : "+source.get(key));
        }
        
        System.out.println("---------------- 2 ----------------");
        
        Object aa = targetMap.put("key1","modify key1");
        System.out.println("aa = "+aa);
        
        for(Iterator keyItr = source.keySet().iterator();keyItr.hasNext();){
            Object key = keyItr.next();
            System.out.println(key + " : "+source.get(key));
        }
    }

关于拷贝,在拷贝一个类时,如果这个类中有final类型的成员变量,那么由于final类型的成员变量如果没有在声明时赋值,只能在构造函数中赋值,因此,这时就必须使用拷贝构造函数的方法来实现深度拷贝,代码如下:

class Test6_A implements Cloneable{
	private final int x;
	public Test6_A(int x){
		this.x=x;
	}
	public Test6_A(Test6_A t){
		this.x=t.x;
	}
	public Test6_A clone(){
		Test6_A a=new Test6_A(this);
		return a;
	}
}

这里的第二个构造函数就是拷贝构造函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值