当前位置:   article > 正文

List集合之ArrayList(三)ArrayList总结_arraylist的consumer

arraylist的consumer

思维导图

代码练习

ArrayList常用方法练习

  1. import java.util.*;
  2. public class ArrayListTest {
  3. public static void main(String[] args) {
  4. ArrayList al = new ArrayList();
  5. // ArrayList集合添加的元素类型可以不一致,但是必须是引用类型
  6. // ArrayList集合的容量是可变的
  7. al.add(new String("ABC"));
  8. al.add(1);
  9. al.add(1);
  10. al.add(true);
  11. al.add(true);
  12. // ArrayList集合元素有序,可重复
  13. System.out.println(al);//[ABC, 1, 1, true, true]
  14. // ArrayList集合元素有索引
  15. System.out.println(al.get(0));//ABC
  16. System.out.println("====================================");
  17. // size()方法用于获取集合元素个数,即ArrayList对象的属性size值。注意size不是ArrayList的容量
  18. System.out.println(al.size());
  19. // isEmpty()方法用于判断集合是否有元素,即ArrayList对象的size是否为0,若为0返回true,反之返回false
  20. System.out.println(al.isEmpty());
  21. System.out.println("====================================");
  22. // ArrayList没有提供方法去获取集合的容量,但是有方法改变集合容量大小
  23. // 缩容 trimToSize(),即将集合容量变为最小容量,即size大小。
  24. //注意该操作会变更modCount值,即在迭代器迭代过程中,不能使用该方法
  25. al.trimToSize();
  26. // 扩容 ensureCapacity(int minCapacity),即将容量扩容到minCapacity大小,前提minCapacity比当前集合容量大
  27. //注意该操作会变更modCount值,即在迭代器迭代过程中,不能使用该方法
  28. al.ensureCapacity(10);
  29. // foreach(Consumer consumer)可以遍历处理集合元素
  30. // 注意foreach过程中,不能有改变集合modCount值的动作
  31. al.forEach(data-> System.out.println(data));
  32. System.out.println("====================================");
  33. // ArrayList实现了Cloneable接口,重写了clone方法,所以具备对象克隆功能
  34. ArrayList clone = (ArrayList) al.clone();
  35. System.out.println(clone);//[ABC, 1, 1, true, true]
  36. // Java中对象克隆分为深克隆和浅克隆,
  37. // 浅克隆是指克隆对象的引用类型变量的值(引用)和原对象一致。
  38. // 深克隆是指克隆对象的引用类型变量的值(引用)和原对象不一致,但是变量指向的对象的内容一致
  39. // ArrayList重写的clone方法是浅克隆,即克隆对象和原对象的elementData成员变量指向的数组对象是同一个对象
  40. System.out.println(clone.get(0) == al.get(0));
  41. System.out.println("====================================");
  42. // ArrayList支持将集合转成数组
  43. // 方式1:toArray()方法,直接将集合转成Object[]数组,
  44. // 注意这里不是直接将ArrayList对象的elementData属性返回,而是返回Arrays.copyOf(elementData,size)
  45. Object[] array = al.toArray();
  46. // 方式2:toArray(T[] a),外部传入一个数组类型对象,一般来说传入一个空的数组即可,
  47. al.toArray(new Object[0]);
  48. // 如果传入一个数组的长度大于集合元素个数,且数组有元素值,则可能会出现不好的效果
  49. Object[] objects = new Object[10];
  50. objects[9]=9;
  51. al.toArray(objects);
  52. System.out.println(Arrays.toString(objects));
  53. System.out.println("====================================");
  54. // ArrayList获取元素索引
  55. // indexOf(E e),获取某值在集合顺序equals比较元素第一次true的索引
  56. System.out.println(al.indexOf("ABC"));
  57. // indexOf(E e),当某值在集合元素中不存在时返回-1
  58. System.out.println(al.indexOf(null));
  59. System.out.println(al.indexOf(1));
  60. // lastIndexOf(E e),获取某值在集合逆序equals比较元素第一次true的索引
  61. System.out.println(al.lastIndexOf(1));
  62. System.out.println("====================================");
  63. // contains(E e)即ArrayList集合是否包含有equals某值的元素
  64. // 内部实现时return indexOf(o) >= 0;
  65. al.contains(1);
  66. // retainAll(Collection c),只保留ArrayList集合中 和c集合有交集的元素。
  67. // 注意该操作会改变modCount值,所以在迭代器迭代过程中不能使用该方法操作ArrayList集合
  68. HashSet hs = new HashSet();
  69. hs.add(1);
  70. hs.add("ABC");
  71. hs.add(222);
  72. al.retainAll(hs);
  73. System.out.println(al);
  74. System.out.println("====================================");
  75. // sort(Comparator con) 排序集合元素
  76. // 注意此操作过程中不能有改变集合modCount的其他操作同时进行
  77. // 注意此操作完成后,会改变modCount值
  78. al.sort((o1,o2)->o1.toString().length()-o2.toString().length());
  79. System.out.println(al);
  80. System.out.println("====================================");
  81. // replaceAll(UnaryOperator uo),对集合元素做批量操作
  82. // 注意此操作过程中不能有改变集合modCount的其他操作同时进行
  83. // 注意此操作完成后,会改变modCount值
  84. al.replaceAll(obj->obj+"123");
  85. System.out.println(al);
  86. System.out.println("====================================");
  87. // 插入元素
  88. // 分为两大类四小种
  89. // 1.在集合元素尾部插入
  90. // 1.1 尾部插入一个元素
  91. al.add(12);//[1123, 1123, ABC123,12]
  92. // 1.2 尾部插入多个元素
  93. al.addAll(hs);//[1123, 1123, ABC123,12,1,ABC,222]
  94. // 2.在集合指定索引处插入
  95. // 2.1 指定索引处插入一个元素
  96. al.add(2,22);//[1123, 1123, 22, ABC123,12,1,ABC,222]
  97. // 2.2 指定索引处插入多个元素
  98. al.addAll(2,hs);//[1123, 1123,1, ABC, 222, 22, ABC123,12,1,ABC,222]
  99. // 注意add操作会改变modCount值
  100. System.out.println(al);
  101. System.out.println("====================================");
  102. // remove(E e),删除集合中第一个equals给定值的元素
  103. al.remove("ABC");//[1123, 1123, 1, 222, 22, ABC123, 12, 1, ABC, 222]
  104. // remove(int index) 删除指定索引处的元素
  105. al.remove(1);//[1123, 1, 222, 22, ABC123, 12, 1, ABC, 222]
  106. // removeAll(Collection c) 只保留ArrayList集合中 和c集合非交集的元素
  107. al.removeAll(hs);//[1123, 22, ABC123, 12]
  108. // removeIf(Predicate p) 删除掉符合predicate test要求的元素
  109. al.removeIf(obj->obj.toString().length()>2);//[22, 12]
  110. // 注意以上的remove操作都会改变modCount值
  111. System.out.println(al);
  112. System.out.println("====================================");
  113. // set(int index,E e) 修改集合某索引位置上的元素的值
  114. al.set(1,111);
  115. // get(int index) 获取集合上某索引位置的元素
  116. System.out.println(al.get(1));
  117. }
  118. }

