前言
相信很多看客都听闻过深拷贝、浅拷贝 , 但是在日常使用的过程中,是否真的有关心过或者遭遇过呢?
不啰嗦,一起来看看。
正文
接下来我通过示例,来复现一下 list.stream浅拷贝 这个事 :
首先是一个对象 Product :
/**
* 产品
*/
@Data
public class Product {
/**
* 级别
*/
private Integer level;
/**
* 售价
*/
private BigDecimal amount;
/**
* 库存
*/
private Long stockNum;
}
然后是模拟获取到的这个Product的一个list集合数据:
private static List<Product> getTestList() {
List<Product> products=new ArrayList<>();
Product product1=new Product();
product1.setLevel(100);
product1.setAmount(new BigDecimal("100"));
product1.setStockNum(100L);
Product product2=new Product();
product2.setLevel(200);
product2.setAmount(new BigDecimal("200"));
product2.setStockNum(200L);
Product product3=new Product();
product3.setLevel(300);
product3.setAmount(new BigDecimal("300"));
product3.setStockNum(300L);
products.add(product1);
products.add(product2);
products.add(product3);
return products;
}
接下来做什么, 比如说,我们对这list数据进行按照金额排序输出 以及按照 库存排序输出。
使用了 list.stream().sorted(xxx).collect(Collectors.toList())
public static void main(String[] args) {
List<Product> testList = getTestList();
List<Product> amountSortList = testList.stream()
.sorted(Comparator.comparing(Product::getAmount))
.collect(Collectors.toList());
List<Product> stockSortList = testList.stream()
.sorted(Comparator.comparing(Product::getStockNum).reversed())
.collect(Collectors.toList());
System.out.println("amountSortList :" +amountSortList.toString());
System.out.println("stockSortList :" +stockSortList.toString());
}
看到排序效果是很OK的 :
接下来我们来看看 浅拷贝的问题场景 :
我们对初始数据 list 进行动手脚,看下是否会影响 list.stream() 出来的新的list
动手脚 ① ,我们进行新增、删除、清除 这些操作 :
这时候有没有一些看客在想, 是不是这就是深拷贝呢?
对原数据操作,新出来的list没有被影响,这不就是深拷贝么?
错
继续看
动手脚 ② 我们修改 原数据里面的值 :
可以看到结果:
受影响了
到这,我们先不说原因和结论,继续看。
动手脚 ③ 我们修改 第一次list.stream() 出来的list里面的值 :
这时候,大家可以想下,有啥影响,下面的根据库存排序 用的是 testList 初始数据做排序的。
直接看下效果:
定论:
①list.stream 是浅拷贝
② list里面装对象, 无论是 add 、remove 、clear 都不会出现牵连影响
③ list里面装对象, 无论是针对初始list还是新的list, 做了set 编辑修改动作, 都会出现牵连影响
简述原由:
我用几幅图来说下
图一
list 里面装对象,对象其实都是同一份引用地址,我就故意画成下面这样,希望大家能看清晰些
图二
自己list针对引用product的内存地址引用,多少 与其他list没有关系
图三
无论你在哪个list,针对product做set操作,修改值, 那么这对象product内测引用地址,也就是相当于公共财产,大家都引用着, 所以哪里动了,用着的都是受害群体。
最后,如果为了避免以上场景出现,我们需要用到list的深拷贝,那么怎么去使用?
深拷贝
简单列举几样:
① 序列化
示例:
List<Product> newList = JSON.parseArray(JSON.toJSONString(testList), Product.class);
引用jar:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.69</version>
</dependency>
② 使用字节流重新生成
public static <T> List<T> doCopy(List<T> src) throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(src);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
List<T> dest = (List<T>) in.readObject();
return dest;
}
额外需要将实体类实现序列化
public class Product implements Serializable
使用:
List<Product> newList =doCopy(testList);
③ 用MapperFacade
引入jar:
<dependency>
<groupId>net.rakugakibox.spring.boot</groupId>
<artifactId>orika-spring-boot-starter</artifactId>
<version>1.9.0</version>
</dependency>
使用:
@Autowired
private MapperFacade mapperFacade;
List<Product> newList = mapperFacade.mapAsList(testList, Product.class);
④使用 MapStruct ,这个不单独列举了
⑤ 手写,循环,new对象,set赋值,再add,这个也不单独列举了
好了,该篇就到这,谢谢。