更新:

  在一次搜索“变量声明在循环体内还是循环体外”问题时,碰见了一个这样的代码,与本文类似,代码如下:

Document [] old ......//这是数据源
EntityDocument[] newArray = new EntityDocument[old.length];//自定义的类,为了把Document里数据保留下来避免Document被关联对象关闭而导致无法取出数据。
EntityDocument d = new EntityDocument();
for(int i=0;i<old.length;i++){
d.content = old[i].getContent();
d.key = old[i].getKey();
d......................
newArray[i] = d;//如此对象重用.....
}

  上面的代码最终结果会导致newArray数组中的每个元素都等于最后一个元素,原因就是每次向newArray中存储对象时,没有新建一个对象进行储存,从而导致错误。

  上面代码只需要将第3行的 EntityDocument d = new EntityDocument(); 放入循环体内即可实现正常功能。

  这种错误隐藏得比较深,所以要好好记住。

原文

  在刷《剑指OFFER》的时候,自己犯了一个错误,发现:在链表中存储一个对象时,如果该对象是不断变化的,则应该创建一个新的对象复制该对象的内容(而不是指向同一个对象),将这个新的对象存储到链表中。如果直接存储该对象的话,链表中的对象也会不断变化。基本数据类型和String则没有这种问题。

其实这归根结底是一个传值和传引用的问题:

  1.如果存储到链表中的是对象,则存储的是引用(地址),所以该地址上的内容变化时,会引起对象的变化。

  2.如果存到链表中的是基本数据类型或者String,存储的就是该数值,不会再发生变化了。(其实String是对象,存储的是引用,后面讨论)。

举个例子:

import java.util.ArrayList;

public class Test {
public static class Person {
int age=1;
}
public static void main(String[] args) {
//=======ArrayList存储String或者基础数据类型=========
ArrayList<String> list = new ArrayList<>();
String aString="abc";
list.add(aString);
System.out.println("before:"+list.toString());
aString="123";
System.out.println("after:"+list.toString()); //========ArrayList存储对象=======
ArrayList<Person> pList = new ArrayList<>();
Person a = new Person();
Person b = new Person();
b = a;
Person c = new Person();
c.age=a.age;
pList.add(a);
pList.add(b);
pList.add(c); System.out.print("before:");
for (Person person : pList) {
System.out.print(person.age+" "); //a,b,c的age此时都是1
}
System.out.println(); a.age=2;
System.out.print("after:");
for (Person person : pList) {
System.out.print(person.age+" "); //输出:2,2,1
}
//关键原因:b是和a指向同一个对象,c不是同一个对象
}
}

  

before:[abc]
after:[abc]
before:
after:

  上面的代码可以知道,存储Person这个对象时(存储的是地址),b和a其实是同一个地址,所以a指向的对象改变,会引起链表中的前两个结点(地址相同)改变,而如果要使存进链表的person存储的是a存储时的状态,只能新建一个对象c,令c的内容等于a,才在后面不会发生变化(因为该地址指向的内容没有再发生改变了)。

  关于String的讨论:其实String也是对象,存储的其实也是引用(地址),但为什么上面代码中before和after输出的内容都是“abc”呢?其实在aString="123";时,相当于aString=new String("123"),即aString指向了另一个对象,aString存储的地址变成了“123”的地址,但链表中存储的还是“abc”的地址,所以链表中的内容不变。

  更详细的传递讨论:值传递和引用传递讨论

