【设计模式】原型模式:如何快速的克隆出一个对象

BlogStats blogStats = entry.getValue();

if(blogStats.getStatus() == 0){

changeDatas.put(id,blogStats);

}

}

return changeDatas;

}

public static void main(String[] args) {

print(mapCache);

//获取修改的数据

Map<Integer, BlogStats> changeData = getChangeData();

//更新内存数据

update(changeData);

//将修改的博客状态改为1并写回数据库,这段省略

System.out.println(“更新过后=====================”);

print(mapCache);

}

private static void print( Map<Integer, BlogStats> map ){

for(Map.Entry<Integer, BlogStats> entry : map.entrySet()){

System.out.println(“key:”+entry.getKey()+" value:"+entry.getValue());

}

}

}

这是改造之后的代码,解释一下上面的代码,

第一:我不在往数据库中获取所有的博客数据,而是通过一个字段status表示这条记录是否更新过,只查询需要更新的博客信息即可。

第二:再修改的时候需要判断再数据库中查询出来的数据是否在内存中存在,如果存在,执行修改操作,如果不存在,执行添加操作。

按照上面的思路,我们的运行结果应该是博客id在0-7的信息无变化,博客id在8-9被修改,博客id等于10的被新增,那结果是不是这样呢?我们直接来看运行结果

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=8, viewNum=8, commentNum=8, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=9, viewNum=9, commentNum=9, status=1)

更新过后=====================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=10, viewNum=10, commentNum=10, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=11, viewNum=11, commentNum=11, status=1)

key:10 value:BlogStats(id=10, title=java是世界上最好的语言10, likeNum=12, viewNum=12, commentNum=12, status=1)

Process finished with exit code 0

从打印的结果来看,id在0-7的没有发生改变,8-9发生了改变,并且新增了一条id为10的新数据,所以这次的改造时成功的,现在已经优化的很好的,但还没有使用到设计模式中的原型模式,那我们还有必要使用吗?

我们之前的操作是直接基于成员变量mapCache,但是我们现在需求稍微做了一下改动,我现在希望,我们需要一次性计算好结果在替换掉成员变量mapCache,而不是一次一次的替换

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 开源分享

,所以这个时候上面的这种方法就不行了,out了,那我们应该怎么办呢?很多人都想到了

  • 第一步:从数据库中获取到status = 1 的所有数据,也就是已经更新过的数据,这个数据和内存中的一样。

  • 第二步:查询出status=0的所有数据,也就是没有更新过的数据。

  • 第三步:新建一个map用于计算修改后的结果。

  • 第四步:将新建的map替换掉成员变量mapCache。

这样基本上就大功告成了,但是你发现一个问题没有,那就是在第一步的时候会耗费大量的时间,为什么这么说呢?如果数据量过大,HashMap的存储是需要进行hash的计算以及还有可能发生的hash碰撞,所以对象的创建会耗费大量的时间,我们的内存中又存在着一份和我们数据库中差不多的数据,为什么我们不使用它来进行操作呢?这个时候你可能疑惑了,不是说不能对他进行修改吗,要等所有数据修改完成之后一次性替换吗?这个时候原型模式就要上场了。

原型模式两大数据拷贝

=========================================================================

不知道大家有没有听过这句话,new出来的在堆里,引用的在栈中,那我们一起来看看Java的两种拷贝模式,浅拷贝和深拷贝。

浅拷贝


浅拷贝就是拷贝指向对象的指针(java中的引用地址),意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存,然而当内存销毁的时候,指向这个内存空间的所有指针需要重新定义,不然会造成野指针错误。

我们来画个图说明一下,算了,不画了,实在找不到比较好的画图工具,画的太难看了,还不如直接讲,如果有好的画图工具,可以评论告诉我哦,十分感谢。

通俗一点来说,浅拷贝就是拷贝数据的引用地址,并没有拷贝真正的数据,也就是说,拷贝出来的对象和原始对象共享一套数据,也就是说拷贝的数据发生改变,原数据也会发生改变,原数据发生改变,拷贝的数据也会发生改变,因为他们共享一套数据。

下面我们使用浅拷贝来实现一下我们上面的demo,由于Map是没有clone()方法的,所以我需要将之前的Map改成HashMap,请看demo

package com.ymy.test;

import com.ymy.entity.BlogStats;

