又一次栽倒arraylist浅拷贝的坑

先记下两次的解决方法。

  1. 实现Cloneable 接口,重写clone方法。

场景:现有一个类Person[sex,age],存放在ArrayList中。要求得到一个ArrayList,存放sex=男和sex=女的年龄总和。

思路:重写Person的hashCode和equals方法,使用sex判断。遍历ArrayList l1,将ArrayList存到Set中,再转回ArrayList l2,获取去重后的性别。再遍历l2,内部遍历l1,判断sex相等的,将age加到l2中。

for(int i=0;i<l2.size();i++){

  for (int j=0;i<l1.size();f++){

    if(l2.i.sex=l1.j.sex){

      l2.age=l2.age+l1.age;

    }  

}

}

问题:在改变l2中person对象的age时,l1的person对象也发生了变化。

原因:经查看HashSet的add方法,内部调用了HashMap的put方法。

private static final Object PRESENT = new Object();

public boolean add(E e) {

        return map.put(e, PRESENT)==null;

    }

查看HashMap的put方法

   public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

可以看到上述代码,在hashmap中找到hash相同的Entry,然后判断key相等的,对value进行交换,即hashMap后边put的会覆盖之前put的value。而对于hashSet而言,传入的value是空的Object,因此Key没有发生变化,即遍历ArrayList l1,将ArrayList存到Set中,这一步,Set中存放的sex=男 的这个对象是l1中第一条sex=男 的对象,后边的add直接跳过。而再Set转回l2后,对l2的person对象的age属性进行操作的同时,l1中的相同的person对象也发生了变化。

错误思路:使用Collections.copy(newList, oldList);创建一个新的l3代替l1,然后进行转set。结果:无效,经查看

Collections.copy创建了新的list对象,但是list中存放的person对象还是原person对象,因此无效。

正确思路:重写person的clone方法。在set转l2的时候,存入l2的存放person的克隆对象,这样修改l2的

person对象的时候不会影响到l1的person对象。

注意:默认的clone方法,其实现还是person层的浅拷贝,若person中有引用类型的属性,而对这个

引用类型的属性进行操作时,还应注意。由此引出下一条。

2.new 一个新对象,挨着set值。

不再赘述,一句话总结:浅拷贝,包含clone的默认实现或者Collections.copy,获取的对象是

新的对象,但是对象内部的引用类型属性的地址是相同的。可以重新指定新对象的引用对象的地址,但是

直接修改引用对象的属性,则原对象的引用对象的属性也会改变。

发表评论

邮箱地址不会被公开。 必填项已用*标注