Iterator迭代器代码练习

  1. import java.util.ArrayList;
  2. import java.util.Iterator;
  3. public class ArrayListIteratorTest {
  4. public static void main(String[] args) {
  5. /**
  6. * ArrayList集合对象的Iterator迭代器对象的基本用法
  7. */
  8. ArrayList list = new ArrayList();
  9. list.add(1);
  10. list.add(2);
  11. list.add(3);
  12. list.add(4);
  13. list.add(5);
  14. // Collection接口继承了Iterable接口,所以它的实现类需要重写iterable方法
  15. // ArrayList中对于iterator()方法的重写是: return new Itr();
  16. // iterator()用于获取集合对象的迭代器对象
  17. // 可以看出迭代器对象必须依赖于集合对象
  18. Iterator iterator = list.iterator();// lastRet=-1,cursor=0
  19. // Itr类实现了Iterator接口,所以需要重写Iterator接口中定义的方法
  20. // hasNext():用于判断迭代器将要迭代的下一个元素是否存在
  21. // Itr类有两个成员变量:lastRet,cursor。
  22. // 其中lastRet是迭代器正在迭代的元素的索引(若没有正在迭代的元素,lastRet=-1),cursor是迭代器将要迭代的下一个元素的索引
  23. // 则hasNext()内部实现就是判断cursor的值是否为size,若cursor=size,则说明lastRet已经迭代到size-1,即集合最后一个元素,无法继续迭代
  24. boolean hasNext = iterator.hasNext();//lastRet=-1,cursor=0
  25. // next():用于获取迭代器将要迭代的下一个元素
  26. // cursor指向的元素就是迭代器将要返回的下一个元素。而由于next()动作会导致lastRet,cursor后移一位。
  27. // 所以用一个中间量 int i = cursor 保存next动作前的cursor值
  28. // next动作中:cursor = cursor + 1;即将cursor后移一位
  29. // lastRet = i;即将lastRet指向后移前的cursor值
  30. // return elementData[lastRet];即获取lastRet指向的元素,并返回
  31. iterator.next();//lastRet=0,cursor=1
  32. // remove():用于删除迭代器正在迭代的元素
  33. // 即删除lastRet指向的元素(前提是lastRet!=-1,即删除一个元素,要保证被删除的元素存在)
  34. // 内部实现:是调用迭代器对象对应的集合对象的remove(lastRet)方法
  35. // 由于ArrayList对象删除集合元素后,底层数组元素会发生移位,来填补被删除的元素位置。
  36. // 所以cursor指向lastRet的位置
  37. // lastRet = -1; 因为lastRet指向的元素已经被删除了,所以lastRet没有指向的元素了。
  38. iterator.remove();//lastRet=-1,cursor=0
  39. // foreachRemaining(Consumer consumer): 遍历处理剩下的元素
  40. // 从该方法的名字可以看出foreach指遍历,Remaining指剩下的,consumer指处理遍历的每个元素
  41. // 即遍历处理剩下的元素
  42. // 注意:该方法遍历元素的起始位置是iterator对象的cursor,
  43. // 即如果iterator对象之前没有做过任何next(),remove()操作的话,foreachRemaining是从cursor=0开始遍历
  44. // 如果iterator对象已经操作过next(),remove()的话,则foreachRemaining从迭代器实际cursor值开始遍历
  45. // 另外需要注意的foreachRemaining方法对cursor和lastRet的处理 和next()是不同的
  46. // next()是调用一次,对cursor加1,lastRet指向最新的cursor-1的位置
  47. // foreahRemaining是调用一次,直接将cursor值设置为size,lastRet=size-1
  48. iterator.forEachRemaining(System.out::println);
  49. System.out.println("====================================");
  50. /**
  51. * ArrayList集合对象的Iterator迭代器迭代过程中出现ConcurrentModificationException引发原因
  52. *
  53. * 并发修改异常是指在迭代器迭代过程中,集合对象发生了结构化修改。
  54. * 简单点理解就是:迭代器迭代过程中,对应集合对象的modCount成员属性值被改变了。
  55. * modCount是定义在AbstractList抽象类中的,专门给子类如ArrayList继承的成员属性。
  56. * ArrayList的
  57. * add,remove,clear,retainAll(改变了集合的size属性)
  58. * || ensureCapacity,trimToSize(改变了集合的容量)
  59. * || sort,replaceAll(改变了集合的所有元素)
  60. * 操作都会改变modCount的值,这些操作统一称为ArrayList集合的结构化修改操作
  61. */
  62. ArrayList list2 = new ArrayList();
  63. list2.add(1);
  64. list2.add(2);
  65. list2.add(3);
  66. list2.add(4);
  67. list2.add(5);
  68. /**
  69. * 那么迭代器的哪些方法可能抛出并发修改异常呢?即哪些迭代器动作算是迭代动作呢?
  70. */
  71. // 注意当iterator1对象初始化时,已经保存了集合对象的modCount值到自己属性expectedModCount中
  72. // iterator1迭代时,会实时比较modCount是否和expectedModCount相等
  73. // 若相等,则说明集合对象没有发生结构化修改
  74. // 若不相等,则说明集合对象发生了结构化修改
  75. Iterator iterator1 = list2.iterator();
  76. iterator1.next();//先next一下,保证remove()不会抛出非法状态异常,因为初始时,lastRet指向-1位置
  77. list2.add(3);//此步操作回导致集合对象的modCount发生改变
  78. iterator1.hasNext();
  79. //iterator1.next();//java.util.ConcurrentModificationException
  80. //iterator1.remove();//java.util.ConcurrentModificationException
  81. //iterator1.forEachRemaining(System.out::println);//java.util.ConcurrentModificationException
  82. /**
  83. * 从上面程序可以看出,只有hasNext()操作不会受到并发修改异常的影响
  84. * 而next,remove,foreachRemaining都要求方法执行中,集合对象不能发生结构化修改
  85. */
  86. }
  87. }