import java.util.HashMap;

import java.util.Map;

public class PrototypeDesignPattern {

/**

  • 内存中存放的数据

  • key:博客id

  • value:博客信息

*/

private static HashMap<Integer, BlogStats> mapCache = new HashMap<Integer, BlogStats>();

/**

  • 数据库存放的数据

  • key:博客id

  • value:博客信息

*/

private static HashMap<Integer, BlogStats> mapMysql = new HashMap<Integer, BlogStats>();

/**

  • 最近一次的修改时间

*/

private static Long updateTime = 1585882804208L;

static {

BlogStats blogStats = null;

//初始化内存中java的博客信息

for(int i = 0; i< 10 ;++i){

blogStats = BlogStats.builder()

.id(i)

.title(“java是世界上最好的语言”+i)

.likeNum(i)

.viewNum(i)

.commentNum(i)

.status(1)

.build();

mapCache.put(i,blogStats);

}

//初始化数据库中java的博客信息

for(int i = 0; i<= 10 ;++i){

blogStats = BlogStats.builder()

.id(i)

.title(“java是世界上最好的语言”+i)

.likeNum(i+2)

.viewNum(i+2)

.commentNum(i+2)

.status(i >= 8 ? 0 : 1)

.build();

mapMysql.put(i,blogStats);

}

}

/**

  • 每天定时更新

*/

private static void update(Map<Integer, BlogStats> changeData){

//这是浅拷贝

HashMap<Integer,BlogStats> oldData = (HashMap<Integer, BlogStats>) mapCache.clone();

for(Map.Entry<Integer, BlogStats> entry : changeData.entrySet()){

Integer id = entry.getKey();

BlogStats blogStats = entry.getValue();

blogStats.setStatus(1);

if(oldData.containsKey(id)){

BlogStats bs = oldData.get(id);

bs.setLikeNum(blogStats.getLikeNum());

bs.setViewNum(blogStats.getViewNum());

bs.setCommentNum(blogStats.getCommentNum());

}else{

oldData.put(id,blogStats);

}

}

System.out.println(“克隆的HashMap最终结果如下=====================”);

print(oldData);

System.out.println(“克隆对象修改完成之后,原数据如下================”);

print(mapCache);

mapCache = oldData;

}

/**

  • 根据更新的状态获取到最新需要修改的数据

  • 由于这里仅仅只是模拟,所以这里我就随便造几条数据,你们主要看思想即可

  • @return

*/

public static Map<Integer,BlogStats> getChangeData(){

//在数据库中查询还没有更新过的数据

//由于我是模拟,所以我直接伪造了几条数据充当需要修改的数据,还请见谅

Map<Integer,BlogStats> changeDatas= new HashMap<Integer,BlogStats>();

for(Map.Entry<Integer, BlogStats> entry : mapMysql.entrySet()){

Integer id = entry.getKey();

BlogStats blogStats = entry.getValue();

if(blogStats.getStatus() == 0){

changeDatas.put(id,blogStats);

}

}

return changeDatas;

}

public static void main(String[] args) {

System.out.println(“内存中最开始存在的数据”);

print(mapCache);

//获取修改的数据

Map<Integer, BlogStats> changeData = getChangeData();

//更新内存数据

update(changeData);

//将修改的博客状态改为1并写回数据库,这段省略

System.out.println(“更新过后内存中的数据=====================”);

print(mapCache);

}

private static void print( Map<Integer, BlogStats> map ){

for(Map.Entry<Integer, BlogStats> entry : map.entrySet()){

System.out.println(“key:”+entry.getKey()+" value:"+entry.getValue());

}

}

}

这里面做的修改就是使用了一个局部变量oldData接收mapCache克隆结果,然后对克隆的结果做修改,代码中使用的mapCache.clone()属于浅拷贝,之前说过浅拷贝的数据是共享的,那我们对拷贝过后的数据进行修改,那原数据会不会受到影响呢?我们一起看打印结果

内存中最开始存在的数据

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=8, viewNum=8, commentNum=8, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=9, viewNum=9, commentNum=9, status=1)

克隆的HashMap最终结果如下=====================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=10, viewNum=10, commentNum=10, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=11, viewNum=11, commentNum=11, status=1)

key:10 value:BlogStats(id=10, title=java是世界上最好的语言10, likeNum=12, viewNum=12, commentNum=12, status=1)

