java集合之 ArrayList

青旅半醒 2022-05-12 15:12 330阅读 0赞

通过本文你将了解到 ArrayList 的如下信息

目录

ArrayList 简介

ArrayList源码分析

重要成员变量

底层数据结构:数组

默认初始容量:10

当前元素个数

重要方法

add(E e): 添加一个元素

grow(int minCapacity):扩容

batchRemove(Collection c, boolean complement):保留或者删除 c 集合中所包含的元素

  1. set(int index, E element):修改指定位置的元素
  2. get(int index) :查找指定位置的元素

遍历

总结


ArrayList 简介

ArrayList 是一个可存储包括 null 值的任意类型数据、支持动态扩容、有序(输入顺序与输出顺序一致)、查找效率高(时间复杂度O(1))的一个集合。

ArrayList源码分析

重要成员变量

底层数据结构:数组

  1. //存储元素
  2. transient Object[] elementData; // non-private to simplify nested class access

transient表示不允许被序列化

默认初始容量:10

  1. private static final int DEFAULT_CAPACITY = 10;

当前元素个数

  1. private int size;

重要方法

add(E e): 添加一个元素

  1. public boolean add(E e) {
  2. //确保有足够的容量
  3. ensureCapacityInternal(size + 1); // Increments modCount!!
  4. //在 size 下标处添加一个元素
  5. elementData[size++] = e;
  6. return true;
  7. }
  8. private void ensureCapacityInternal(int minCapacity) {
  9. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  10. //当前所需要的最小容量大于等于 10
  11. minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  12. }
  13. ensureExplicitCapacity(minCapacity);
  14. }
  15. private void ensureExplicitCapacity(int minCapacity) {
  16. modCount++;
  17. // 当前存储空间(数组容量)不够了
  18. if (minCapacity - elementData.length > 0)
  19. grow(minCapacity);
  20. }

简单描述:空间是否足够存储—->如果不够就进行扩容—->存储数据到 size 处

grow(int minCapacity):扩容

在没有达到边界值的情况下,扩容后的数组容量 = 旧数组容量+旧数组容量/2

  1. private void grow(int minCapacity) {
  2. // overflow-conscious code
  3. int oldCapacity = elementData.length;
  4. int newCapacity = oldCapacity + (oldCapacity >> 1);
  5. if (newCapacity - minCapacity < 0)
  6. newCapacity = minCapacity;
  7. if (newCapacity - MAX_ARRAY_SIZE > 0)
  8. newCapacity = hugeCapacity(minCapacity);
  9. // minCapacity is usually close to size, so this is a win:
  10. elementData = Arrays.copyOf(elementData, newCapacity);
  11. }

batchRemove(Collection<?> c, boolean complement):保留或者删除 c 集合中所包含的元素

根据 complement 来决定是保留还是移除指定集合中存在的元素

  1. private boolean batchRemove(Collection<?> c, boolean complement) {
  2. final Object[] elementData = this.elementData;
  3. int r = 0, w = 0;
  4. boolean modified = false;
  5. try {
  6. for (; r < size; r++)
  7. //complement为true:保留 elementData 和 c 中都存在的元素(相当于求交集)
  8. //complement为false:保留只存在于 elementaData 中,但是不存在于c中的元素
  9. if (c.contains(elementData[r]) == complement)
  10. elementData[w++] = elementData[r];
  11. } finally {
  12. // Preserve behavioral compatibility with AbstractCollection,
  13. // even if c.contains() throws.
  14. if (r != size) {
  15. System.arraycopy(elementData, r,
  16. elementData, w,
  17. size - r);
  18. w += size - r;
  19. }
  20. if (w != size) {
  21. // clear to let GC do its work
  22. for (int i = w; i < size; i++)
  23. elementData[i] = null;
  24. modCount += size - w;
  25. size = w;
  26. modified = true;
  27. }
  28. }
  29. return modified;
  30. }

set(int index, E element):修改指定位置的元素

  1. public E set(int index, E element) {
  2. rangeCheck(index);
  3. E oldValue = elementData(index);
  4. elementData[index] = element;
  5. return oldValue;
  6. }

get(int index) :查找指定位置的元素

  1. public E get(int index) {
  2. rangeCheck(index);
  3. return elementData(index);
  4. }

遍历