ListIterator迭代器代码联系

  1. import java.util.ArrayList;
  2. import java.util.ListIterator;
  3. public class ArrayListListIteratorTest {
  4. public static void main(String[] args) {
  5. /**
  6. * ArrayList的ListIterator迭代器对象的基本用法
  7. */
  8. ArrayList list = new ArrayList();
  9. list.add(1);
  10. list.add(2);
  11. list.add(3);
  12. list.add(4);
  13. list.add(5);
  14. // ArrayList除了可以生成Iterator迭代器对象外
  15. // 还可以生成ListIterator迭代器对象。
  16. // ArrayList自定义了ListIterator listIterator(int index); 内部是: return new ListItr(int index);
  17. // 和ListIterator listIterator(); 内部是: return new ListItr(0);
  18. // 这两个方法都能够获取ListIterator接口的实现类ListItr对象
  19. // ListItr继承了Itr类,实现类ListIterable接口
  20. // 由于ListItr继承了Itr,所以ListItr也有两个成员属性lastRet,cursor
  21. // 当使用ListIterator listIterator(int index),其中index的值会被赋值给cursor
  22. // 当使用ListIterator listIterator(),内部会默认将index设置为0,并赋值给cursor
  23. ListIterator listIterator = list.listIterator(5);//lastRet=-1,cursor=5
  24. // hasPrevious()方法 用于判断当前迭代器将要迭代的上一个元素
  25. // 即判断 cursor是否等于0,如果cursor等于0了,则说明已经迭代到了集合的第一个元素,不能再往前迭代了
  26. boolean hasPrevious = listIterator.hasPrevious();
  27. // previous()方法 用于获取当前迭代器将要迭代的元素的上一个元素
  28. // 由于cursor是将要迭代的元素,所以cursor-1是将要迭代的元素的上一个元素
  29. // 内部实现:previous操作会将cursor-1指向的元素作为返回值,且将cursor=cursor-1,即lastRet,cursor都指向被返回的元素
  30. listIterator.previous();
  31. // set(E e)方法 即将当前正在迭代的元素的值改为e
  32. // 内部实现:内部调用ArrayList对象的set(lastRet,e)方法,不影响lastRet,cursor指向
  33. listIterator.set(55);
  34. // add(E e)方法 即将e插入到cursor位置
  35. // 内部实现:内部调用ArrayList对象的add(cursor,E e)方法,完成插入元素到指定索引处。
  36. // 由于插入元素到cursor位置,导致cursor指向的元素后移了一位,所以cursor=cursor+1
  37. // 而lastRet指向的元素要么是cursor-1,要么是curosr,要么是-1。所以插入元素都会导致lastRet指向的元素变更,所以将LastRet重置为-1
  38. listIterator.add(45);
  39. // nextIndex()方法用于获取cursor值,即将要迭代的元素索引
  40. listIterator.nextIndex();
  41. // previous()方法用于获取cursor-1值,即将要迭代的元素的上一个元素的索引
  42. listIterator.previousIndex();
  43. /**
  44. * ArrayList的ListIterator迭代器对象迭代过程中可能发生ConcurrentModificationException的方法
  45. */
  46. ArrayList list1 = new ArrayList();
  47. list1.add(1);
  48. list1.add(2);
  49. list1.add(3);
  50. list1.add(4);
  51. list1.add(5);
  52. ListIterator listIterator1 = list1.listIterator(5);
  53. list1.add(6);
  54. listIterator1.hasPrevious();
  55. //listIterator1.previous();//java.util.ConcurrentModificationException
  56. //listIterator1.set(55);//java.util.ConcurrentModificationException
  57. //listIterator1.add(12);//java.util.ConcurrentModificationException
  58. listIterator1.nextIndex();
  59. listIterator1.previousIndex();
  60. /**
  61. * 通过上面测试可以发现ListIterator迭代器对象的previous,set,add方法中都会检查modCount,即集合对象是否结构化修改
  62. * 如果集合对象发生了结构化修改,就会抛出并发修改异常
  63. * 另外,需要注意ListIterator迭代器的对象是ListItr实现类对象,ListItr继承了Itr类,
  64. * 所以hasNext(),next(),remove(),foreachRemaining(Consumer c)也会被ListItr继承,同样的
  65. * ListIterator迭代器的next,remove,foreachRemaining也一样可能抛出并发修改异常
  66. */
  67. /**
  68. * 对比ListIterator和Iterator迭代器的联系与却别
  69. *
  70. * ListIerator的的实现类ListItr继承了Iterator的实现类Itr
  71. *
  72. * Iterator只能顺序迭代,且只能单向迭代,一旦迭代到最后一个元素,迭代器就不能再次利用
  73. * ListIterator即可顺序迭代,也可以逆序迭代,即可以双向迭代,一旦迭代到第一个或最后一个元素,还可以next()或previous()继续反向迭代
  74. *
  75. * Iterator只有hasNext(),next(),remove(),foreachRemaining(Consumer consumer)方法
  76. * ListIterator的实现类继承了Iterator的实现类,所以继承了Iterator所有方法,且还有hasPrevious(),previous(),add(E e),set(E e)
  77. *
  78. * Iterator适用于所有集合,ListIterator只适用于List集合
  79. */
  80. }
  81. }

