深入研究集合-List

目录

6.1.ArrayList

1.组成:

2.怎么扩容

3.新增

4.插入

5、插入数组

6、删除

6.2.LinkedList

1.组成:

2.新增

3.插入

4.删除

6.3两种List使用对比


参考书:《很好-Java程序性能优化.pdf》

List有三种:ArrayList、Vector和LinkedList.我们只讨论Arrylist和LinkedList

以下研究都是在jdk1.7

6.1.ArrayList

讨论:组成,初始化大小,怎么扩容,怎么插入、删除

1.组成:

内部封装了对数组的操作,对他进行添加、删除、插入都是对数组的操作.

代码:

public class ArrayList<E> {

    private static final int DEFAULT_CAPACITY = 10;//默认容量

    private transient Object[] elementData;//数组

    private int size;//数组实际存储大小,并不是数组的大小

}

原文:https://blog.csdn.net/lan861698789/article/details/81323634

2.怎么扩容

如果不指定容量,那么默认容量大小就是10。

每次对数组进行添加、插入都需要判断 内部数组是否有足够的空间,没有则扩容。

扩容大小为原来大小的1.5倍。

以添加为例,代码:

public boolean add(E e) {

    ensureCapacityInternal(size + 1);  // Increments modCount!!

    elementData[size++] = e;

    return true;

}

private void ensureExplicitCapacity(int minCapacity) {

    modCount++;

    if (minCapacity - elementData.length > 0) {//判断是否需要扩容

        int oldCapacity = elementData.length;//现在容量大小

        int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容原来容量的1.5

        if (newCapacity - minCapacity < 0) //如果扩容后还是不能满足,则当前容量设置为 需要插入的大小

            newCapacity = minCapacity;

 

        elementData = Arrays.copyOf(elementData, newCapacity);//数组的扩容

    }

}

合理的数组大小,有助于减少数组扩容的次数,从而提高系统性能.

原文:https://blog.csdn.net/lan861698789/article/details/81323634

3.新增

新增是加到数组的最后一个。

代码:

public boolean add(E e) {

    ensureCapacityInternal(size + 1);  // Increments modCount!!

    elementData[size++] = e;

    return true;

}

原理:

a.扩容一个长度

b.将数组的最后一位赋值为要增加的元素

原文:https://blog.csdn.net/lan861698789/article/details/81323634

4.插入

任意位置添加元素.

代码:

public void add(int index, E element) {

    ensureCapacityInternal(size + 1);  // Increments modCount!!

    System.arraycopy(elementData, index, elementData, index + 1, size - index);

    elementData[index] = element;

    size++;

}

原理:

a.扩容一个长度

b.把从插入位置的元素往后移一位

c.赋值要插入的元素

原文:https://blog.csdn.net/lan861698789/article/details/81323634

5、插入数组

和插入一个元素区别不大。

代码:

public boolean addAll(int index, Collection<? extends E> c) {

    Object[] a = c.toArray();

    int numNew = a.length;

    ensureCapacityInternal(size + numNew);  // Increments modCount

 

    int numMoved = size - index;

    if (numMoved > 0)

        System.arraycopy(elementData, index, elementData, index + numNew, numMoved);//部分元素整体右移

 

    System.arraycopy(a, 0, elementData, index, numNew);//复制要插入的元素数组

    size += numNew;

    return numNew != 0;

}

 

原理:

原文:https://blog.csdn.net/lan861698789/article/details/81323634

6、删除

代码:

public E remove(int 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;

}

原文:https://blog.csdn.net/lan861698789/article/details/81323634

6.2.LinkedList

讨论:组成,初始化大小,怎么扩容,怎么插入、删除

1.组成:

使用了双向链表的数据结构。

不需要扩容。

linkedlist只会存这个容器的长度,首、尾元素。

然后每个元素里面包含了该元素的当前值,前一个元素,后一个元素。

代码

public class LinkedList<E> {

    transient int size = 0;

    transient Node<E> first;//头节点

    transient Node<E> last;//尾节点

}

 

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;

    }

}

原文:https://blog.csdn.net/lan861698789/article/details/81323634

原文:https://blog.csdn.net/lan861698789/article/details/81323634

2.新增

新增是往最后插的。

代码:

void linkLast(E e) {

    final Node<E> l = last;//原来的last赋值

    final Node<E> newNode = new Node<>(l, e, null);//构建新节点,这里把原来的last赋值给了新节点的pre

    last = newNode;//last赋值

    l.next = newNode;

    size++;

    modCount++;

}

原理:

构建新节点,把新节点的prev指向原来的最后一个节点;

把原来最后一个节点的next指向新节点。

原文:https://blog.csdn.net/lan861698789/article/details/81323634

3.插入

插入任一点

代码:

public void add(int index, E element) {

    linkBefore(element, node(index));

}

//找出该位置的节点值

Node<E> node(int index) {

    //做了一个size判断。是从头循环开始找,还是从尾循环开始找

    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;

    }

}

//插入

void linkBefore(E e, Node<E> succ) {

    final Node<E> pred = succ.prev;

    final Node<E> newNode = new Node<>(pred, e, succ);

    succ.prev = newNode;

    pred.next = newNode;

    size++;

    modCount++;

}

原理:

从首or尾节点循环遍历到该index位置,找出要插入位置的值

再进行 各种赋值。

原文:https://blog.csdn.net/lan861698789/article/details/81323634

4.删除

删除任意位置的值

代码:

public E remove(int index) {

    return unlink(node(index));

}

E unlink(Node<E> x) {

    // assert x != null;

    final E element = x.item;

    final Node<E> prev = x.prev;

    final Node<E> next = x.next;

    //前面元素的next 重新赋值

    prev.next = next;

    x.prev = null;

    //后面元素的prev 重新赋值

    next.prev = prev;

    x.next = null;

    //当前元素置为空

    x.item = null;

    size--;

    modCount++;

    return element;

}

原理:

从首or尾节点循环遍历到该index位置,找出要插入位置的值

再进行 各种赋值。

原文:https://blog.csdn.net/lan861698789/article/details/81323634

6.3两种List使用对比

如果是经常要进行任意位置的插入,建议使用linkedList;不使用arrayList,是因为要频繁扩容。

如果是经常要进行任意位置的查找,建议使用arrayList;不使用linkedlist,是因为要循环查找。

猜你喜欢

转载自blog.csdn.net/lan861698789/article/details/81323634