ArrayList 内部实现了两种迭代器(Itr、ListItr)来遍历元素。本质都是根据数组下标来操作的

  1. private class Itr implements Iterator<E> {
  2. int cursor; // index of next element to return
  3. int lastRet = -1; // index of last element returned; -1 if no such
  4. int expectedModCount = modCount;
  5. public boolean hasNext() {
  6. return cursor != size;
  7. }
  8. @SuppressWarnings("unchecked")
  9. public E next() {
  10. checkForComodification();
  11. int i = cursor;
  12. if (i >= size)
  13. throw new NoSuchElementException();
  14. Object[] elementData = ArrayList.this.elementData;
  15. if (i >= elementData.length)
  16. throw new ConcurrentModificationException();
  17. cursor = i + 1;
  18. return (E) elementData[lastRet = i];
  19. }
  20. public void remove() {
  21. if (lastRet < 0)
  22. throw new IllegalStateException();
  23. checkForComodification();
  24. try {
  25. ArrayList.this.remove(lastRet);
  26. cursor = lastRet;
  27. lastRet = -1;
  28. expectedModCount = modCount;
  29. } catch (IndexOutOfBoundsException ex) {
  30. throw new ConcurrentModificationException();
  31. }
  32. }
  33. @Override
  34. @SuppressWarnings("unchecked")
  35. public void forEachRemaining(Consumer<? super E> consumer) {
  36. Objects.requireNonNull(consumer);
  37. final int size = ArrayList.this.size;
  38. int i = cursor;
  39. if (i >= size) {
  40. return;
  41. }
  42. final Object[] elementData = ArrayList.this.elementData;
  43. if (i >= elementData.length) {
  44. throw new ConcurrentModificationException();
  45. }
  46. while (i != size && modCount == expectedModCount) {
  47. consumer.accept((E) elementData[i++]);
  48. }
  49. // update once at end of iteration to reduce heap write traffic
  50. cursor = i;
  51. lastRet = i - 1;
  52. checkForComodification();
  53. }
  54. final void checkForComodification() {
  55. if (modCount != expectedModCount)
  56. throw new ConcurrentModificationException();
  57. }
  58. }
  59. /**
  60. * An optimized version of AbstractList.ListItr
  61. */
  62. private class ListItr extends Itr implements ListIterator<E> {
  63. ListItr(int index) {
  64. super();
  65. cursor = index;
  66. }
  67. public boolean hasPrevious() {
  68. return cursor != 0;
  69. }
  70. public int nextIndex() {
  71. return cursor;
  72. }
  73. public int previousIndex() {
  74. return cursor - 1;
  75. }
  76. @SuppressWarnings("unchecked")
  77. public E previous() {
  78. checkForComodification();
  79. int i = cursor - 1;
  80. if (i < 0)
  81. throw new NoSuchElementException();
  82. Object[] elementData = ArrayList.this.elementData;
  83. if (i >= elementData.length)
  84. throw new ConcurrentModificationException();
  85. cursor = i;
  86. return (E) elementData[lastRet = i];
  87. }
  88. public void set(E e) {
  89. if (lastRet < 0)
  90. throw new IllegalStateException();
  91. checkForComodification();
  92. try {
  93. ArrayList.this.set(lastRet, e);
  94. } catch (IndexOutOfBoundsException ex) {
  95. throw new ConcurrentModificationException();
  96. }
  97. }
  98. public void add(E e) {
  99. checkForComodification();
  100. try {
  101. int i = cursor;
  102. ArrayList.this.add(i, e);
  103. cursor = i + 1;
  104. lastRet = -1;
  105. expectedModCount = modCount;
  106. } catch (IndexOutOfBoundsException ex) {
  107. throw new ConcurrentModificationException();
  108. }
  109. }
  110. }

总结

上面通过增删改查四个具有代表性的方法分析了ArrayList 源码,ArrayList中的其它方法其实也是类似的,所以没有罗列出来。想要表达的中心思想是:ArrayList 底层数据结构是采用的是数组,所以对于查询会很快(直接根据数组下标),删除会比较满(会涉及到元素的移动)。对于数组的遍历,建议直接使用 for 循环,根据数组下标直接获取。

发表评论

表情:
评论列表 (有 0 条评论,330人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java集合ArrayList

    ArrayList List接口的一个实现类 内部封装了一个长度可变的数组对象 当存入的元素,超过数组长度时,会在内存中,分配一个更大的数组 来存储这些元素,可

    相关 Java ArrayList集合

    1、介绍 - 集合和数组的区别:数组长度不能改变,但是集合长度可以随意变化 - 集合泛型只能是引用类型,不能是基本类型 - 对于集合来说,直接打印得到的不是地址值,而是...