java 6种拷贝集合方式及其性能差异对比

文章对比了不同Java对象拷贝方法的性能,包括ArrayList构造、for循环、Stream流、Spring的BeanUtils以及Hutool工具,发现本地数组拷贝最快,而Hutool在不同版本间有显著优化,JSON序列化则是完全深拷贝,需谨慎选择拷贝策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

测试背景
在项目中很多地方需要拷贝对象集合、A类型对象的集合转换到B类型这种操作,但是这种操作的完成是有各种写法的,每种写法的性能可能不一样,因此对比一下各个写法的性能差异,选择最优解。
对一个有1000个对象的List复制1000次
ActivityCouponVO 对象有35个字段,各类型都有

	/**
     * 集合大小
     */
    private int len = 1000;
    /**
     * 循环次数
     */
    private int loop = 1000;
	List<ActivityCouponVO> getList(int len){
     // ... 生成一千个这个对象的集合,且每个字段赋值了
}

测试方法完整代码示范如下,后续各种方式的代码只写一次复制的代码示意,不写外层循环和耗时打印这些了
在这里插入图片描述

ArrayList 构造方法拷贝

底层使用了Arrays.copyOf()方法,这个方法底层又用了System.arraycopy方法,这个方法是native方法,使用本地实现,(一般为c++),直接操作内存复制,效率高
注意:new ArrayList<>(list)、Arrays.copyOf()、System.arraycopy这三个都是浅拷贝!!!

List<ActivityCouponVO> list = getList(len);
ArrayList<ActivityCouponVO> activityCouponVOS = new ArrayList<>(list);

运行1000次耗时 1ms

for循环拷贝

这个直接add 对象 也是浅拷贝,拷贝的是对象的内存地址
如果要深拷贝则循环的时候每次new一个新的对象并复制属性值

			List<ActivityCouponVO> list = getList(len);
			List<ActivityCouponVO> collect = new ArrayList<>(len);
            for (ActivityCouponVO activityCouponVO : list) {
                collect.add(activityCouponVO);
            }

运行1000次耗时 14ms

Stream流 collect实现拷贝

浅拷贝

List<ActivityCouponVO> list = getList(len);
List<ActivityCouponVO> collect = list.stream().collect(Collectors.toList());

运行1000次耗时 54ms

Stream流+spring的BeanUtils实现拷贝

这种拷贝方式是第一层属性深拷贝,嵌套的对象是浅拷贝

List<ActivityCouponVO> list = getList(len);
List<ActivityCouponVO> activityCouponVOS = list.stream().map(o -> {
                ActivityCouponVO activityCouponVO = new ActivityCouponVO();
                BeanUtils.copyProperties(o, activityCouponVO);
                return activityCouponVO;
            }).collect(Collectors.toList());

运行1000次耗时 2468 ms

Hutool工具实现拷贝

这种拷贝方式是第一层属性深拷贝,嵌套的对象是浅拷贝

List<ActivityCouponVO> list = getList(len);
List<ActivityCouponVO> activityCouponVOS = BeanUtil.copyToList(list, ActivityCouponVO.class);

Hutool 5.7.13版本运行1000次耗时 64674 ms

(看了下源码是因为加锁了)

Hutool 5.8.24版本运行1000次耗时 15874 ms

(新版本里没有加锁了,而且还做了优化,例如properties缓存)

JSON序列化

完全深拷贝

JSONObject.parseArray(JSONObject.toJSONString(list), ActivityCouponVO.class);

运行1000次 耗时 3387 ms

结论

不需要深拷贝时建议使用
Arrays.copyOf()
System.arraycopy
这种本地实现的拷贝数组方式
深拷贝时可以使用for循环、stream流、BeanUtils工具等方式实现,但是要注意使用工具的时候看看工具的源码实现,有可能里面有锁,然后有性能的坑~

Java 中,可以使用以下两种方式实现集合的深拷贝: 1. 使用序列化与反序列化 这种方式需要将集合对象先序列化成字节数组,再通过反序列化生成一个新的对象。由于序列化会将对象的所有属性都进行深拷贝,因此可以实现集合的深拷贝。示例代码如下: ```java public static <T extends Serializable> List<T> deepCopy(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); @SuppressWarnings("unchecked") List<T> dest = (List<T>) in.readObject(); return dest; } ``` 2. 遍历拷贝 这种方式需要手动遍历集合中的每个元素,对每个元素进行深拷贝,最后将所有拷贝后的元素添加到一个新的集合中。示例代码如下: ```java public static <T extends Serializable> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException { List<T> dest = new ArrayList<T>(src.size()); for (T element : src) { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(byteOut); out.writeObject(element); ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream in = new ObjectInputStream(byteIn); @SuppressWarnings("unchecked") T copy = (T) in.readObject(); dest.add(copy); } return dest; } ``` 需要注意的是,这两种方式都要求集合中的元素必须实现 Serializable 接口。如果集合中的元素没有实现 Serializable 接口,则需要手动实现深拷贝方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HumorChen99

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值