克隆对象修改完成之后,原数据如下================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=10, viewNum=10, commentNum=10, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=11, viewNum=11, commentNum=11, status=1)

更新过后内存中的数据=====================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=10, viewNum=10, commentNum=10, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=11, viewNum=11, commentNum=11, status=1)

key:10 value:BlogStats(id=10, title=java是世界上最好的语言10, likeNum=12, viewNum=12, commentNum=12, status=1)

Process finished with exit code 0

我们来看:克隆的HashMap最终结果如下=====================

在这里插入图片描述

修改了两行,新增了一行,从上面的打印结果可以看出原数据也发生了相应的变化

在这里插入图片描述

虽然克隆对象中新增的对象没有在原对象中增加,但是修改的属性在原对象中也发生了改变,使用浅拷贝的同学一定要注意这一点。

所以浅拷贝就不能实现我们的需求了,前面还有一个深拷贝还没有说,那它能不能满足我们的要求 呢?

深拷贝


一个引用对象一般来说由两个部分组成:一个具名的Handle,也就是我们所说的声明(如变量)和一个内部(不具名)的对象,也就是具名Handle的内部对象。它在Manged Heap(托管堆)中分配,一般由新增引用对象的New方法是进行创建。深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人。比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等。

根据介绍来说,深度拷贝是可以满足我们需求的,那我们因该如何实现深拷贝呢?

序列化

序列化很好理解,就是将对象序列化,然后再反序列化,这两步之后就会得到一个全新的对象。

核心代码

/**

  • 深拷贝

  • @param obj

  • @return

*/

private static Object deepColne(Object obj) {

try {

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

objectOutputStream.writeObject(obj);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);

return objectInputStream.readObject();

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

return null;

}

这个就是通过序列化再反序列化的核心代码,然后再看一下完整demo

package com.ymy.test;

import com.ymy.entity.BlogStats;

import java.io.*;

import java.util.HashMap;

import java.util.Map;

public class PrototypeDesignPattern {

/**

  • 内存中存放的数据

  • key:博客id

  • value:博客信息

*/

private static HashMap<Integer, BlogStats> mapCache = new HashMap<Integer, BlogStats>();

/**

  • 数据库存放的数据

  • key:博客id

  • value:博客信息

*/

private static HashMap<Integer, BlogStats> mapMysql = new HashMap<Integer, BlogStats>();

/**

  • 最近一次的修改时间

*/

private static Long updateTime = 1585882804208L;

static {

BlogStats blogStats = null;

//初始化内存中java的博客信息

for (int i = 0; i < 10; ++i) {

blogStats = BlogStats.builder()

.id(i)

.title(“java是世界上最好的语言” + i)

.likeNum(i)

.viewNum(i)

.commentNum(i)

.status(1)

.build();

mapCache.put(i, blogStats);

}

//初始化数据库中java的博客信息

for (int i = 0; i <= 10; ++i) {

blogStats = BlogStats.builder()

.id(i)

.title(“java是世界上最好的语言” + i)

.likeNum(i + 2)

.viewNum(i + 2)

.commentNum(i + 2)

.status(i >= 8 ? 0 : 1)

.build();

mapMysql.put(i, blogStats);

}

}

/**

  • 每天定时更新

*/

private static void update(Map<Integer, BlogStats> changeData) {

//这是浅拷贝

HashMap<Integer, BlogStats> oldData = (HashMap<Integer, BlogStats>) deepColne(mapCache);

for (Map.Entry<Integer, BlogStats> entry : changeData.entrySet()) {

Integer id = entry.getKey();

BlogStats blogStats = entry.getValue();

blogStats.setStatus(1);

if (oldData.containsKey(id)) {

BlogStats bs = oldData.get(id);

bs.setLikeNum(blogStats.getLikeNum());

bs.setViewNum(blogStats.getViewNum());

bs.setCommentNum(blogStats.getCommentNum());

} else {

oldData.put(id, blogStats);

}

}

System.out.println(“克隆的HashMap最终结果如下=====================”);

print(oldData);

System.out.println(“克隆对象修改完成之后,原数据如下================”);

print(mapCache);

mapCache = oldData;

}

/**

  • 根据更新的状态获取到最新需要修改的数据

  • 由于这里仅仅只是模拟,所以这里我就随便造几条数据,你们主要看思想即可

  • @return

*/

public static Map<Integer, BlogStats> getChangeData() {

//在数据库中查询还没有更新过的数据

//由于我是模拟,所以我直接伪造了几条数据充当需要修改的数据,还请见谅

Map<Integer, BlogStats> changeDatas = new HashMap<Integer, BlogStats>();

for (Map.Entry<Integer, BlogStats> entry : mapMysql.entrySet()) {

Integer id = entry.getKey();

BlogStats blogStats = entry.getValue();

if (blogStats.getStatus() == 0) {

changeDatas.put(id, blogStats);

}

}

return changeDatas;

}

public static void main(String[] args) {

System.out.println(“内存中最开始存在的数据”);

print(mapCache);

//获取修改的数据

Map<Integer, BlogStats> changeData = getChangeData();

//更新内存数据

update(changeData);

//将修改的博客状态改为1并写回数据库,这段省略

System.out.println(“更新过后内存中的数据=====================”);

print(mapCache);

}

/**

  • 深拷贝

  • @param obj

  • @return

*/

private static Object deepColne(Object obj) {

try {

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

objectOutputStream.writeObject(obj);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);

return objectInputStream.readObject();

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

return null;

}

private static void print(Map<Integer, BlogStats> map) {

for (Map.Entry<Integer, BlogStats> entry : map.entrySet()) {

System.out.println(“key:” + entry.getKey() + " value:" + entry.getValue());

}

}

}

