1.ArrayList和LinkedList对比分析

2020-09-08  本文已影响0人  Junma_c631
【参考文档】
鲁班语雀ArrayList:[https://www.yuque.com/books/share/9f4576fb-9aa9-4965-abf3-b3a36433faa6/fua39n](https://www.yuque.com/books/share/9f4576fb-9aa9-4965-abf3-b3a36433faa6/fua39n)

java中的浅拷贝和深拷贝:[https://www.cnblogs.com/ysocean/p/8482979.html](https://www.cnblogs.com/ysocean/p/8482979.html)

Fail-Fast机制:[https://www.cnblogs.com/fanlinglong/p/6627393.html](https://www.cnblogs.com/fanlinglong/p/6627393.html)

一、通常ArrayList和LinkedList给人的感觉

ArrayList:读快,写慢
LinkedList:读慢,写快

二、而实际情况是什么呢?

1.先来看看ArrayList

【读分析】
ArrayList读之所以快,是因为ArrayList底层是维护的一个数组,数组在内存中是连续的,
并且每个数组元素分配的内存大小一致,所以程序在读取时只需知道数组下标,就可以
定位到数组元素的起始偏移量,读取效率相当快。
//elementData是arrylist中维护的数组 
public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }
//根据下标获取数组的元素
E elementData(int index) {
        return (E) elementData[index];
}
【写分析】
创建ArrayList如果不指定长度,在add操作时,会通过Arrays.copyOf扩容到默认数组长度10,
如果写入的元素数远远大于数组长度10,那就会每隔一段时间进行一次数组扩容copy,每次扩容是原数组长度的1.5,
在扩容的时候,会进行数组copy,数组元素越多,copy所消耗的性能越大,这才是导致arraylist写操作慢的原因,如果在创建
arraylist时指定合适的数组长度,使其不进行数组扩容,就会解决写入慢的问题。
public boolean add(E e) {
        //判断数组是否已满,数组满的话进行数组扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //添加数组元素
        elementData[size++] = e;
        return true;
    }
   private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, 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;
        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);
    }

2.LinkedList分析

【读分析】
LinkedList的get(i)方法需要通过循环遍历查找i所在的节点对象,一旦涉及到循环,效率就不会
高。
有一种高效率的读取方式就是通过iterate,遍历读取,这种读取方式,只会遍历一次查找到遍历的
起点节点,后续的遍历调用iter.next()方法是通过当前节点维护的next
节点或者pre节点的指针进行循环。
【get(i)方法】
public E get(int index) {
       //检查index是否合法
        checkElementIndex(index);
       //循环查找node返回node.item
      //其中item就是我们存入的元素 
       return node(index).item;
    }
//先判断index是跟起始节点近还是跟末尾节点近
//跟谁近就从哪头开始遍历
Node<E> node(int index) {
        // assert isElementIndex(index);
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
【迭代器的next()】
public ListIterator<E> listIterator(int index) {  // 继承回调该方法
    checkPositionIndex(index);
    return new ListItr(index);
}
 
private class ListItr implements ListIterator<E> {
    private Node<E> lastReturned;  // 使用node节点查找并缓存数据
    private Node<E> next;
    private int nextIndex;  // 是否遍历结束
    private int expectedModCount = modCount;
 
    ListItr(int index) {
        // assert isPositionIndex(index);
        next = (index == size) ? null : node(index);
        nextIndex = index;
    }
 
    public boolean hasNext() {
        return nextIndex < size;
    }
 
    public E next() {
        checkForComodification();
        if (!hasNext())
            throw new NoSuchElementException();
 
        lastReturned = next;
        next = next.next;
        nextIndex++;
        return lastReturned.item;
    }
 
    public boolean hasPrevious() {
        return nextIndex > 0;
    }
 
    public E previous() {  // 支持前后两种遍历方法
        checkForComodification();
        if (!hasPrevious())
            throw new NoSuchElementException();
 
        lastReturned = next = (next == null) ? last : next.prev;
        nextIndex--;
        return lastReturned.item;
    }
 
    public int nextIndex() {
        return nextIndex;
    }
 
    public int previousIndex() {
        return nextIndex - 1;
    }
 
    public void remove() {  // 不带索引值的移除
        checkForComodification();
        if (lastReturned == null)
            throw new IllegalStateException();
 
        Node<E> lastNext = lastReturned.next;
        unlink(lastReturned);
        if (next == lastReturned)
            next = lastNext;
        else
            nextIndex--;
        lastReturned = null;
        expectedModCount++;
    }
 
    public void set(E e) {
        if (lastReturned == null)
            throw new IllegalStateException();
        checkForComodification();
        lastReturned.item = e;
    }
 
    public void add(E e) {  // 添加和重置元素值的功能
        checkForComodification();
        lastReturned = null;
        if (next == null)
            linkLast(e);
        else
            linkBefore(e, next);
        nextIndex++;
        expectedModCount++;
    }
 
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (modCount == expectedModCount && nextIndex < size) {
            action.accept(next.item);
            lastReturned = next;
            next = next.next;
            nextIndex++;
        }
        checkForComodification();
    }
 
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}
【写分析】
以add(str)为例,默认添加到队列末端,添加元素只是把新元素设置为
last,把新元素的prev元素设置成旧的末端元素,把旧的末端元素的
next元素设置成新元素;总之就是指针的一种切换,效率比较高。
 /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
     Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
    }
上一篇下一篇

猜你喜欢

热点阅读