java中修改List的对象元素时碰到的坑

case1

case1:

@Data
class Person {
    Integer age;
    String name;

    public Person(Integer age, String name) {

        this.age = age;
        this.name = name;
    }
}
List<Person> v1List = new ArrayList<>();
v1List.add(new Person(11, "小刚"));
v1List.add(new Person(12, "小红"));
List<Person> v2List = new ArrayList<>();
v2List.addAll(v1List);
v2List.forEach(person -> { // 改变v2List的每个元素的年龄为0
    person.setAge(0);
});
logger.info("v1List: {}", v1List); 

v1List 输出为[{age:0, name: “小刚”}, {age:0, name: “小红”}]
验证:
在这里插入图片描述

问:为什么修改v2List的元素会影响到原列表v1List?
答:因为v1List中装的是对象,这里的对象相当于是引用,即指针,指向的是内存中真正存储的这个对象的地址。
所以当我们将v1List中所有对象就赋值给v2List的时候,相当于把引用或者指针复制了一份到v2List中,修改v2List的元素相当于修改v1List中的对应元素,因为他们的引用相同都指向同一个内存地址
可以参考上面这幅图的框住的部分,可以看到v1List的第一个元素的内存地址和v2List的对应元素的内存地址相同,第二个元素同样如此,所以当我这样修改v2List的时候,v1中的元素也会变,大家一定要注意这个坑。

case2

case2:
List<Integer> v1List = new ArrayList<>();
v1List.add(1);
v1List.add(2);
List<Integer> v2List = new ArrayList<>();
v2List.addAll(v1List); 
v2List.forEach(integer -> {
	integer = 10; //试图改变v2List的元素的值均为10,但是没有修改成功!!
	logger.info(System.identityHashCode(integer)); // 获取integer的内存地址
});
logger.info("v1List: {}", v1List); 
logger.info("v2List: {}", v2List); 

v1List和v2List输出值为1, 2
也就是说如果把list中存储的元素设置为基本数据类型,则不会产生错误。

问1:v1List和v2List的对应元素的内存地址是不是相同的?
答:是,因为两个List的元素都是引用类型,他们对应位置上的元素都指向同一个内存地址。可以参考下图,
在这里插入图片描述
问2:为什么List中存放基本数据类型数据的包装类时候运行上面这个程序不会产生case1的bug呢?int,long和float是基本数据类型,但是对应的包装类Integer,Long, Float还是属于引用类型的啊,为什么的不会产生错误呢
答:Integer,Long, Float属于包装类型,本身是immutable(不可更改)的,体现在Integer,Float类没有set方法上这就是说当执行到integer = 10;这一句的时候,integer的内存地址会发生变化,也就是说存放新值的地址更改了,并不会影响到v2List中的各个引用对象

问3: 为什么Integer,Long, Float,String等包装类型不能更改?
答:因为这样做有一个巨大的好处:在多线程环境下,因为包装类型的不可更改性,使得程序员开发时无需考虑线程的安全性,所有的更改操作都只会在新创建的对象上完成,对原对象没有影响。强烈建议参考下面这篇文章:Java’s primitive wrappers are written in stone

case3

如果一个List里装的是字符串对象,则也不会产生case1中碰到的错误

case3:
List<String> strs1 = new ArrayList<>(); // line 1
strs1.add("a");  // line 2
strs1.add("b");  // line 3
List<String> strs2 = new ArrayList<>();  // line 4
strs2.addAll(strs1);  // line 5
strs2.set(0, "aaa"); // 改变strs2的第一个元素的值, line 6
logger.info("strs1: {}", strs1);  //line 7

输出为a,b

问:为什么List中存放字符串时也不会产生bug呢?
答:这是因为在java中把所有的不同的字符串都放到了字符串常量池中,一旦产生了一个string是常量池中没有的,就会将其加入到池子中,然后将新的对象的引用指向这个新常量。 以case3为例,执行到line 4时,常量池中有"a"和"b",当执行万line 6时,池子中有"a"和"b"和"aaa"(因为aaa是新的,所以会加入池子中), 此时strs1.get(0)和get(1)指向池子中的"a"和“b“,strs2的get(0)指向"aaa", get(1)指向不变。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值