与浅拷贝相比,改动的地方并不多,只有克隆的地方发生了修改,其他地方不变

内存中最开始存在的数据

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=8, viewNum=8, commentNum=8, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=9, viewNum=9, commentNum=9, status=1)

克隆的HashMap最终结果如下=====================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=10, viewNum=10, commentNum=10, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=11, viewNum=11, commentNum=11, status=1)

key:10 value:BlogStats(id=10, title=java是世界上最好的语言10, likeNum=12, viewNum=12, commentNum=12, status=1)

克隆对象修改完成之后,原数据如下================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=8, viewNum=8, commentNum=8, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=9, viewNum=9, commentNum=9, status=1)

更新过后内存中的数据=====================

key:0 value:BlogStats(id=0, title=java是世界上最好的语言0, likeNum=0, viewNum=0, commentNum=0, status=1)

key:1 value:BlogStats(id=1, title=java是世界上最好的语言1, likeNum=1, viewNum=1, commentNum=1, status=1)

key:2 value:BlogStats(id=2, title=java是世界上最好的语言2, likeNum=2, viewNum=2, commentNum=2, status=1)

key:3 value:BlogStats(id=3, title=java是世界上最好的语言3, likeNum=3, viewNum=3, commentNum=3, status=1)

key:4 value:BlogStats(id=4, title=java是世界上最好的语言4, likeNum=4, viewNum=4, commentNum=4, status=1)

key:5 value:BlogStats(id=5, title=java是世界上最好的语言5, likeNum=5, viewNum=5, commentNum=5, status=1)

key:6 value:BlogStats(id=6, title=java是世界上最好的语言6, likeNum=6, viewNum=6, commentNum=6, status=1)

key:7 value:BlogStats(id=7, title=java是世界上最好的语言7, likeNum=7, viewNum=7, commentNum=7, status=1)

key:8 value:BlogStats(id=8, title=java是世界上最好的语言8, likeNum=10, viewNum=10, commentNum=10, status=1)

key:9 value:BlogStats(id=9, title=java是世界上最好的语言9, likeNum=11, viewNum=11, commentNum=11, status=1)

key:10 value:BlogStats(id=10, title=java是世界上最好的语言10, likeNum=12, viewNum=12, commentNum=12, status=1)

Process finished with exit code 0

这个时候我们发现,即使拷贝的数据发生了变化,原始数据还是原来的数据,这就是深度拷贝,深度拷贝有一个比较致命的缺点,那就是拷贝的时间太长,因为它不仅需要拷贝引用,同时还需要拷贝数据,相对于浅拷贝而言效率太差,当然深拷贝的实现方式不止这一种,还有很多,我在这里就不做过多的展示了。

这里有一点需要特别注意,深度拷贝的对象需要实现序列化,也就是:

在这里插入图片描述

否则会导致程序报错。

