概述
Iterator迭代器代码练习
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListIteratorTest {
public static void main(String[] args) {
/**
- ArrayList集合对象的Iterator迭代器对象的基本用法
*/
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
// Collection接口继承了Iterable接口,所以它的实现类需要重写iterable方法
// ArrayList中对于iterator()方法的重写是: return new Itr();
// iterator()用于获取集合对象的迭代器对象
// 可以看出迭代器对象必须依赖于集合对象
Iterator iterator = list.iterator();// lastRet=-1,cursor=0
// Itr类实现了Iterator接口,所以需要重写Iterator接口中定义的方法
// hasNext():用于判断迭代器将要迭代的下一个元素是否存在
// Itr类有两个成员变量:lastRet,cursor。
// 其中lastRet是迭代器正在迭代的元素的索引(若没有正在迭代的元素,lastRet=-1),cursor是迭代器将要迭代的下一个元素的索引
// 则hasNext()内部实现就是判断cursor的值是否为size,若cursor=size,则说明lastRet已经迭代到size-1,即集合最后一个元素,无法继续迭代
boolean hasNext = iterator.hasNext();//lastRet=-1,cursor=0
// next():用于获取迭代器将要迭代的下一个元素
// cursor指向的元素就是迭代器将要返回的下一个元素。而由于next()动作会导致lastRet,cursor后移一位。
// 所以用一个中间量 int i = cursor 保存next动作前的cursor值
// next动作中:cursor = cursor + 1;即将cursor后移一位
// lastRet = i;即将lastRet指向后移前的cursor值
// return elementData[lastRet];即获取lastRet指向的元素,并返回
iterator.next();//lastRet=0,cursor=1
// remove():用于删除迭代器正在迭代的元素
// 即删除lastRet指向的元素(前提是lastRet!=-1,即删除一个元素,要保证被删除的元素存在)
// 内部实现:是调用迭代器对象对应的集合对象的remove(lastRet)方法
// 由于ArrayList对象删除集合元素后,底层数组元素会发生移位,来填补被删除的元素位置。
// 所以cursor指向lastRet的位置
// lastRet = -1; 因为lastRet指向的元素已经被删除了,所以lastRet没有指向的元素了。
iterator.remove();//lastRet=-1,cursor=0
// foreachRemaining(Consumer consumer): 遍历处理剩下的元素
// 从该方法的名字可以看出foreach指遍历,Remaining指剩下的,consumer指处理遍历的每个元素
// 即遍历处理剩下的元素
// 注意:该方法遍历元素的起始位置是iterator对象的cursor,
// 即如果iterator对象之前没有做过任何next(),remove()操作的话,foreachRemaining是从cursor=0开始遍历
// 如果iterator对象已经操作过next(),remove()的话,则foreachRemaining从迭代器实际cursor值开始遍历
// 另外需要注意的foreachRemaining方法对cursor和lastRet的处理 和next()是不同的
// next()是调用一次,对cursor加1,lastRet指向最新的cursor-1的位置
// foreahRemaining是调用一次,直接将cursor值设置为size,lastRet=size-1
iterator.forEachRemaining(System.out::println);
System.out.println(“====================================”);
/**
-
ArrayList集合对象的Iterator迭代器迭代过程中出现ConcurrentModificationException引发原因
-
并发修改异常是指在迭代器迭代过程中,集合对象发生了结构化修改。
-
简单点理解就是:迭代器迭代过程中,对应集合对象的modCount成员属性值被改变了。
-
modCount是定义在AbstractList抽象类中的,专门给子类如ArrayList继承的成员属性。
-
ArrayList的
-
add,remove,clear,retainAll(改变了集合的size属性)
-
|| ensureCapacity,trimToSize(改变了集合的容量)
-
|| sort,replaceAll(改变了集合的所有元素)
-
操作都会改变modCount的值,这些操作统一称为ArrayList集合的结构化修改操作
*/
ArrayList list2 = new ArrayList();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list2.add(5);
/**
- 那么迭代器的哪些方法可能抛出并发修改异常呢?即哪些迭代器动作算是迭代动作呢?
*/
// 注意当iterator1对象初始化时,已经保存了集合对象的modCount值到自己属性expectedModCount中
// iterator1迭代时,会实时比较modCount是否和expectedModCount相等
// 若相等,则说明集合对象没有发生结构化修改
// 若不相等,则说明集合对象发生了结构化修改
Iterator iterator1 = list2.iterator();
iterator1.next();//先next一下,保证remove()不会抛出非法状态异常,因为初始时,lastRet指向-1位置
list2.add(3);//此步操作回导致集合对象的modCount发生改变
iterator1.hasNext();
//iterator1.next();//java.util.ConcurrentModificationException
//iterator1.remove();//java.util.ConcurrentModificationException
//iterator1.forEachRemaining(System.out: 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 :println);//java.util.ConcurrentModificationException
/**
-
从上面程序可以看出,只有hasNext()操作不会受到并发修改异常的影响
-
而next,remove,foreachRemaining都要求方法执行中,集合对象不能发生结构化修改
*/
}
}
ListIterator迭代器代码联系
import java.util.ArrayList;
import java.util.ListIterator;
public class ArrayListListIteratorTest {
public static void main(String[] args) {
/**
- ArrayList的ListIterator迭代器对象的基本用法
*/
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
// ArrayList除了可以生成Iterator迭代器对象外
// 还可以生成ListIterator迭代器对象。
// ArrayList自定义了ListIterator listIterator(int index); 内部是: return new ListItr(int index);
// 和ListIterator listIterator(); 内部是: return new ListItr(0);
// 这两个方法都能够获取ListIterator接口的实现类ListItr对象
// ListItr继承了Itr类,实现类ListIterable接口
// 由于ListItr继承了Itr,所以ListItr也有两个成员属性lastRet,cursor
// 当使用ListIterator listIterator(int index),其中index的值会被赋值给cursor
// 当使用ListIterator listIterator(),内部会默认将index设置为0,并赋值给cursor
ListIterator listIterator = list.listIterator(5);//lastRet=-1,cursor=5
// hasPrevious()方法 用于判断当前迭代器将要迭代的上一个元素
// 即判断 cursor是否等于0,如果cursor等于0了,则说明已经迭代到了集合的第一个元素,不能再往前迭代了
boolean hasPrevious = listIterator.hasPrevious();
// previous()方法 用于获取当前迭代器将要迭代的元素的上一个元素
// 由于cursor是将要迭代的元素,所以cursor-1是将要迭代的元素的上一个元素
// 内部实现:previous操作会将cursor-1指向的元素作为返回值,且将cursor=cursor-1,即lastRet,cursor都指向被返回的元素
listIterator.previous();
// set(E e)方法 即将当前正在迭代的元素的值改为e
// 内部实现:内部调用ArrayList对象的set(lastRet,e)方法,不影响lastRet,cursor指向
listIterator.set(55);
// add(E e)方法 即将e插入到cursor位置
// 内部实现:内部调用ArrayList对象的add(cursor,E e)方法,完成插入元素到指定索引处。
// 由于插入元素到cursor位置,导致cursor指向的元素后移了一位,所以cursor=cursor+1
// 而lastRet指向的元素要么是cursor-1,要么是curosr,要么是-1。所以插入元素都会导致lastRet指向的元素变更,所以将LastRet重置为-1
listIterator.add(45);
// nextIndex()方法用于获取cursor值,即将要迭代的元素索引
listIterator.nextIndex();
// previous()方法用于获取cursor-1值,即将要迭代的元素的上一个元素的索引
listIterator.previousIndex();
/**
- ArrayList的ListIterator迭代器对象迭代过程中可能发生ConcurrentModificationException的方法
*/
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
ListIterator listIterator1 = list1.listIterator(5);
list1.add(6);
listIterator1.hasPrevious();
//listIterator1.previous();//java.util.ConcurrentModificationException
//listIterator1.set(55);//java.util.ConcurrentModificationException
//listIterator1.add(12);//java.util.ConcurrentModificationException
listIterator1.nextIndex();
listIterator1.previousIndex();
/**
-
通过上面测试可以发现ListIterator迭代器对象的previous,set,add方法中都会检查modCount,即集合对象是否结构化修改
-
如果集合对象发生了结构化修改,就会抛出并发修改异常
-
另外,需要注意ListIterator迭代器的对象是ListItr实现类对象,ListItr继承了Itr类,
-
所以hasNext(),next(),remove(),foreachRemaining(Consumer c)也会被ListItr继承,同样的
-
ListIterator迭代器的next,remove,foreachRemaining也一样可能抛出并发修改异常
*/
/**
-
对比ListIterator和Iterator迭代器的联系与却别
-
ListIerator的的实现类ListItr继承了Iterator的实现类Itr
-
Iterator只能顺序迭代,且只能单向迭代,一旦迭代到最后一个元素,迭代器就不能再次利用
-
ListIterator即可顺序迭代,也可以逆序迭代,即可以双向迭代,一旦迭代到第一个或最后一个元素,还可以next()或previous()继续反向迭代
-
Iterator只有hasNext(),next(),remove(),foreachRemaining(Consumer consumer)方法
-
ListIterator的实现类继承了Iterator的实现类,所以继承了Iterator所有方法,且还有hasPrevious(),previous(),add(E e),set(E e)
-
Iterator适用于所有集合,ListIterator只适用于List集合
*/
}
}
思考题
===
ArrayList的底层数据结构是什么?
ArrayList底层数据结构是Object[]数组。
ArrayList类定义了一个成员属性:Object[] elementData
ArrayList的元素是否可以是null?
可以。
因为ArrayList底层数据结构是Object[]类型数组,该数组可以存入任何引用类型对象,null也是引用类型对象。
ArrayList的元素的类型为什么可以不一致?
因为ArrayList底层数据结构是Object[]数组,所有只要存入的元素是引用类型即可,不要求每个元素类型一致。
ArrayList有几个扩容方案?扩容原理是什么?
ArrayList的扩容原理就是 创建一个新数组,该新数组的长度是扩容后容量,将老数组的元素复制到新数组中。
ArrayList提供了三种构造器,不同的构造器的扩容方案不同:
ArrayList(),无参构造器,初始时是空的Object[]数组,第一次add时,创建一个长度为10的Object[]数组,并将add的元素存入size(size初始值是0)位置,后size=size+1。非第一次add,且容量不足时(小于当前容量+1),扩容Math.max(当前容量+1,当前容量*1.5)
ArrayList(int initialCapacity)或者ArrayList(Collection c)有参构造器,如果initialCapacity=0.或者c是空集合,那么初始时是空的Object[]数组,否则是对应InitiailCapacity大小的空Object[]数组或者是对应c.toArray()的Object[]数组。后面add操作时,若
容量不足(小于当前容量+1),则扩容Math.max(当前容量+1,当前容量*1.5)
ArrayList的add方法可以自动扩容,有没有人为扩容的方法?
ArrayList的add操作可以扩容,包括add(E e),add(Collection c),add(int index, E e),add(int index,Collection c),以及 ensureCapacity(int minCapacity)
ArrayList的remove方法删除元素后会导致缩容吗?
不会,remove方法不会导致集合缩容
ArrayList有没有方法可以缩容?其实现原理什么?
ArrayList提供了trimToSize()方法将集合容量缩小为size,即有多少个元素,就给多少容量。
Arrays.copyOf(elementData,size)
ArrayList是否支持对象克隆?为什么?ArrayLsit的对象克隆是深克隆还是浅克隆?为什么?
支持,因为ArrayList实现了Cloneable接口,并重写了clone方法
ArrayList的对象克隆是浅克隆。
因为ArrayList最重要的成员属性:elementData,克隆方式是:Arrays.copyOf(elementData,size); 该克隆方式将原数组elementData中的元素的地址值复制到新数组中。所以新数组中的元素指向的对象和原数组中相同,属于浅克隆。
ArrayList的哪些方法会导致集合对象结构化修改?结构化修改的含义是什么?
集合结构化修改是指导致集合modCount属性改变了的修改。
ArrayList的
add(E e),add(Collection c),add(int index,E e),add(int index,Collection c)
最后
以上就是成就鸡翅为你收集整理的List集合之ArrayList(三)ArrayList总结的全部内容,希望文章能够帮你解决List集合之ArrayList(三)ArrayList总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复