ArrayList和LinkedList源码阅读笔记


List 接口继承自Collection接口,并在Collection接口的基础上添加了大量的方法,使得可以在List 的中间插入和删除元素。
List 按照插入的顺序来保存元素,它常用的实现类主要有两种:ArrayList和LinkedList,其中ArrayList 擅长随机访问元素操作,但是在中间插入和存取元素时比较慢;而LinkedList在中间进行插入和删除的操作比较快,随机访问方面相对较慢。只要阅读以下这两个类的底层实现方式,就很容易理解它们各自的优势。 

ArrayList:

ArrayList 的列表对象实质是存放在一个引用型的数组内,并通过不断的改变该引用型数组的指向来改变数组的容量。

ArrayList 类内部:

private transient Object[] element;//存放列表对象的数组的引用,
private int size; //用来记录列表中实际对象的数量;
同时还引入Capacity,用来不断调整存放对象的数组的大小。

ArrayList 随机访问元素:

因为底层是通过Object类型的数组实现,所以可以直接通过数组下标来访问任何元素。

 

ArrayList中间插入和删除元素:

   public void add(int index, E element) {
        rangeCheckForAdd(index);
 
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData,index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
   }


可以看出,当向ArrayList的index位置中插入元素时,包括index以后的所有元素都要向后移动一位,需要进行很多的赋值操作,所以ArrayList在中间插入和删除代价较大。

 

当向ArrayList增加元素时,必须保证数组的容量能够确保这个元素还能再加进去。这时候如果数组的size与capacity的值一样,就必须增大capacity的值,使add操作能顺利进行。ArrayList 通过下面方法实现自动改变数组大小机制:

private void ensureCapacityInternal(intminCapacity) {
        modCount++;
        // add 方法前面通常会有ensureCapacityInternal(size + 1),当size + 1的值比数组容量的值小时,则不做任何操作,当size + 1的值比数组容量的值大时,则要扩大数组的容量。
 
        if (minCapacity - elementData.length> 0)
            grow(minCapacity);
}
 
private staticfinal int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private voidgrow(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);
       
//这步操作是关键,通过改变elementData的引用来实现数组容量的自动改变机制。
        element = Arrays.copyOf(elementData, newCapacity);
   }
   private static int hugeCapacity(intminCapacity) {
        if (minCapacity < 0) //overflow
            throw newOutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE)?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
}

通过以上代码,我们可知java自动增加ArrayList大小的思路是:向ArrayList添加对象时,原对象数目加1后如果大于数组长度,则建一个适当长度的新数组,并将原数组的内容进行拷贝,然后让element对这个新数组进行引用。原数组会自动抛弃(java垃圾回收机制会自动回收)。

 

LinkedList:

LinkedList内部定义了Node类型的结点,其中包括列表对象的值,以及前一个结点和后一个结点的引用

    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
 
        Node(Node<E> prev, E element, Node<E>next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
}


LinkedList 通过Node<E> first,Node<E> last两个属性用来标记该链表的首结点和尾结点。

 

LinkedList 在中间增加和删除元素:

LinkedList 找到index所在位置的结点,然后通过改变该位置结点的prev和next 的引用来进行插入和删除操作。

   

 public void add(intindex, E element) {
        checkPositionIndex(index);
 
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
    void linkBefore(E e, Node<E>succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = newNode<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
}


LinkedList随机查找的操作:

  

  public E get(intindex) {
        checkElementIndex(index);
        return node(index).item;
}
   Node<E> node(int 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;
       }
    }

如果index比size/2小,就从前往后找,如果index比size/2大,就从后往前找。虽然对查找进行了一定的优化,但是查找时间级别仍是O(n),而ArrayList随机访问的时间是O(1)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值