目录

1、简介

2、浅复制和深复制

2.1浅复制(shallow copy)

2.2深复制(deep copy)

3、常见实现方式

3.1【浅复制】

3.1.1使用HashMap的构造器

3.1.2使用clone()

3.1.3Map.put()

3.1.4Map.putAll()

3.1.5Java 8 Stream API

3.1.6Google Guava的copyOf

3.2【深复制】

3.2.1Apache Commons Lang【不建议使用】

3.2.2Json序列化和反序列化【一般推荐】

Jackson:

方式一:含参数TypeReference的方法

方式二:使用含参数JavaType的方法

Gson:需要使用泛型参数TypeToken

fastjson:需要使用TypeReference指定泛型

3.2.3序列化和反序列化【推荐】

以Protostuff为例:

3.3其它

4.总结

1、简介

探索HashMap的浅复制、浅拷贝(shallow copy)和深复制、深拷贝(deep copy),以及各自的几种实现方式,包括JDK里的和引用第三方的。

2、浅复制和深复制

2.1浅复制(shallow copy)

浅复制的HashMap,是一个新的HashMap,拥有和原HashMap相同的key,相同的value

比如,我们创建一个员工类Employee:

public class Employee {

private String name;

private Address address;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Address getAddress() {

return address;

}

public void setAddress(Address address) {

this.address = address;

}

}

一个Address类

public class Address {

private String name;

private String province;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getProvince() {

return province;

}

public void setProvince(String province) {

this.province = province;

}

}

测试:

import org.junit.Test;

import java.util.HashMap;

public class TestHashMapCopy {

private HashMap originalMap= new HashMap();

@Test

public void testShallowCopy() {

Employee emp1 = new Employee();

emp1.setName("刘亦菲");

originalMap.put("emp1", emp1);

Employee emp2 = new Employee();

emp2.setName("范冰冰");

originalMap.put("emp2",emp2);

HashMap shallowCopy = new HashMap<>(originalMap);

assert shallowCopy != orignalMap;

// System.out.println("orignalMap beforeChange" + JacksonJsonUtil.obj2JsonString(orignalMap));

// System.out.println("shallowCopy beforeChange" + JacksonJsonUtil.obj2JsonString(shallowCopy));

emp1.setName("关晓彤");

Employee shallowCopyEmp2 = shallowCopy.get("emp2");

shallowCopyEmp2.setName("emp2改名字");

// System.out.println("orignalMap afterChange" + JacksonJsonUtil.obj2JsonString(orignalMap));

// System.out.println("shallowCopy afterChange" + JacksonJsonUtil.obj2JsonString(shallowCopy));

// assert结果是true,表示浅复制map里的emp1和原map里的emp1是同一个对象,

// 无论是在浅复制map里更改emp1的属性,还是在原map里更改emp1的属性,对方都会跟着更改

assert (shallowCopy.get("emp1")).equals(orignalMap.get("emp1"));

}

}

测试结果:

浅复制,原map和复制map不是同一个对象,但是map里的value如果是引用类型的话,那么该引用类型是同一个对象,比如测试例子中的emp1.

更改了emp1里的属性之后,原map和浅复制map里的emp1属性都变了,会相互影响。

2.2深复制(deep copy)

深复制的HashMap,是一个新的HashMap,并且深复制了所有的属性。它为所有的key、value、mappings创建了新的对象,

因此原map和深复制map更改了值之后,相互不影响。

深复制的实现,比如Apache Commons Lang,但是要求map的所有key和value是实现了Serializable接口。

3、常见实现方式

3.1【浅复制】

3.1.1使用HashMap的构造器

HashMap(Map m) :

HashMap shallowCopy = new HashMap<>(originalMap);

3.1.2使用clone()

HashMap shallowCopy = (HashMap) originalMap.clone();

3.1.3Map.put()

HashMap shallowCopy = new HashMap();

Set> entries = originalMap.entrySet();

for(Map.Entry entry: entries) {

shallowCopy.put(entry.getKey(), entry.getValue());

}

