前言

事件起因是由于同事使用ArrayList的带参构造方法进行ArrayList对象复制,修改新的ArrayList对象中的元素(对象)的成员变量时也会修改原ArrayList中的元素(对象)的成员变量。

下面会通过复盘代码向大家重现遇到的问题

复盘代码

用户类

public class User {

private Integer id;

private String name;

public User(Integer id, String name) {

this.id = id;

this.name = name;

}

@Override

public String toString() {

return "User{" +

"id=" + id +

", name='" + name + '\'' +

'}';

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

问题重现示例

import java.util.ArrayList;

import java.util.List;

public class ArrayListReference {

public static void main(String[] args) {

// 原用户列表

List users = new ArrayList<>();

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

users.add(new User(i, "test"));

}

// 新用户列表

List newUsers = new ArrayList<>(users);

for (int j = 0; j < newUsers.size(); j++) {

// 修改新用户列表的用户名

newUsers.get(j).setName(String.valueOf(j));

}

// 打印新用户列表

System.out.println("newUsers:" + newUsers);

// 重新打印原用户列表

System.out.println("After update newUsers,users:" + users);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

示例运行结果

users:[User{id=0, name='test'}, User{id=1, name='test'}, User{id=2, name='test'}, User{id=3, name='test'}, User{id=4, name='test'}, User{id=5, name='test'}, User{id=6, name='test'}, User{id=7, name='test'}, User{id=8, name='test'}, User{id=9, name='test'}]

newUsers:[User{id=0, name='0'}, User{id=1, name='1'}, User{id=2, name='2'}, User{id=3, name='3'}, User{id=4, name='4'}, User{id=5, name='5'}, User{id=6, name='6'}, User{id=7, name='7'}, User{id=8, name='8'}, User{id=9, name='9'}]

After update newUsers,users:[User{id=0, name='0'}, User{id=1, name='1'}, User{id=2, name='2'}, User{id=3, name='3'}, User{id=4, name='4'}, User{id=5, name='5'}, User{id=6, name='6'}, User{id=7, name='7'}, User{id=8, name='8'}, User{id=9, name='9'}]

1

2

3

分析

问题

为什么使用了ArrayList的构造方法重新构造一个新的ArrayList后,操作新ArrayList对象中的元素时会影响到原来的ArrayList中的元素呢?

首先需要分析ArrayList的构造方法

ArrayList源码分析

下面是示例中调用的ArrayList构造方法的源码

public ArrayList(Collection extends E> c) {

elementData = c.toArray();

if ((size = elementData.length) != 0) {

if (elementData.getClass() != Object[].class)

// 此处为关键代码,此处就是数组元素的复制方法

elementData = Arrays.copyOf(elementData, size, Object[].class);

} else {

this.elementData = EMPTY_ELEMENTDATA;

}

}

1

2

3

4

5

6

7

8

9

10

从源码中得知数组复制的关键代码为

elementData = Arrays.copyOf(elementData, size, Object[].class);

1

下面进入Arrays.copyOf()的源码进行研究

public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {

@SuppressWarnings("unchecked")

// 构造一个新的数组对象

T[] copy = ((Object)newType == (Object)Object[].class)

? (T[]) new Object[newLength]

: (T[]) Array.newInstance(newType.getComponentType(), newLength);

// 将原数组元素复制到新数组中

System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));

return copy;

}

1

2

3

4

5

6

7

8

9

10

从上面的源码得知关键代码为

System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));

1

以下为System.arraycopy()方法的源码

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

1

由于System.arraycopy()方法为native方法,很难跟踪其实现代码。不过可以从方法注释中可以知道这个方法的特点:

Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array. A subsequence of array components are copied from the source array referenced by src to the destination array referenced by dest. The number of components copied is equal to the length argument. The components at positions srcPos through srcPos+length-1 in the source array are copied into positions destPos through destPos+length-1, respectively, of the destination array.

翻译结果为

将数组从指定的源数组(从指定位置开始)复制到目标数组的指定位置。将数组组件的子序列从src引用的源数组复制到dest引用的目标数组,复制的组件数量等于length参数。源数组中通过srcPos+length-1位置的组件分别复制到目标数组中通过destPos+length-1位置的destPos。

既然ArrayList的构造方法是复制新的数组,那么是为什么呢?这里提前透露一下结论:数组元素为对象时,实际上存储的是对象的引用,ArrayList进行数组复制也只是复制了对象的引用。所以才会出现一开始说的问题

再次验证

下面将会使用一个数组的复制示例验证结论,使用==来比较对象引用是否相同

问题重现示例

import java.util.Arrays;

