ArrayList and LinkedList source of learning, understanding both the insert, delete, and search performance difference

List of use

List of sub-categories

1). ArrayList

  数据结构:数组

2). Vector

  数据结构:数组

3). LinkedList

   数据结构:循环双向链表

ArrayList, Vector, LinkedList comes from the realization AbstractList, AbstratList direct implementation of the List interface and extended self AbstactCollection.

A), the operation of the ArrayList

== == ArrayList property

//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空元素数据
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认容量为空的空元素数据
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//底层结构
transient Object[] elementData;
//数组的大小
private int size;

== == create ArrayList object

The default size of the array elementData = {} When creating a new ArrayList ()

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //{}
    }

== ArrayList to be source code analysis of the operation of the Add ==

1). When you first add elements to re-establish elementData array of length 10

2) to determine whether additional capacity is needed by the internal volume ensureCapacityInternal (size + 1) elements are added

I: 当内部容量需要扩容时,在原数组长度的基础上以1.5倍的规则进行扩展。

   II: 通过System.arraycopy()将原数组的元素复制到新数组中。

3) add an element to the end of add (E e):

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

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;
        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);
    }
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

4) add an element to a specified position add (int index, E element):

public void add(int index, E element) {
        //范围检查
        rangeCheckForAdd(index);
        //是否进行扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
    
        /**
        以index为原数组elementData的起始位置,顺序获取size -             index个元素,将这size - index个元素复制到以index+1为起始
        位置的elementData数组中。
        */
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        elementData[index] = element;
        size++;
    }

Add core code elements:

/**
        以index为原数组elementData的起始位置,顺序获取size -             index个元素,将这size - index个元素复制到以index+1为起始
        位置的elementData数组中。
        */
        System.arraycopy(elementData, index, elementData, index + 1,size - index);

== delete the specified location element remove (int index) ==

public class Test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("a");
        list.remove(0);
    }
}

public E remove(int index) {
        //范围查找
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            /**
            将elementData数组的的元素从index+1,开始共复制numMove
            个元素到elementData中从index开始为起始赋值位置
            */
            System.arraycopy(elementData, index+1, elementData, index,numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

remove the core code:

 /**
 将elementData数组的的元素从index+1,开始共复制numMove
 个元素到elementData中从index开始为起始赋值位置
 */
System.arraycopy(elementData, index+1, elementData, index,numMoved)

Two), the operation of LinkedList

Definition: A LinkedList connected together by a plurality of entries, one entry consists of three parts, the predecessor table entry, the contents, the drive entry.

== add elements add to the end (E) ==

public class Test {
    public static void main(String[] args) {
        LinkedList list1 = new LinkedList();
        list1.add("1");
    }
}


public boolean add(E e) {
        linkLast(e);
        return true;
}
void linkLast(E e) {
    //获取链表的最后一个表项
        final Node<E> l = last;
    //创建一个新的表项,将链表的最后一个表项设置为新表项的前驱表项
        final Node<E> newNode = new Node<>(l, e, null);
    //将链表的最后的表项置为新添加的表项
        last = newNode;
    //判断链表是否为null,若链表为空则将当前新建表项设为第一个表项
        if (l == null)
            first = newNode;
    //若最后一个表项不为空,则将该表项的next指针指向新的表项
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    }

== insert elements add to the specified position (int index, E e) ==

public class Test {
    public static void main(String[] args)  {
        LinkedList<String> list1 = new LinkedList();
        list1.add("1");
        list1.add(0,"bb");
         list1.add(0,"bb");
        System.out.println(list1.size());//size = 3
    }
}


public void add(int index, E element) {
        //检查范围,判断需要插入的位置是否在size范围内
        checkPositionIndex(index);
        //末尾插入
        if (index == size)
            linkLast(element);
        //在某个位置前插入
        else
            linkBefore(element, node(index));
    }

//根据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;
        }
    }

//改变链表的部分指向
void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

in conclusion:

     1).当需要向List的末尾添加元素时,使用ArrayList的效率比LinkedList的效率

          高,但总体速率相差不大。

   原因: 

      i).使用LinkedList添加元素时每次都要new 一个Node对象,不间断的生成新

           的对象占用了一定的系统资源。

        ii).ArrayList只有在空间不足的情况下才会产生数组扩容和数组复制,所以

            决定大部份的追加操作效率非常高。



   2).操作List向指定位置添加元素时,若插入的位置在前半段或后半段部分使用

    LinkedList进行插入,若插入位置在中间使用ArrayList进行插入性能较好,若

    插入的数据在末尾 LinkedList和ArrayList的速率大致相同。



   3). 当频繁对List进行删除和插入操作时使用LinkedList.



    注:删除指定位置的元素原理和结论与在指定位置插入元素相同。

== delete the specified location element remove (index) ==

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
//先找到对应的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;
        }
    }
//修改链表的指向
E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

C), a traverse set of List

Three kinds of traversal:

List<String> list = new ArrayList();
List<String> list = new LinkedLisr();

//foEach;增强for循环
String temp = null;
for(String str : list){
    temp = str;
}

//迭代器
for(Iterator it = list.Iterator() ; it.hasNext() ; ){
    temp = it.next();
}

//for循环
for(int i = 0 ; i < list.size() ; i++){
    temp = list.get(i)
}

in conclusion:

1) When using an iterator traverses the same speed LinkedList and ArrayList list

2) Using forEach LinkedList when traversing the same speed and an ArrayList

3). Iterates over faster than the speed of traverse forEach

4) When used for traversing the list when the circulation rate is much greater than the LinkedList ArrayList.

Cause: The for loop iterates using a random access mechanism. LinkedList when traversing the list, each time get (int index), the list should be traversed once operation, index to find the corresponding entry, and then remove the entry corresponding to the value, and ArrayList sequence storage array based structure, directly through the standard elements can be acquired.

Over the rate list:

Iterator> forEach> for loop

D) why easier to traverse rate is less than forEach iterates over it?

The reason: decompile know forEach is based traversal iterator traversal implemented

Decompilation iterates over:

for(Iterator it = list.Iterator() ; it.hasNext() ; ){
    String s = it.next();
    String s1 = s; //forEach遍历比迭代器遍历多了一步赋值操作
    
}

Iterates over:

for(Iterator it = list.Iterator() ; it.hasNext() ; ){
    String s = it.next(); 
}

Guess you like

Origin www.cnblogs.com/Auge/p/11649304.html