博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Map的深浅拷贝的探究
阅读量:5891 次
发布时间:2019-06-19

本文共 6999 字,大约阅读时间需要 23 分钟。

1. 复制map示例

首先看一个例子,当我使用不同方法将一个源map拷贝到另一个map后,改变源map,复制后的map理应不受影响

import java.math.BigDecimal;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;public class MapTest {    public static void main(String[] args) {        Map
detailsmap = new HashMap
(); Map
detailsmapByEquals = new HashMap
(); Map
detailsmapByPutAll = new HashMap
(); Map
detailsmapByIterator = new HashMap
(); detailsmap.put("key1", new BigDecimal("123.22")); detailsmap.put("key2", new BigDecimal("156.2")); // 通过“=”的方式复制map对象 detailsmapByEquals = detailsmap; // 通过putAll的方式复制map对象 detailsmapByPutAll.putAll(detailsmap); // 通过iterator的方式复制map对象 mapCopy(detailsmapByIterator, detailsmap); System.out.println("detailsmap 的内容为:" + detailsmap); detailsmap.remove("key2"); System.out.println("移除key2的 detailsmap 的内容为:" + detailsmap); System.out.println("通过 “=” 复制的map当前值为:" + detailsmapByEquals); System.out.println("通过 “putAll方法” 复制的map当前值为:" + detailsmapByPutAll); System.out.println("通过 “iterator方法” 复制的map当前值为:" + detailsmapByIterator); } public static void mapCopy(Map detailsmapByIterator, Map detailsmap) { // 将detailsmap内容拷贝到detailsmapByIterator if (detailsmapByIterator == null) { detailsmapByIterator = new HashMap(); } if(detailsmap == null){ return; } Iterator it = detailsmap.entrySet().iterator(); while(it.hasNext()){ Map.Entry entry = (Entry) it.next(); Object key = entry.getKey(); detailsmapByIterator.put(key, detailsmap.get(key) != null?detailsmap.get(key):""); } }}

查看输出:

可以看到通过“=”复制的map内容随源map的改变而改变,而通过putAll方法和Iterator复制的map则不受源map改变的影响。

2. Map的两种拷贝类型

Map的拷贝分为两种情况:

  • 浅拷贝:只拷贝对象的引用,两个引用仍然指向同一个对象,在内存中占用同一块内存。被拷贝对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即浅拷贝仅仅拷贝对象的引用,而不拷贝它所引用的对象。
  • 深拷贝:被拷贝对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被拷贝过的新对象,而不再是原有的那些被引用的对象。即深拷贝把要拷贝的对象所引用的对象都拷贝了一遍。

如示例中的三种拷贝方法:针对map中的数据为统一的、简单的基本数据类型,当拷贝的数据通过“=”复制map的方法为浅拷贝,putAll方法为深拷贝,iterator遍历添加的方式为深拷贝。

3. putAll方法并非深拷贝

两种深拷贝方法,iterator遍历添加的方式,由于是重新创建了一个对象,且遍历添加源Map的元素,因此在内存中另开辟了一块内存,毋庸置疑是深拷贝;而对于putAll方法参考其源码:

/**     * Copies all of the mappings from the specified map to this map.     * These mappings will replace any mappings that this map had for     * any of the keys currently in the specified map.     *     * @param m mappings to be stored in this map     * @throws NullPointerException if the specified map is null     */    public void putAll(Map
m) { int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; /* * Expand the map if the map if the number of mappings to be added * is greater than or equal to threshold. This is conservative; the * obvious condition is (m.size() + size) >= threshold, but this * condition could result in a map with twice the appropriate capacity, * if the keys to be added overlap with the keys already in this map. * By using the conservative calculation, we subject ourself * to at most one extra resize. */ if (numKeysToBeAdded > threshold) { int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } for (Map.Entry
e : m.entrySet()) put(e.getKey(), e.getValue()); }

通过源码可以看到putAll() 方法的实现仅仅是将源Map的第一层put进Map中,这种方式对于value为基本类型的map复制是实现深拷贝的效果的,但是当value为对象时,是不会奏效的。这里简单使用源Map内嵌Map的方式测试putAll方法,看其是否实现了深层次的复制:

import java.util.HashMap;import java.util.Map;public class MapDeepCopy {    @SuppressWarnings("unchecked")    public static void main(String[] args) {        Map
map = new HashMap
(); Map
mapCopy = new HashMap
(); // Map中要嵌套Map Map mapInner = new HashMap(); mapInner.put("num", "100"); map.put("key1", mapInner); map.put("key2", "600"); // 复制 mapCopy.putAll(map); System.out.println("使用“putAll”方法复制map到mapCopy中,此时mapCopy值为:————"+mapCopy); // 更改复制之后的map中内嵌map的内容 ((Map) mapCopy.get("key1")).put("num", "200"); System.out.println("更改复制之后的mapCopy内容,更改之后mapCopy值为:————"+mapCopy); System.out.println("此时源map map的值为:————"+map);// 源Map也被改变 }}

输出如下,说明map和mapCopy中的mapInner元素使用的还是同一块内存:

4. Map深拷贝的实现

有一种方法,是使用序列化的方式来实现对象的深拷贝,但是前提是,对象必须是实现了Serializable接口才可以,Map本身没有实现 Serializable 这个接口,所以这种方式不能序列化Map,也就是不能深拷贝Map。但是HashMap是可以的,因为它实现了 Serializable。下面的方式,基于HashMap来讲,非Map的拷贝。

  /**     * @Title: 对象深度克隆---使用序列化进行深拷贝      * @Description:  使用序列化的方式来实现对象的深拷贝,但是前提是,对象必须是实现了 Serializable接口才可以,Map本身没有实现                      Serializable 这个接口,所以这种方式不能序列化Map,也就是不能深拷贝Map。但是HashMap是可以的,因为它                      实现了Serializable。     * @param obj     * @return T     */    @SuppressWarnings("unchecked")    public static 
T clone(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; }

调用

import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.ArrayList;import java.util.HashMap;import java.util.List;public class MapTest {    public static void main(String[] args) {            List
list = new ArrayList
(); list.add(100); list.add(200); HashMap
map = new HashMap
(); // 放基本类型数据 map.put("basic", 100); // 放对象 map.put("list", list); HashMap
mapNew = new HashMap
(); mapNew.putAll(map); System.out.println("----数据展示-----"); System.out.println(map); System.out.println(mapNew); System.out.println("----更改基本类型数据-----"); map.put("basic", 200); System.out.println(map); System.out.println(mapNew); System.out.println("----更改引用类型数据-----"); list.add(300); System.out.println(map); System.out.println(mapNew); System.out.println("----使用序列化进行深拷贝-----"); mapNew = clone(map); list.add(400); System.out.println(map); System.out.println(mapNew); }}

输出如下:

 

 

附:

【4. Map深拷贝的实现】转自:https://www.cnblogs.com/cxxjohnson/p/6258742.html

 

你可能感兴趣的文章
occActiveX - ActiveX with OpenCASCADE
查看>>
redmine
查看>>
css 序
查看>>
DirectshowLib摄像头拍照的”未找到可用于建立连接的介质筛选器组合“ 解决办法...
查看>>
三种简单排序
查看>>
Dalvik VM和JVM的比较以及Android新的虚拟机ART
查看>>
【CSU 1803】2016
查看>>
SQLServer 批量备份与还原
查看>>
51Nod 1010 只包含因子2 3 5的数 Label:None
查看>>
Java中String和byte[]间的转换浅析
查看>>
什么是异步
查看>>
WordPress 主题切换
查看>>
【java】path和classpath
查看>>
UVa 10057 - A mid-summer night's dream
查看>>
解决3 字节的 UTF-8 序列的字节 3 无效
查看>>
浅谈浏览器兼容性问题-(1)产生、看待与思
查看>>
iOS8中定位服务的变化(CLLocationManager协议方法不响应,无法回掉GPS方法,不出现获取权限提示)...
查看>>
BeanUtils\DBUtils
查看>>
VC 创建托盘,托盘tooltip。右键托盘菜单,点击别的地方会隐藏掉的问题。
查看>>
第一天,新的定义
查看>>