浅复制和深复制-以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:

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<String , Employee> 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<String ,Employee> 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<? 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()

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

3.1.4Map.putAll()

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

3.1.5Java 8 Stream API

HashMap<String, Employee> shallowCopy = (HashMap<String, Employee>) 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<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的方法

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

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


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

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

        // fastJson序列化,注意要使用TypeReference指定泛型
        String originalJsonStr = JSON.toJSONString(originalMap);
        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

public class SerializeWrapper {

	private Object obj = null;

	public Object getObj() {
		return obj;
	}

	public void setObj(Object obj) {
		this.obj = obj;
	}
}

        Schema<SerializeWrapper> 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<String, Employee> deepCopy = (HashMap<String, Employee>) SerializeWrapper.getObj();

3.3其它

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

4.总结

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

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMap深拷贝是指创建一个新的HashMap对象,并且该新对象拥有与原HashMap相同的键值对。深拷贝的关键在于保证新HashMap中的value也是全新的对象,而不是原HashMap中value的引用。这样做的目的是确保对新HashMap的操作不会影响到原HashMap。 在处理HashMap深拷贝时,如果HashMap中的value是基础类型,可以通过构造函数来实现深拷贝。但是当value是引用类型时,简单的构造函数方式就会存在问题。因为构造函数只是将引用复制给新HashMap的value,这样新旧HashMap中的value仍然指向同一个对象,修改其中一个HashMap的value会影响另一个HashMap。 要实现HashMap深拷贝,我们可以借助序列化和反序列化的方式。通过序列化原HashMap对象,将其转化为字节流,然后再通过反序列化创建一个新的HashMap对象,这样就可以得到一个全新的HashMap,并且其中的value也是全新的对象。这种方式可以确保对新HashMap的操作不会影响到原HashMap。 除了使用Java自带的序列化和反序列化方式,还可以使用第三方库,比如Apache Commons Lang库中的SerializationUtils类提供了对对象的深拷贝功能。使用这个类可以方便地实现HashMap深拷贝操作。 综上所述,HashMap深拷贝可以通过序列化和反序列化的方式来实现,确保新HashMap中的value是全新的对象,不会受到原HashMap的影响。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [复制复制-以HashMap为例](https://blog.csdn.net/lzhfdxhxm/article/details/117199833)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [HashMap 深拷贝](https://blog.csdn.net/yy_diego/article/details/123123459)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值