【Java】链表中存储对象的问题的更多相关文章

  1. JAVA链表中迭代器的实现

    注:本文代码出自<java数据结构和算法>一书. PS:本文中类的名字定义存在问题,Link9应改为Link.LinkList9应该为LinkList.由于在同包下存在该名称,所以在后面接 ...

  2. Java 开发中的对象拷贝

    前言 在 Java 开发中,很多时候需要将两个属性基本相同的对象进行属性复制,比如 DO 转 VO等等. 本文主要介绍自己实现的简易拷贝工具类与 Spring 提供的属性拷贝的对比. Spring 提 ...

  3. COM结构化存储中存储对象或者流对象的命名规则

      COM结构化存储中存储对象或者流对象的命名规则

  4. java内存中的对象

    前记:几天前,在浏览网页时偶然的发现一道以前就看过很多遍的面试题,题目是:“请说出‘equals’和‘==’的区别”,当时我觉得我还是挺懂的,在心里答了一点(比如我们都知道的:‘==’比较两个引用是否 ...

  5. 面试题:JVM在Java堆中对对象的创建、内存结构、访问方式

    一.对象创建过程 1.检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载.解析.初始化,若没有,则进行类加载 ...

  6. Java操作Redis存储对象类型数据

    背景描述      关于JAVA去操作Redis时,如何存储一个对象的数据,大家是非常关心的问题,虽然官方提供了存储String,List,Set等等类型,但并不满足我们现在实际应用.存储一个对象是是 ...

  7. 在localStorage中存储对象数组并读取

    频繁ajax请求导致页面响应变慢. 于是考虑将数据存储在window.storage中,这样只请求一次ajax,而不需要频繁请求. 鉴于localstorage中只能存储字符串,所以我们要借助于JSO ...

  8. Java连接Redis,存储对象获取对象()byte和json),连接池

    Java连接Redis Jedis连接Redis,Lettuce连接Redis Jedis连接Redis 1. 创建maven项目 2. 引入依赖 <dependencies> <d ...

  9. java list中的对象去重原理

    /******************************************************************************* * * Copyright (c) W ...

随机推荐

  1. 【bzoj1044】木棍分割

    Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...

  2. 解题:CF1130E Wrong Answer

    题面 巧妙构造题 这种题一定要限制一个条件,使得在这个条件下能推出要叉的代码的式子 令序列$a$的第一个元素为负,其余元素为正,且保证序列中至少有两个元素,那么Alice的代码将会从第二个元素开始计算 ...

  3. 【CH6201】走廊泼水节

    题目大意:给定一棵树,要求增加若干条边,将其转化为完全图,且该完全图以该树为唯一的最小生成树,求增加的边权最小是多少. 题解:完全图的问题一般要考虑组合计数.重新跑一遍克鲁斯卡尔算法,每次并查集在合并 ...

  4. python+正态分布+蒙特卡洛预测男女身高概率!

    sklearn实战-乳腺癌细胞数据挖掘 https://study.163.com/course/introduction.htm?courseId=1005269003&utm_campai ...

  5. JVM调优命令-jstat

    JVM Statistics Monitoring Tool,是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载.内存.垃圾收集.JIT编译等运行数据.[性能分析] 命令格式 1 ...

  6. HDU 3595 every-sg模型

    多个子游戏同时进行,每个子游戏给出两个数a,b,可以将大的数减去k倍小的数,不能操作者输. 策略就是对于一个必胜的游戏要使得步数更长,对于一个必败的游戏使得步数最短. 以下都来自贾志豪的论文.. 对于 ...

  7. 原生JS 基础总结

    0. 好习惯 分号 ; 花括号 {}, var 弄清楚 null , undefined 区别 , isNaN, === 与 == 区别 1. prompt , confirm , alert 不同框 ...

  8. .NET面试题系列(七)IIS

    应用程序池的集成模式和经典模式的区别 应用程序池模式会影响服务器处理托管代码请求的方式. 如果托管应用程序在采用集成模式的应用程序池中运行,服务器将使用 IIS 和 ASP.NET 的集成请求处理管道 ...

  9. 鼠标样式 cursor 全总结

    本文地址:https://www.cnblogs.com/veinyin/p/10752805.html  最常用的 key  pointer   cursor: key; // 除了pointer, ...

  10. dialog 菜单

    dialog 菜单 # 默认将所有输出用 stderr 输出,不显示到屏幕 使用参数 --stdout 可将选择赋给变量 # 退出状态 0正确 1错误 窗体类型 --calendar # 日历 --c ...