美文网首页
1.ArrayList和LinkedList对比分析

1.ArrayList和LinkedList对比分析

作者: Junma_c631 | 来源:发表于2020-09-08 09:35 被阅读0次
    【参考文档】
    鲁班语雀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;
        }
    

    相关文章

      网友评论

          本文标题:1.ArrayList和LinkedList对比分析

          本文链接:https://www.haomeiwen.com/subject/fqjkektx.html