当前位置:   article > 正文

浅复制和深复制-以HashMap为例_hashmap深拷贝

hashmap深拷贝

目录

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:

  1. public class Employee {
  2. private String name;
  3. private Address address;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public Address getAddress() {
  11. return address;
  12. }
  13. public void setAddress(Address address) {
  14. this.address = address;
  15. }
  16. }

一个Address类

  1. public class Address {
  2. private String name;
  3. private String province;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public String getProvince() {
  11. return province;
  12. }
  13. public void setProvince(String province) {
  14. this.province = province;
  15. }
  16. }

测试:

  1. import org.junit.Test;
  2. import java.util.HashMap;
  3. public class TestHashMapCopy {
  4. private HashMap<String , Employee> originalMap= new HashMap();
  5. @Test
  6. public void testShallowCopy() {
  7. Employee emp1 = new Employee();
  8. emp1.setName("刘亦菲");
  9. originalMap.put("emp1", emp1);
  10. Employee emp2 = new Employee();
  11. emp2.setName("范冰冰");
  12. originalMap.put("emp2",emp2);
  13. HashMap<String ,Employee> shallowCopy = new HashMap<>(originalMap);
  14. assert shallowCopy != orignalMap;
  15. // System.out.println("orignalMap beforeChange" + JacksonJsonUtil.obj2JsonString(orignalMap));
  16. // System.out.println("shallowCopy beforeChange" + JacksonJsonUtil.obj2JsonString(shallowCopy));
  17. emp1.setName("关晓彤");
  18. Employee shallowCopyEmp2 = shallowCopy.get("emp2");
  19. shallowCopyEmp2.setName("emp2改名字");
  20. // System.out.println("orignalMap afterChange" + JacksonJsonUtil.obj2JsonString(orignalMap));
  21. // System.out.println("shallowCopy afterChange" + JacksonJsonUtil.obj2JsonString(shallowCopy));
  22. // assert结果是true,表示浅复制map里的emp1和原map里的emp1是同一个对象,
  23. // 无论是在浅复制map里更改emp1的属性,还是在原map里更改emp1的属性,对方都会跟着更改
  24. assert (shallowCopy.get("emp1")).equals(orignalMap.get("emp1"));
  25. }
  26. }

测试结果:

浅复制,原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<? extends K,? extends V> m) :

HashMap<String ,Employee> shallowCopy = new HashMap<>(originalMap);

3.1.2使用clone()

HashMap<String ,Employee> shallowCopy = (HashMap<String, Employee>) originalMap.clone();

3.1.3Map.put()

  1. HashMap<String ,Employee> shallowCopy = new HashMap<String , Employee>();
  2. Set<Map.Entry<String, Employee>> entries = originalMap.entrySet();
  3. for(Map.Entry<String, Employee> entry: entries) {
  4. shallowCopy.put(entry.getKey(), entry.getValue());
  5. }

3.1.4Map.putAll()

  1. HashMap<String ,Employee> shallowCopy = new HashMap<String , Employee>();
  2. shallowCopy.putAll(originalMap);

3.1.5Java 8 Stream API

  1. HashMap<String, Employee> shallowCopy = (HashMap<String, Employee>) originalMap.entrySet().stream().collect(
  2. // 阿里开发手册里集合处理第3条有写toMap的规范:要使用带有BinaryOperator的方法
  3. // Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue
  4. Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v2
  5. ));

3.1.6Google Guava的copyOf

Map<String, Employee> 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<String, Employee> deepCopy = SerializationUtils.clone(originalMap);

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

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

  •     Jackson:

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

             方式一:含参数TypeReference的方法

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. String originalJsonStr = objectMapper.writeValueAsString(originalMap);
  3. HashMap<String, Employee> deepCopy = new HashMap<>();
  4. deepCopy = objectMapper.readValue(originalJsonStr, new TypeReference<HashMap<String, Employee>>() {
  5. });

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

  1. ObjectMapper objectMapper = new ObjectMapper();
  2. String originalJsonStr = objectMapper.writeValueAsString(originalMap);
  3. JavaType javaType = objectMapper.getTypeFactory().constructParametricType(HashMap.class, String.class, Employee.class);
  4. HashMap<String, Employee> deepCopy = objectMapper.readValue(originalJsonStr, javaType);
  •     Gson:需要使用泛型参数TypeToken

  1. // Gson序列化,需要使用泛型参数TypeToken
  2. Gson gson = new Gson();
  3. String json = gson.toJson(originalMap);
  4. HashMap<String, Employee> deepCopy = new HashMap<>();
  5. // 注意,gson的jsonStr转集合,需要使用泛型参数TypeToken,才能准确获取到需要的对象
  6. deepCopy = gson.fromJson(json, new TypeToken<HashMap<String, Employee>>(){}.getType());
  •     fastjson:需要使用TypeReference指定泛型

  1. // fastJson序列化,注意要使用TypeReference指定泛型
  2. String originalJsonStr = JSON.toJSONString(originalMap);
  3. HashMap<String, Employee> deepCopy = JSON.parseObject(originalJsonStr,new TypeReference<HashMap<String, Employee>>(){});

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

  1. public class SerializeWrapper {
  2. private Object obj = null;
  3. public Object getObj() {
  4. return obj;
  5. }
  6. public void setObj(Object obj) {
  7. this.obj = obj;
  8. }
  9. }
  1. Schema<SerializeWrapper> schema = RuntimeSchema.getSchema(SerializeWrapper.class);
  2. SerializeWrapper SerializeWrapper = new SerializeWrapper();
  3. SerializeWrapper.setObj(originalMap);
  4. LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
  5. final byte[] protostuff;
  6. try{
  7. protostuff = ProtostuffIOUtil.toByteArray(SerializeWrapper,schema,buffer);
  8. } finally {
  9. buffer.clear();
  10. }
  11. ProtostuffIOUtil.mergeFrom(protostuff, SerializeWrapper, schema);
  12. HashMap<String, Employee> deepCopy = (HashMap<String, Employee>) SerializeWrapper.getObj();

3.3其它

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

4.总结

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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/68567
推荐阅读
相关标签
  

闽ICP备14008679号