Map复制给新Map时,用 “=、clone、还是putAll”?论Map的深复制和浅复制
使用场景
在我们最初使用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>
- 1
上述的办法不行,使用Map本身提供的方法,网上大都说putAll()和clone()方法就是深拷贝,但是实际使用后,发现前后Map中的引用对象还是都被改变了;这里就是开头说到的,这两个方法只能修改基本数据类型的,如果是引用类型不行,这两个方法是浅拷贝!
来,让我们一起跟一下源码↓↓↓
2. 使用.putAll()方法
创建一个新的Map结构,使用putAll()方法把原先的Map添加到新的Map中,但是发现修改了副本的Map之后,原先的Map中数据也被修改了;(源码如下)
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true); // 调用了putMapEntries方法
}
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中的引用对象指针并没有改变。
// 扩展:map.put("key","value")的put()也是调用了putVal()方法
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
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中的引用对象指针并没有改变
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
测试用例
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]}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
#最上面的两条是原始数据,使用了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
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
文章来源: chensj.blog.csdn.net,作者:_陈哈哈,版权归原作者所有,如需转载,请联系作者。
原文链接:chensj.blog.csdn.net/article/details/102717884
- 点赞
- 收藏
- 关注作者
评论(0)