public class ArrayReference {

public static void main(String[] args) {

// 原用户列表

User[] users = new User[10];

for (int i = 0; i < users.length; i++) {

users[i] = (new User(i, "test"));

}

// 新用户列表

User[] newUsers = Arrays.copyOf(users, users.length);

for (int j = 0; j < users.length; j++) {

// 比较对象引用

System.out.println(j + ":" + (users[j] == newUsers[j]));

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

示例运行结果

0:true

1:true

2:true

3:true

4:true

5:true

6:true

7:true

8:true

9:true

1

2

3

4

5

6

7

8

9

10

结果分析

从运行结果中可以得知,上面提出的结论是正确的。即数组元素为对象时,实际上存储的是对象的引用。

解决办法

解决方法很简单,只需要遍历对象数组中的元素,调用对象的构造方法构造新的对象并加入新的数组中即可

解决办法示例

public class ArrayListReferenceSolution {

public static void main(String[] args) {

// 原用户列表

List users = new ArrayList<>();

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

users.add(new User(i, "test"));

}

// 新用户列表

List newUsers = new ArrayList<>();

for (int j = 0; j < users.size(); j++) {

// 使用构造方法构造新的对象

newUsers.add(new User(users.get(j).getId(),users.get(j).getName()));

}

for (int k= 0; k < users.size(); k++) {

// 比较对象引用

System.out.println(k + ":" + (users.get(k) == newUsers.get(k)));

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

示例运行结果

0:false

1:false

2:false

3:false

4:false

5:false

6:false

7:false

8:false

9:false

1

2

3

4

5

6

7

8

9

10

结果分析

从运行结果可以得知,使用示例中的方法就可以复制出一个不会干扰原ArrayList的对象。

---------------------

java arraylist对象_Java中ArrayList的对象引用问题相关推荐

  1. java list原理_Java中ArrayList实现原理

    前言 这个分类中,将会写写Java中的集合.集合是Java中非常重要而且基础的内容,因为任何数据必不可少的就是该数据是如何存储的,集合的作用就是以一定的方式组织.存储数据.这里写的集合,一部分是比较常 ...

  2. java arraylist 重复_Java中ArrayList去除重复元素

    Java中ArrayList去除重复元素 //删除ArrayList中重复元素 public   static   void  removeDuplicate(ArrayList list)   { ...

  3. java arraylist 源代码_java中ArrayList的源代码是什么

    展开全部 package java.util; public class ArrayList extends AbstractList implements List, RandomAccess, C ...

  4. java list对象_JAVA中list

    Java 查找 List 中的最大值.最小值 Java 查找 List 中的最大值.最小值 java> List list = new ArrayList(); java.util.List l ...

  5. java arraylist 源代码_Java中ArrayList源码浅析

    ArrayList基本使用 public class ArrayListTest { public static void main(String[] args) { List list = new ...

  6. java用arraylist求和_Java中ArrayList的使用

    ArrayList类是一个特殊的数组--动态数组.来自于System.Collections命名空间:通过添加和删除元素,就可以动态改变数组的长度. 优点: 1.支持自动改变大小 2.可以灵活的插入元 ...

  7. java 组合对象_Java 中组合模型之对象结构模式的详解

    Java 中组合模型之对象结构模式的详解 一.意图 将对象组合成树形结构以表示"部分-整体"的层次结构.Composite使得用户对单个对象和组合对象的使用具有一致性. 二.适用性 ...

  8. java教程对象_Java基础教程之对象引用

    我们之前一直在使用"对象"这个概念,但没有探讨对象在内存中的具体存储方式.这方面的讨论将引出"对象引用"(object reference)这一重要概念. 对象 ...

  9. java 遍历对象_java中some(),every()循环遍历,Object.getOwnPropertyNames()遍历对象的属性...

    由于java知识的应用具有一定的广泛性,所以它经常会应用到我们的日常使用当中.那么今天就为大家介绍一下java中some(),every()循环遍历,Object.getOwnPropertyName ...

  10. java to对象_java中同类对象之间的compareTo()和compare()方法对比分析

    首先我们都知道java中的比较都是同一类对象与对象之间的比较,就好像现实生活中比较人和人的年龄一样,你不会去把人的年龄和人的身高来比较,这显然是没有意义的. java中同类对象之间的比较又分为两种,基 ...

最新文章

  1. 英特尔90亿美元卖掉「起家业务」,SK海力士接盘
  2. Linux 内核开发 - Eclipse内核开发环境搭建
  3. php 网站数量,PHP给网站添加在线人数统计
  4. matlab图像的主视图,求助大佬【如用MATLAB实现满足投影关系的三视图】
  5. java和c语言的区别_都说C语言不会过时,但你是否还需要掌握其他语言?
  6. 怎样把任意exe程序注册成windows系统服务(手动注册服务)
  7. scala代码示例_Scala注释示例
  8. Dockerfile 中的 CMD 与 ENTRYPOINT
  9. python3 学习日志 Microsoft Office 编程
  10. keil教程——解压缩BCD码
  11. 纳维-斯托克斯方程:行到水穷处,坐看云起时
  12. The maximum number of tolerable server reconnection errors has been reached
  13. Linux 学习之路-------苦行,只为修得正果。每日三省吾身
  14. R语言方差分析ANOVA
  15. 在线工作计划安排日历表工具
  16. 三农经济的空缺,农业理财平台还能回暖吗?
  17. Koahub微信商城系统免费专区
  18. YC-Framework版本更新:V1.0.9
  19. 高性能计算之九-GPU在ANSYS高性能仿真计算中的应用
  20. 软件工程,一步一个台阶

热门文章

  1. android重写view和viewgroup的区别
  2. C++基础知识面试精选100题系列(21-30)[C++ basics]
  3. 找出一个字符串中的数字
  4. mysql 1045 拒绝远程链接
  5. MyBatis-Plus updateById方法更新不了空字符串/null解决方法
  6. CF1106F Lunar New Year and a Recursive Sequence(矩阵快速幂+bsgs+exgcd)
  7. 现代浏览器探秘(part 1):架构
  8. javascript 字符串中单引号和双引号区别
  9. 如何为SUSE配置IP地址,网关和DNS
  10. 腾讯2014在广州站实习生offer经验(TEG-开发背景)