3.1.4Map.putAll()

HashMap shallowCopy = new HashMap();

shallowCopy.putAll(originalMap);

3.1.5Java 8 Stream API

HashMap shallowCopy = (HashMap) originalMap.entrySet().stream().collect(

// 阿里开发手册里集合处理第3条有写toMap的规范:要使用带有BinaryOperator的方法

// Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue

Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2

));

3.1.6Google Guava的copyOf

Map shallowCopy = ImmutableMap.copyOf(originalMap);

3.2【深复制】

主要是序列化、反序列化方式

3.2.1Apache Commons Lang【不建议使用】

但是要求map的所有key和value都实现了Serializable接口,包括对象里的对象属性也需要

public class Employee implements Serializable {

public class Address implements Serializable {

HashMap deepCopy = SerializationUtils.clone(originalMap);

3.2.2Json序列化和反序列化【一般推荐】

常见的序列化工具,Jackson、Gson、fastjson等,但是要注意反序列化为Map的时候,需要指定对应的泛型

Jackson:

可以使用Jackson的工具类,封装下列的方法

方式一:含参数TypeReference的方法

ObjectMapper objectMapper = new ObjectMapper();

String originalJsonStr = objectMapper.writeValueAsString(originalMap);

HashMap deepCopy = new HashMap<>();

deepCopy = objectMapper.readValue(originalJsonStr, new TypeReference>() {

});

方式二:使用含参数JavaType的方法

ObjectMapper objectMapper = new ObjectMapper();

String originalJsonStr = objectMapper.writeValueAsString(originalMap);

JavaType javaType = objectMapper.getTypeFactory().constructParametricType(HashMap.class, String.class, Employee.class);

HashMap deepCopy = objectMapper.readValue(originalJsonStr, javaType);

Gson:需要使用泛型参数TypeToken

// Gson序列化,需要使用泛型参数TypeToken

Gson gson = new Gson();

String json = gson.toJson(originalMap);

HashMap deepCopy = new HashMap<>();

// 注意,gson的jsonStr转集合,需要使用泛型参数TypeToken,才能准确获取到需要的对象

deepCopy = gson.fromJson(json, new TypeToken>(){}.getType());

fastjson:需要使用TypeReference指定泛型

// fastJson序列化,注意要使用TypeReference指定泛型

String originalJsonStr = JSON.toJSONString(originalMap);

HashMap deepCopy = JSON.parseObject(originalJsonStr,new TypeReference>(){});

3.2.3序列化和反序列化【推荐】

常用的序列化框架:

JDK Serializable、FST、Kryo、Protobuf、Thrift、Hession和Avro、Protostuff,3.7.2里提到的也是,只是是序列化成json结构

序列化框架的详情可以看下另一篇博客【待发布】

以Protostuff为例:

因为Protostuff常规上只支持序列化、反序列化Bean对象,不支持List、Map,因此这里建了一个包装类Wrapper:

注:List、Map,也可以使用MessageCollectionSchema,参考:How do I serialize list or map collection objects? · Issue #233 · protostuff/protostuff · GitHub

public class SerializeWrapper {

private Object obj = null;

public Object getObj() {

return obj;

}

public void setObj(Object obj) {

this.obj = obj;

}

}

Schema schema = RuntimeSchema.getSchema(SerializeWrapper.class);

SerializeWrapper SerializeWrapper = new SerializeWrapper();

SerializeWrapper.setObj(originalMap);

LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

final byte[] protostuff;

try{

protostuff = ProtostuffIOUtil.toByteArray(SerializeWrapper,schema,buffer);

} finally {

buffer.clear();

}

ProtostuffIOUtil.mergeFrom(protostuff, SerializeWrapper, schema);

HashMap deepCopy = (HashMap) SerializeWrapper.getObj();

3.3其它

还有其它的框架可以实现,比如HuTool等

4.总结

浅复制的话,上面的浅复制方法都可以选择;深复制的话,推荐使用Protostuff等序列化工具,最好不需要对象实现序列化接口,或者需要转换成json格式。