既然浅拷贝会导致数据共享,深拷贝印象对象创建的效率,那我们有没有一种这种的方案来解决上面的这个问题呢?可以先想一想,不要着急看下面,因为下面肯定有答案,没有的话我也肯定不会让你们想的。

浅拷贝+深拷贝


对于上面的需求来说,我们需要修改发生改变的博客信息,但是需要修改的数据并不是很多,可能只有几条,也有可能只有几十条,再几十万数据中修改几条使用深拷贝就有点过粪了,所以这里我们采用一种比较折中的方式:浅拷贝+深拷贝

如何实现呢?我们首先将所有的数据心境浅拷贝,然后遇到需要修改的数据再进行深拷贝即可。

改造代码

package com.ymy.test;

import com.ymy.entity.BlogStats;

import java.io.*;

import java.util.HashMap;

import java.util.Map;

public class PrototypeDesignPattern {

/**

  • 内存中存放的数据

  • key:博客id

  • value:博客信息

*/

private static HashMap<Integer, BlogStats> mapCache = new HashMap<Integer, BlogStats>();

/**

  • 数据库存放的数据

  • key:博客id

  • value:博客信息

*/

private static HashMap<Integer, BlogStats> mapMysql = new HashMap<Integer, BlogStats>();

/**

  • 最近一次的修改时间

*/

private static Long updateTime = 1585882804208L;

static {

BlogStats blogStats = null;

//初始化内存中java的博客信息

for (int i = 0; i < 10; ++i) {

blogStats = BlogStats.builder()

.id(i)

.title(“java是世界上最好的语言” + i)

.likeNum(i)

.viewNum(i)

.commentNum(i)

.status(1)

.build();

mapCache.put(i, blogStats);

}

//初始化数据库中java的博客信息

for (int i = 0; i <= 10; ++i) {

blogStats = BlogStats.builder()

.id(i)

.title(“java是世界上最好的语言” + i)

.likeNum(i + 2)

.viewNum(i + 2)

.commentNum(i + 2)

.status(i >= 8 ? 0 : 1)

.build();

mapMysql.put(i, blogStats);

}

}

/**

  • 每天定时更新

*/

private static void update(Map<Integer, BlogStats> changeData) {

//这是浅拷贝

HashMap<Integer, BlogStats> oldData = (HashMap<Integer, BlogStats>) mapCache.clone();

for (Map.Entry<Integer, BlogStats> entry : changeData.entrySet()) {

Integer id = entry.getKey();

BlogStats blogStats = entry.getValue();

blogStats.setStatus(1);

if (oldData.containsKey(id)) {

//我们先删除数据,因为浅拷贝对删除/新增是不会改变原数据的属性的

oldData.remove(id);

}

//这里再重新赋值

oldData.put(id,blogStats);

}

System.out.println(“克隆的HashMap最终结果如下=====================”);

print(oldData);

System.out.println(“克隆对象修改完成之后,原数据如下================”);

print(mapCache);

mapCache = oldData;

}

/**

  • 根据更新的状态获取到最新需要修改的数据

  • 由于这里仅仅只是模拟,所以这里我就随便造几条数据,你们主要看思想即可

  • @return

*/

public static Map<Integer, BlogStats> getChangeData() {

//在数据库中查询还没有更新过的数据

//由于我是模拟,所以我直接伪造了几条数据充当需要修改的数据,还请见谅

Map<Integer, BlogStats> changeDatas = new HashMap<Integer, BlogStats>();

for (Map.Entry<Integer, BlogStats> entry : mapMysql.entrySet()) {

Integer id = entry.getKey();

BlogStats blogStats = entry.getValue();

if (blogStats.getStatus() == 0) {

changeDatas.put(id, blogStats);

}

}

return changeDatas;

}

public static void main(String[] args) {

System.out.println(“内存中最开始存在的数据”);

print(mapCache);

//获取修改的数据

Map<Integer, BlogStats> changeData = getChangeData();

//更新内存数据

update(changeData);

//将修改的博客状态改为1并写回数据库,这段省略

System.out.println(“更新过后内存中的数据=====================”);

print(mapCache);

}

/**

  • 深拷贝

  • @param obj

  • @return

*/

private static Object deepColne(Object obj) {

try {

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);

objectOutputStream.writeObject(obj);

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());

ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);

return objectInputStream.readObject();

} catch (IOException e) {

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值