思考题

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)

remove(E e),remove(int index),remove(Collection c),clear(),

retainAll(Collection c)

trimToSize(),ensureCapacity(int minCapacity)

sort(Comaprator c),replaceAll(UnaryOperator u)

都会导致集合成员属性modCount值改变,都是与结构化修改。

ArrayList的迭代器有哪些?这些迭代器各有什么特点,这些迭代器之间是否有联系?

ArrayList的迭代器有 Iterator迭代器 和 ListIterator迭代器

Iterator迭代器只能是单向,顺序迭代。ListIterator迭代器可以双向迭代。

ListIterator的实现类ListItr继承了Iterator的实现类Itr

ListIterator迭代器对象不仅继承了Itr的 hasNext(),next(),remove(),foreachRemaining(Consumer c)

还有hasPrevious(),previous(),add(E e),set(E e),nextIndex(),previousIndex()

Arrays.asList(T... a)方法返回值类型也是ArrayList?这个ArrayList和java.util.ArrayList关系是什么?

Arrays.asList(T... a)返回值类型ArrayList,是Arrays类的静态内部类。即该类全称是Arrays.ArrayList

Arrays.ArrayList的特点是 固定长度的集合,不支持元素插入和删除

和java.util.ArrayList没有关系。唯一的联系是:都是AbstractList的子类

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/752603
推荐阅读
相关标签
  

闽ICP备14008679号