Java Collection框架 - ArrayList

    xiaoxiao2021-03-25  92

    Java Collection框架 - ArrayList

    基于jdk1.8

    简介

    ArrayList是基于数组实现的动态数组,不是线程安全的

    时间复杂度: * 查找和修改 ==> O(1) * 删除和添加(除了向末尾添加) ==> O(n)

    数据结构

    先看下ArrayList的数据结构:

    ArrayList的数据结构很简单,就是一个数组跟一个size属性作为尾指针

    主要属性与方法列表

    //代表ArrayList默认容量 10 DEFAULT_CAPACITY : int //内部数组 elementData : Object[] //容量 size : int //最大容量 Integer.MAX_VALUE - 8 MAX_ARRAY_SIZE : int trimToSize() : void ensureCapacity(int) : void size() : int isEmpty() : boolean contains(Object) : boolean indexOf(Object) : int lastIndexOf(Object) : int clone() : Object toArray() : Object[] toArray(T[]) : T[] get(int) : E set(int, E) : E add(E) : boolean add(int, E) : void remove(int) : E remove(Object) : boolean clear() : void addAll(Collection<? extends E>) : boolean addAll(int, Collection<? extends E>) : boolean removeAll(Collection<?>) : boolean retainAll(Collection<?>) : boolean listIterator(int) : ListIterator<E> listIterator() : ListIterator<E> subList(int, int) : List<E> sort(Comparator<? super E>) : void

    主要代码分析

    查找

    ArrayList提供了get(int index)用于读取数组中的元素。由于ArrayList使用数组实现,所以可以直接使用下标来获取元素

    public E get(int index) { rangeCheck(index); return elementData(index); } E elementData(int index) { return (E) elementData[index]; }

    增加

    ArrayList提供了add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)四个方法来添加元素

    add(E e):将元素添加到ArrayList末尾 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }

    这里的ensureCapacityInternal()方法的作用是尝试对数组进行扩容

    add(int index, E element):将元素添加至指定位置,并将之后的元素向后移一位 public void add(int index, E element) { //判断索引位置是否合法 rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! //将原数组从index之后的元素,全部向后移动一位 //其目的是将index位置空出来 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }

    这里的rangeCheckForAdd(index)方法的作用是检查index的值是否在0~size之间。ensureCapacityInternal()方法的作用与上面一样,都是尝试对数组进行扩容。System.arraycopy()方法用于将elementData从index开始的元素复制到elementData的index+1位置,一共复制size-index个元素,目的是将index位置空出来,方便后来的重新赋值

    addAll(Collection<? extends E> c):将集合内的所有元素依次添加到ArrayList末尾 public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount //将a数组内的元素添加到内部数组中 System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }

    这里的System.arraycopy()方法用于将a数组从0开始的所有元素复制到elementData的size位置,一共复制numNew个,使用System.arraycopy()方法相比于一个个复制速度更快

    addAll(int index, Collection<? extends E> c):将集合内所有元素依次添加到指定位置 public boolean addAll(int index, Collection<? extends E> c) { //确定索引位置是否合法 rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount //计算需要后移的元素个数 int numMoved = size - index; //如果大于0,就将指定元素后移 if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; }

    在这里System.arraycopy()的作用是先将指定元素后移numNew位(如果有必要),然后将a数组中的元素复制到指定位置

    修改

    ArrayList提供了set(int index, E element)方法来修改指定位置的元素

    set(int index, E element):将index位置的元素设置为element public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }

    与add(int index, E element)方法类似,只不过不需要后移元素了

    删除

    ArrayList提供了remove(int index)、remove(Object o)、removeAll(Collection<?> c)、retainAll(Collection<?> c)四个方法来删除元素

    remove(int index):删除指定位置的元素 public E remove(int index) { rangeCheck(index); modCount++; //获取指定位置的元素 E oldValue = elementData(index); //计算需要移动的元素个数 int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; } remove(Object o):删除指定元素 public boolean remove(Object o) { if (o == null) { //删除元素为null的情况 for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { //不为null for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //将最后的多余元素清除,防止内存泄漏 elementData[--size] = null; // clear to let GC do its work } removeAll(Collection<?> c):删除所有集合中包含的元素 public boolean removeAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, false); } private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++) //保留元素策略取决于complement参数 //用于实现removeAll跟retainAll方法 if (c.contains(elementData[r]) == complement) //将保留元素移动到列表头部 elementData[w++] = elementData[r]; } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. //当contains方法抛出异常时,r不会等于size if (r != size) { //0~w:需要保留 //w~r:需要删除 //r~size:不能确定 //只删除必定需要删除的元素 System.arraycopy(elementData, r, elementData, w, size - r); w += size - r; } if (w != size) { // clear to let GC do its work for (int i = w; i < size; i++) elementData[i] = null; modCount += size - w; size = w; modified = true; } } return modified; }

    removeAll()方法依赖于batchRemove()方法,通过complement属性指定需要删除的元素为c集合包含的元素

    retainAll(Collection<?> c):删除所有集合中不包含的元素 public boolean retainAll(Collection<?> c) { Objects.requireNonNull(c); return batchRemove(c, true); }

    与removeAll()方法类似,通过complement属性指定需要删除的元素为c集合不包含的元素

    扩增

    ArrayList提供了ensureCapacity(int minCapacity)、ensureCapacityInternal(int minCapacity)两个方法供其他方法调用,用于扩增容量。这两个方法都依赖ensureExplicitCapacity(int minCapacity)方法,ensureExplicitCapacity(int minCapacity)方法又依赖grow(int minCapacity)方法

    public void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table ? 0 // larger than default for default empty table. It's already // supposed to be at default size. : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //计算新的数组容量,以1.5倍扩容 //1.5倍是通过测试得到的一个最佳值 //以1.5倍扩容。既不会消耗太多性能,也不会消耗太多内存 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }

    迭代器

    ArrayList实现了Itr、ListItr两个迭代器。ArrayList的迭代器能在遍历的同时添加或删除元素,是由于在这两个方法中修改了迭代器的expectedModCount记录

    public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; //修改记录 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } public void add(E e) { checkForComodification(); try { int i = cursor; ArrayList.this.add(i, e); cursor = i + 1; lastRet = -1; //修改记录 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }

    总结

    ArrayList的默认初始容量为10ArrayList以1.5倍扩容
    转载请注明原文地址: https://ju.6miu.com/read-17045.html

    最新回复(0)