阅读 94

list的add(int index,E e)用法踩坑分析以及【解决+分析】list集合循环添加对象被覆盖问题

add(int index ,E e)用法分析

事情的开始是打算for循环list存数据 但是到最后数据取出的总是最后存进去的那个,前面的数据都被覆盖了。 就看到了add(int index ,E e)方法,想着通过index一个索引插入一个数据,肯定就没有毛病了。但是并没有想象的那么顺利。 在用add(int index ,E e)方法时,后台会报错

java.lang.IndexOutOfBoundsException: Index: 1, Size: 0 复制代码

索引的值超出了list的size,原本的概念是list不是可以自己扩容吗,怎么size还会是0; 但是仔细分析下add(int index ,E e)这个方法找到了原因。 这是add(int index ,E e)这个方法

 /**      * Inserts the specified element at the specified position in this      * list. Shifts the element currently at that position (if any) and      * any subsequent elements to the right (adds one to their indices).      *      * @param index index at which the specified element is to be inserted      * @param element element to be inserted      * @throws IndexOutOfBoundsException {@inheritDoc}      */     public void add(int index, E element) {         rangeCheckForAdd(index);           ensureCapacityInternal(size + 1);  // Increments modCount!!         System.arraycopy(elementData, index, elementData, index + 1,                          size - index);         elementData[index] = element;         size++;     } 复制代码

方法首先会执行rangeCheckForAdd(index);再看看这个方法:

 /**      * A version of rangeCheck used by add and addAll.      */     private void rangeCheckForAdd(int index) {         if (index > size || index < 0)             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));     } 复制代码

也就是说我们使用这个方法的时候要保证index<size

index会和ArrayList的私有变量size做比较,那问题就是size到底是多少?什么时候会被改写?

JDK1.6和JDK1.7的实现方式发生了变化,我们以1.7为例子,

添加元素成功后会执行size++,删除元素成功会执行size--。也就是说size是元素个数,并不是数组的长度。

所以不管你设置数组长度是多长,如果没有存入元素,size还是0。

所以总结add(int index ,E e)只适用于list在已存入元素后,在size的范围内添加元素,同时在添加的位置会把原本的元素向后挤一个单位

【解决+分析】list集合循环添加对象被覆盖问题

解决对象被覆盖问题还是需要用add(E e)方法。

解决方法有两种: 1.循环外定义变量,循环内实例化对象赋值 2.循环内定义变量并实例化对象

建议用第一种解决方法,理由: 第一种方法节省大量栈空间内存

代码如下

//准备一个Teacher类 只有一个id属性 static class Teacher { private String Id; public String getId() { return Id; } public void setId(String Id) { this.Id = Id; } public String toString() { return "Teacher [Id=" + Id + "]"; } } public static void main(String[] args) { //list1代码块  最后添加会出错【对象重复】 { List<Teacher> list1 = new ArrayList<>(); //添加出错原因,在for循环外实例化对象 Teacher t = new Teacher(); for (int i = 0; i < 3; i++) { t.setId("00" + i); list1.add(t); } System.out.println("list1:" + list1); System.out.println(""); } //list2代码块  推荐的解决办法 { List<Teacher> list2 = new ArrayList<>(); //解决办法1:for循环外定义变量,循环内实例化对象 Teacher t = null; for (int i = 0; i < 3; i++) { t = new Teacher(); t.setId("0" + i); list2.add(t); } System.out.println("list2:" + list2); System.out.println(""); } //list3  解决办法  不推荐哦 { List<Teacher> list3 = new ArrayList<>(); for (int i = 0; i < 3; i++) { //解决办法2:循环内实例化对象 Teacher t = new Teacher(); t.setId("0" + i); list3.add(t); } System.out.println("list3:" + list3); } } 复制代码

运行结果: 在这里插入图片描述 list1是错误添加,list2和list3是解决办法。

然后再具体分析一下, list1为什么会添加重复的对象 list2为什么会比list3节省大量栈空间

在这里插入图片描述 如图,我们每次实例化一个对象,如:Teacher t = null; t = new Teacher(); Teacher t = null;相当于在栈空间开辟一块内存存放引用地址【这个地址应该是十六进制的一串数字,此处用*代替】 t = new Teacher();相当于引用地址值指向堆空间中的实际值。

对于list1,当我在循环外实例化对象时,就是在栈空间开辟了一块名字为t的内存,指向了堆空间的内存,此时堆空间内存存放值为null。然后,for循环为堆空间内存中的对象赋值,每次循环相当于t指向堆空间。而list集合每次添加的只是对象的引用值,而非堆空间的实际值,所以,**每次循环添加的都是栈空间的引用地址值,都是同一个对象,最后一次循环确定了这个对象的值。**如图: 在这里插入图片描述

对于list3,对象的实例化放在了循环里面,于是,每次循环都会在栈空间重新开辟一块内存空间,循环了多少次,就开辟了多少次空间,显然很浪费,还可能会导致栈空间内存溢出。 如图: 在这里插入图片描述

所以建议使用list2,将开辟栈空间内存放在循环外面,每次循环只是重新指向一个新的值。如图:在这里插入图片描述

如果感到有用就点个赞冒个泡让我看到你哈哈哈


作者:仙儿仙儿
链接:https://juejin.cn/post/7022832863414321166


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