目录

  • 使用场景
  • 尝试过的办法
  • 1. “=”赋值
  • 2. 使用.putAll()方法
  • 3. 使用.clone()方法
  • 测试用例
  • 测试用例源码

使用场景

在我们最初使用map复制开发业务代码时,通常会踩到深浅复制(拷贝)这个坑里,比如我,在Map复制时
(如:Map<String, String> new_Map = old_Map) 出现过以下两类问题:

1.使用Map<String, String> new_Map = old_Map 操作,当修改new_Map属性后,old_Map属性也跟着变了,但我并没有修改过old_Map
2.由于Map中的value值不仅有基本数据类型,还有引用数据类型,所以当我修改引用类型属性后,new_Map和old_Map的引用变量值都发生变化;(如你的value都是基本类型,就不涉及深浅拷贝的问题)

尝试过的办法

1. “=”赋值

新建一个Map,然后使用“=”直接赋值,这样只是复制了old_Map的引用,和old_Map仍使用同一个内存区域,所以,在修改new_Map的时候,old_Map的值同样会发生变化。

<Map<String, String> new_Map = old_Map>


3. 使用.clone()方法

HashMap自带了一个clone()方法,但是,它的源码中注释说明了也只是一种浅复制(拷贝):(源码如下)

@Override
    public Object clone() {
        HashMap<K,V> result;
        try {
            result = (HashMap<K,V>)super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
        result.reinitialize(); // 清空map
        result.putMapEntries(this, false);  // 可见,和putAll调用了同一个接口,
        return result;
    }

    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        if (table == null) { // pre-size
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        else if (s > threshold)
            resize();
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict); // 同上,循环调用了“value”,value中的引用对象指针并没有改变
        }
    }
}

测试用例

List<Integer> list = new ArrayList<Integer>();
list.add(100);
list.add(200);
HashMap<String,Object> old_map = new HashMap<String,Object>();
 old_map.put(“name”, “蔡虚坤”);//放基本类型数据
 old_map.put(“list”, list);//放对象HashMap<String,Object> new_map = new HashMap<String,Object>();
new_map.putAll(old_map);
 System.out.println(“----基础数据展示-----”);
 System.out.println(“old: " + old_map);
 System.out.println(“new: " + new_map);
 System.out.println(”----更改基本数据类型的数据-----”);
 old_map.put(“name”, “娘炮”);
 System.out.println(“old: " + old_map);
 System.out.println(“new: " + new_map);
 System.out.println(”----更改引用类型的数据-----”);
 list.add(300);
 System.out.println(“old: " + old_map);
 System.out.println(“new: " + new_map);
 System.out.println(”----使用序列化进行深拷贝 自定义Clone方法-----”);
 new_map = myClone(old_map); // myClone() 方法源码在下方 ↓↓
 list.add(400);
 System.out.println("old: " + old_map);
 System.out.println("new: " + new_map);

输出结果:
Connected to the target VM, address: ‘127.0.0.1:58242’, transport: ‘socket’
----基础数据展示-----
old: {name=蔡虚坤, list=[100, 200]}
new: {name=蔡虚坤, list=[100, 200]}
----更改基本数据类型的数据-----
old: {name=娘炮, list=[100, 200]}
new: {name=蔡虚坤, list=[100, 200]}
----更改引用类型的数据-----
old: {name=娘炮, list=[100, 200, 300]}
new: {name=蔡虚坤, list=[100, 200, 300]}
----使用序列化进行深拷贝-----
old: {name=娘炮, list=[100, 200, 300, 400]}
new: {name=娘炮, list=[100, 200, 300]}

#最上面的两条是原始数据,使用了putAll方法拷贝了一个新的new_map对象,
#中间两条,是修改old_map对象的基本数据类型的时候,并没有影响到new_map对象。
#但是看倒数第二组,更改引用数据类型的时候,发现new_map的值也变化了,所以putAll并没有对old_map产生深拷贝。
#最后面是使用序列化的方式,发现,更改引用类型的数据的时候,new_map对象并没有发生变化,所以产生了深拷贝。(下方提供自定义clone方法源码)
#上述的工具类,可以实现对象的深拷贝,不仅限于HashMap,前提是实现了Serlizeable接口。

测试用例源码

package com.softsec.demo;

import java.io.;
import java.util.
;

public class demoMap implements Cloneable{

public static void main(String[] srag) {
    List<Integer> list = new ArrayList<Integer>();
    list.add(100);
    list.add(200);

    HashMap<String,Object> old_map = new HashMap<String,Object>();
    old_map.put("name", "蔡虚坤");//放基本类型数据
    old_map.put("list", list);//放对象

    HashMap<String,Object> new_map = new HashMap<String,Object>();

    new_map.putAll(old_map);
    System.out.println("----基础数据展示-----");
    System.out.println("old:" + old_map);
    System.out.println("new:" + new_map);
    System.out.println("----更改基本数据类型的数据-----");
    old_map.put("name", "娘炮");
    System.out.println("old:" + old_map);
    System.out.println("new:" + new_map);
    System.out.println("----更改引用类型的数据-----");
    list.add(300);
    System.out.println("old:" + old_map);
    System.out.println("new:" + new_map);
    System.out.println("----使用序列化进行深拷贝 自定义Clone方法-----");
    new_map = myClone(old_map);
    list.add(400);
    System.out.println("old:" + old_map);
    System.out.println("new:" + new_map);
}

/**
 * 自定义clone方法(对象必须是实现了Serializable接口)
 *
 */
public static <T extends Serializable> T myClone(T obj) {
    T clonedObj = null;
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        clonedObj = (T) ois.readObject();
        ois.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return clonedObj;
}


目录

  • 使用场景
  • 尝试过的办法
  • 1. “=”赋值
  • 2. 使用.putAll()方法
  • 3. 使用.clone()方法
  • 测试用例
  • 测试用例源码

使用场景

在我们最初使用map复制开发业务代码时,通常会踩到深浅复制(拷贝)这个坑里,比如我,在Map复制时
(如:Map<String, String> new_Map = old_Map) 出现过以下两类问题:

1.使用Map<String, String> new_Map = old_Map 操作,当修改new_Map属性后,old_Map属性也跟着变了,但我并没有修改过old_Map
2.由于Map中的value值不仅有基本数据类型,还有引用数据类型,所以当我修改引用类型属性后,new_Map和old_Map的引用变量值都发生变化;(如你的value都是基本类型,就不涉及深浅拷贝的问题)

尝试过的办法

1. “=”赋值

新建一个Map,然后使用“=”直接赋值,这样只是复制了old_Map的引用,和old_Map仍使用同一个内存区域,所以,在修改new_Map的时候,old_Map的值同样会发生变化。

<Map<String, String> new_Map = old_Map>