ArrayList详解
一. 关于 ArrayList 的常见内容
描述: 实现 List<E> 接口; 元素可排序,可重复,可为 null ;不是线程安全的.
继承以及实现关系:
1 public class ArrayList<E> extends AbstractList<E> 2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ 3 }
List<E> 接口定义了列表的方法和默认实现, AbstractList<E> 实现了 List<E> 接口,对其中的部分方法添加相关实现; RandomAccess 接口表示可以随机访问, Cloneable 接口表示可以克隆, Serializable 表示可以序列化,用于数据传输
二. ArrayList 的实现原理
ArrayList 是通过动态数组实现的,对 ArrayList 的操作实质是对其维护的动态数组进行操作.所以对元素的访问就是对数组的访问(查询为随机访问),元素的添加删除实质就是数组的复制或者追加和末尾移除.
三. ArrayList 中定义的常量、属性和子集
常量:
/** * 默认容量 */ private static final int DEFAULT_CAPACITY = 10; /** * 空数组 */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * 默认容量空数组 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * 最大数组大小 */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
常量描述:
默认容量:无容量参数构造时,创建的数组默认容量
空数组:有容量参数构造时使用的共享的空数组
默认容量空数组:无容量构造,使用默认容量时使用的共享的空数组.源码注释中写到:与上面的空数组进行区分是为了了解首次添加元素时,要扩展多大容量
最大数组大小:用于分配数组容量时的最大值,源码注释中写到:一些虚拟机会在数组中添加头数据,为了避免分配新数组的内存时发生内存溢出(注意:此处的内存溢出不一定是真的内存不够,而是需要的容量值大于 int 型最大值导致的),所以采用 int 型最大值减 8
属性:
/** * ArrayList中维护的动态数组 */ transient Object[] elementData; /** * 集合大小 */ private int size;
属性描述:
动态数组:作为集合实现,用于存储集合元素
集合大小:表示集合的实际大小,集合的容量是当前数组的长度
子集:
1 private class SubList extends AbstractList<E> implements RandomAccess { 2 private final AbstractList<E> parent; 3 private final int parentOffset; 4 private final int offset; 5 int size; 6 7 SubList(AbstractList<E> parent, 8 int offset, int fromIndex, int toIndex) { 9 this.parent = parent; 10 this.parentOffset = fromIndex; 11 this.offset = offset + fromIndex; 12 this.size = toIndex - fromIndex; 13 this.modCount = ArrayList.this.modCount; 14 } 15 16 public E set(int index, E e) { 17 rangeCheck(index); 18 checkForComodification(); 19 E oldValue = ArrayList.this.elementData(offset + index); 20 ArrayList.this.elementData[offset + index] = e; 21 return oldValue; 22 } 23 24 public E get(int index) { 25 rangeCheck(index); 26 checkForComodification(); 27 return ArrayList.this.elementData(offset + index); 28 } 29 30 public int size() { 31 checkForComodification(); 32 return this.size; 33 } 34 35 public void add(int index, E e) { 36 rangeCheckForAdd(index); 37 checkForComodification(); 38 // 调用原集合的 add 方法,在原集合上添加元素,add 时,原集合的 size 已经加 1 39 parent.add(parentOffset + index, e); 40 // 重新拉取修改次数,处理子集的 size 41 this.modCount = parent.modCount; 42 this.size++; 43 } 44 45 public E remove(int index) { 46 rangeCheck(index); 47 checkForComodification(); 48 E result = parent.remove(parentOffset + index); 49 this.modCount = parent.modCount; 50 this.size--; 51 return result; 52 } 53 54 protected void removeRange(int fromIndex, int toIndex) { 55 checkForComodification(); 56 parent.removeRange(parentOffset + fromIndex, 57 parentOffset + toIndex); 58 this.modCount = parent.modCount; 59 this.size -= toIndex - fromIndex; 60 } 61 62 public boolean addAll(Collection<? extends E> c) { 63 return addAll(this.size, c); 64 } 65 66 public boolean addAll(int index, Collection<? extends E> c) { 67 rangeCheckForAdd(index); 68 int cSize = c.size(); 69 if (cSize==0) 70 return false; 71 72 checkForComodification(); 73 parent.addAll(parentOffset + index, c); 74 this.modCount = parent.modCount; 75 this.size += cSize; 76 return true; 77 } 78 79 public Iterator<E> iterator() { 80 return listIterator(); 81 } 82 83 public ListIterator<E> listIterator(final int index) { 84 checkForComodification(); 85 rangeCheckForAdd(index); 86 final int offset = this.offset; 87 88 return new ListIterator<E>() { 89 int cursor = index; 90 int lastRet = -1; 91 int expectedModCount = ArrayList.this.modCount; 92 93 public boolean hasNext() { 94 return cursor != SubList.this.size; 95 } 96 97 @SuppressWarnings("unchecked") 98 public E next() { 99 checkForComodification(); 100 int i = cursor; 101 if (i >= SubList.this.size) 102 throw new NoSuchElementException(); 103 Object[] elementData = ArrayList.this.elementData; 104 if (offset + i >= elementData.length) 105 throw new ConcurrentModificationException(); 106 cursor = i + 1; 107 return (E) elementData[offset + (lastRet = i)]; 108 } 109 110 public boolean hasPrevious() { 111 return cursor != 0; 112 } 113 114 @SuppressWarnings("unchecked") 115 public E previous() { 116 checkForComodification(); 117 int i = cursor - 1; 118 if (i < 0) 119 throw new NoSuchElementException(); 120 Object[] elementData = ArrayList.this.elementData; 121 if (offset + i >= elementData.length) 122 throw new ConcurrentModificationException(); 123 cursor = i; 124 return (E) elementData[offset + (lastRet = i)]; 125 } 126 127 @SuppressWarnings("unchecked") 128 public void forEachRemaining(Consumer<? super E> consumer) { 129 Objects.requireNonNull(consumer); 130 final int size = SubList.this.size; 131 int i = cursor; 132 if (i >= size) { 133 return; 134 } 135 final Object[] elementData = ArrayList.this.elementData; 136 if (offset + i >= elementData.length) { 137 throw new ConcurrentModificationException(); 138 } 139 while (i != size && modCount == expectedModCount) { 140 consumer.accept((E) elementData[offset + (i++)]); 141 } 142 // update once at end of iteration to reduce heap write traffic 143 lastRet = cursor = i; 144 checkForComodification(); 145 } 146 147 public int nextIndex() { 148 return cursor; 149 } 150 151 public int previousIndex() { 152 return cursor - 1; 153 } 154 155 public void remove() { 156 if (lastRet < 0) 157 throw new IllegalStateException(); 158 checkForComodification(); 159 160 try { 161 SubList.this.remove(lastRet); 162 cursor = lastRet; 163 lastRet = -1; 164 expectedModCount = ArrayList.this.modCount; 165 } catch (IndexOutOfBoundsException ex) { 166 throw new ConcurrentModificationException(); 167 } 168 } 169 170 public void set(E e) { 171 if (lastRet < 0) 172 throw new IllegalStateException(); 173 checkForComodification(); 174 175 try { 176 ArrayList.this.set(offset + lastRet, e); 177 } catch (IndexOutOfBoundsException ex) { 178 throw new ConcurrentModificationException(); 179 } 180 } 181 182 public void add(E e) { 183 checkForComodification(); 184 185 try { 186 int i = cursor; 187 SubList.this.add(i, e); 188 cursor = i + 1; 189 lastRet = -1; 190 expectedModCount = ArrayList.this.modCount; 191 } catch (IndexOutOfBoundsException ex) { 192 throw new ConcurrentModificationException(); 193 } 194 } 195 196 final void checkForComodification() { 197 if (expectedModCount != ArrayList.this.modCount) 198 throw new ConcurrentModificationException(); 199 } 200 }; 201 } 202 203 public List<E> subList(int fromIndex, int toIndex) { 204 subListRangeCheck(fromIndex, toIndex, size); 205 return new SubList(this, offset, fromIndex, toIndex); 206 } 207 208 private void rangeCheck(int index) { 209 if (index < 0 || index >= this.size) 210 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 211 } 212 213 private void rangeCheckForAdd(int index) { 214 if (index < 0 || index > this.size) 215 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 216 } 217 218 private String outOfBoundsMsg(int index) { 219 return "Index: "+index+", Size: "+this.size; 220 } 221 222 private void checkForComodification() { 223 if (ArrayList.this.modCount != this.modCount) 224 throw new ConcurrentModificationException(); 225 } 226 227 public Spliterator<E> spliterator() { 228 checkForComodification(); 229 return new ArrayListSpliterator<E>(ArrayList.this, offset, 230 offset + this.size, this.modCount); 231 } 232 }
子集描述:
自己的本质是截取原集合相应的部分展示,对自己的操作实际就是对原集合的操作
四. 一些重要的方法:
1.构造方法
1 /** 2 * 初始化容量构造 3 */ 4 public ArrayList(int initialCapacity) { 5 if (initialCapacity > 0) { 6 this.elementData = new Object[initialCapacity]; 7 } else if (initialCapacity == 0) { 8 this.elementData = EMPTY_ELEMENTDATA; 9 } else { 10 throw new IllegalArgumentException("Illegal Capacity: "+ 11 initialCapacity); 12 } 13 } 14 15 /** 16 * 无参构造 17 */ 18 public ArrayList() { 19 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 20 } 21 22 /** 23 * 集合构造 24 */ 25 public ArrayList(Collection<? extends E> c) { 26 Object[] a = c.toArray(); 27 if ((size = a.length) != 0) { 28 if (c.getClass() == ArrayList.class) { 29 elementData = a; 30 } else { 31 elementData = Arrays.copyOf(a, size, Object[].class); 32 } 33 } else { 34 // replace with empty array. 35 elementData = EMPTY_ELEMENTDATA; 36 } 37 }
描述:在新建列表时,使用无参构造或者容量为 0 的构造,得到的列表实际上是一个空列表(空数组),只有在首次添加元素时才会扩展至默认容量或者指定容量的列表(新建指定长度的数组)
2.grow
1 private void grow(int minCapacity) { 2 // overflow-conscious code 3 int oldCapacity = elementData.length; 4 // 扩容至原容量的1.5倍 5 int newCapacity = oldCapacity + (oldCapacity >> 1); 6 // 如果新容量仍然比指定容量小,则新容量就是指定的最小容量 7 if (newCapacity - minCapacity < 0) 8 newCapacity = minCapacity; 9 if (newCapacity - MAX_ARRAY_SIZE > 0) 10 newCapacity = hugeCapacity(minCapacity); 11 // minCapacity is usually close to size, so this is a win: 12 elementData = Arrays.copyOf(elementData, newCapacity); 13 }
描述:列表容量扩展的主要方法,结果是常规式扩展(原容量的1.5倍),或者最小可用容量,是在不大于最大数组长度的条件下进行的
3.ensureXxx
1 private static int calculateCapacity(Object[] elementData, int minCapacity) { 2 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 3 return Math.max(DEFAULT_CAPACITY, minCapacity); 4 } 5 return minCapacity; 6 } 7 8 private void ensureCapacityInternal(int minCapacity) { 9 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 10 } 11 12 // 根据指定容量确定明确的合适容量容量 13 private void ensureExplicitCapacity(int minCapacity) { 14 modCount++; 15 16 // overflow-conscious code 17 if (minCapacity - elementData.length > 0) 18 grow(minCapacity); 19 }
描述:这三个方法是为了确认当前容量是否足够
五.常用API
1.size
1 public int size() { 2 return size; 3 }
2.isEmpty
1 public boolean isEmpty() { 2 return size == 0; 3 }
描述:以上两个是直接使用size属性进行操作
3.indexOf
1 public int indexOf(Object o) { 2 if (o == null) { 3 for (int i = 0; i < size; i++) 4 if (elementData[i]==null) 5 return i; 6 } else { 7 for (int i = 0; i < size; i++) 8 if (o.equals(elementData[i])) 9 return i; 10 } 11 return -1; 12 }
4.lastIndexOf
1 public int lastIndexOf(Object o) { 2 if (o == null) { 3 for (int i = size-1; i >= 0; i--) 4 if (elementData[i]==null) 5 return i; 6 } else { 7 for (int i = size-1; i >= 0; i--) 8 if (o.equals(elementData[i])) 9 return i; 10 } 11 return -1; 12 }
5.contains
1 public boolean contains(Object o) { 2 return indexOf(o) >= 0; 3 }
6.remove
1 public boolean remove(Object o) { 2 if (o == null) { 3 for (int index = 0; index < size; index++) 4 if (elementData[index] == null) { 5 fastRemove(index); 6 return true; 7 } 8 } else { 9 for (int index = 0; index < size; index++) 10 if (o.equals(elementData[index])) { 11 fastRemove(index); 12 return true; 13 } 14 } 15 return false; 16 }
7.forEach
1 public void forEach(Consumer<? super E> action) { 2 Objects.requireNonNull(action); 3 final int expectedModCount = modCount; 4 @SuppressWarnings("unchecked") 5 final E[] elementData = (E[]) this.elementData; 6 final int size = this.size; 7 for (int i=0; modCount == expectedModCount && i < size; i++) { 8 action.accept(elementData[i]); 9 } 10 if (modCount != expectedModCount) { 11 throw new ConcurrentModificationException(); 12 } 13 }
描述:以上五个是遍历数组操作,需要注意的是,在遍历时需要区分 null 和非 null 时的相等比较
8.get
1 public E get(int index) { 2 rangeCheck(index); 3 4 return elementData(index); 5 }
9.set
1 public E set(int index, E element) { 2 rangeCheck(index); 3 4 E oldValue = elementData(index); 5 elementData[index] = element; 6 return oldValue; 7 }
10.remove
1 public E remove(int index) { 2 rangeCheck(index); 3 // 修改次数 4 modCount++; 5 E oldValue = elementData(index); 6 // 要移动的元素数,从 index + 1 到 size ,共 size - index - 1 个 7 int numMoved = size - index - 1; 8 if (numMoved > 0) 9 // 将数组从 index + 1 开始到 size 结束的 size - index - 1 个元素前移 10 System.arraycopy(elementData, index+1, elementData, index, 11 numMoved); 12 elementData[--size] = null; // clear to let GC do its work 13 14 return oldValue; 15 }
描述:以上三个是采用随机访问,其中的rangeCheck方法是对下标的判断是否越界
remove方法有重载方法,即remove(Object o),此方法是通过遍历数组的方式查找元素
11.toArray
1 public Object[] toArray() { 2 return Arrays.copyOf(elementData, size); 3 }
描述:直接对列表中维护的集合进行复制返回
12.add
1 public boolean add(E e) { 2 ensureCapacityInternal(size + 1); // Increments modCount!! 3 elementData[size++] = e; 4 return true; 5 } 6 7 public void add(int index, E element) { 8 // 校验 index ,需要额外验证是否小于 0 9 rangeCheckForAdd(index); 10 11 ensureCapacityInternal(size + 1); // Increments modCount!! 12 // 将数组从 index + 1 位置开始到 size 结束的 size - index 个元素后移 13 System.arraycopy(elementData, index, elementData, index + 1, 14 size - index); 15 elementData[index] = element; 16 size++; 17 }
描述:add方法是通过随机访问找到位置,然后添加(追加或者数组复制)
13.clear
1 public void clear() { 2 modCount++; 3 4 // clear to let GC do its work 5 for (int i = 0; i < size; i++) 6 elementData[i] = null; 7 8 size = 0; 9 }
描述:clear方法不是将列表清除,此时列表容量(数组和数组长度都)没有发生变化,只是将列表中的元素清空.
14.subList
1 public List<E> subList(int fromIndex, int toIndex) { 2 subListRangeCheck(fromIndex, toIndex, size); 3 return new SubList(this, 0, fromIndex, toIndex); 4 }
受限于个人水平,如有错误或者补充望请告知(博客园:xiao_lin_unit)