Java List two implementations ArrayList and LinkedList performance comparison and source code detailed explanation

Java ADT

ArrayList

Implementation of Growable Array of List Interface

Similar to the characteristics of arrays, the insertion and deletion of ArrayList is relatively expensive, but the get/set operation only consumes constant time.

LinkedList

Double linked list implementation of List interface

In fact, for LinkedList , deleting and inserting in the middle of the table is still a very expensive process . In the test, a LinkedList with a length of 10000 repeats remove(li.size()/2) or add(li.size( )/2, arg) operations 10,000 times, the actual efficiency of the middle segment of LinkedList is lower than that of ArrayList, and the gap is more obvious. But LinkedList is much more efficient than ArrayList in the header insertion and deletion .

Another point to note is that the remove() method of the LinkedList iterator Iterator is very efficient, because the Iterator basically knows the location of the target element when it calls remove.

The overhead of get / set is relatively large compared to ArrayList, which is why LinkedList is inefficient in inserting or deleting in the middle.

Example 1: Comparing the insertion efficiency of LinkedList and ArrayList at position 0

package ryo;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ListTest {
	
	public static void main(String[] args) {
		set(new ArrayList<Integer>() ,10000);
		set(new LinkedList<Integer>() ,10000);
		
	}
	
	public static void set(List<Integer> li ,int times) {
		long before = System.currentTimeMillis();
		for(int i=0 ;i<times ;i++) {
			li.add(0 ,i);
		}
		long after = System.currentTimeMillis();
		System.out.println(li.getClass().getName()+" :"+(after-before)+"ms");
	}

}

Example 2: Comparing the efficiency of LinkedList and ArrayList in get(length/2)

package ryo;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ListTest {
	
	public static void main(String[] args) {
		set(new ArrayList<Integer>() ,10000);
		set(new LinkedList<Integer>() ,10000);
		
	}
	
	public static void set(List<Integer> li ,int times) {
		for(int i=0 ;i<times ;i++) {
			li.add(0,1);
		}
		long before = System.currentTimeMillis();
		for(int i=0 ;i<times ;i++) {
			li.get(times/2);
		}
		long after = System.currentTimeMillis();
		System.out.println(li.getClass().getName()+" :"+(after-before)+"ms");
	}

}

However, when getting the header or footer of the table, the performance of LinkedList is almost the same as that of ArrayList. Benefiting from the double-linked list structure of LinkedList, the get operation near the end of the table will be performed from the end of the table forward.

Example 3 : Comparing the efficiency of LinkenList and ArrayList in remove(size/2)

The code follows the code of example 2, only the get is changed to remove(li.size()/2);

Source code analysis of ArrayList and LinkedList

LinkedList is mainly analyzed here. The implementation of ArrayList is actually not too difficult. Many operations use System.arraycopy. Due to the characteristics of arrays, we can only copy and then copy back to the original array. Take remove as an example:

 
 
public E remove(int index) {
        rangeCheck (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;
    }

Here I mainly want to explain the implementation of LinkedList, which includes the implementation of the double-linked list structure, which is abstracted into an inner class named Node:

 
 
	//Inner class of LinkedList
    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;
        }
    }

 The principle of LinkedList is to take remove as an example. All methods used in remove source code are given together with comments.

 
 
//linkedList的remove
	public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
	/ / Determine the legitimacy of the index
	private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
	//This method will return the node at the index position
	Node<E> node(int index) {
		//here >> means right shift is equivalent to size/2
        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;
        }
    }
	
	//Remove the connection relationship of node x
	E unlink(Node<E> x) {
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
		//If x is the first, the firstnode of linkedList becomes the nextnode of x
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;//The front node next of x points to the back node of x
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;//The back node prev of x points to the front node of x
            x.next = null;
        }
        x.item = null;//The purpose of assigning null here is mainly to GC the memory of the instance
        size--;
        modCount++;
        return element;
    }

Conclusion: It is not difficult to know from the test results and principle analysis that when most operations ( regardless of additions, deletions, modifications ) occur at the head or tail of the table, the performance of LinkedList is better than that of ArrayList , but when operations frequently occur at size/2 position, the performance of LinkedList is greatly reduced, far less than ArrayList

Here is a simple singly linked list structure I customized, not very perfect, but the basic functions are complete


package com.ryo.structure.linkedlist;

import com.ryo.structure.DataStructure;

/**
 * A custom singly linked list
 * The insert method is not overridden
 * A method to check the validity of index is not set
 * @author shiin
 * @param <T> storage type
 */
public class SinglyLinkedList<T> implements DataStructure<T>{
	
	private Node<T> first;
	private Node<T> last;
	private int size;
	
	public SinglyLinkedList(){
		
	}
	
	public SinglyLinkedList(T[] arr) {
		this();
		addArray(arr);
	}
	
	private void addArray(T[] arr) {
		if(arr == null || arr.length == 0) {
			return;
		}
		else {
			size = arr.length;
			@SuppressWarnings("unchecked")
			Node<T>[] temp = new Node[arr.length];
			for(int i=0 ;i<size ;i++) {
				temp[i] = new Node<T>(arr[i] ,null);
			}
			for(int i=0 ;i<size-1 ;i++) {
				temp[i].next = temp[i+1];
			}
			first = temp[0];
			last = temp[size-1];
		}
	}

	@Override
	public void insert(T newEn) {
		return;
	}

	@Override
	public void delete(int index) {
		if(index < 0) {
			return;
		}
		else if(index == 0) {
			if(first.next != null) {
				first = first.next;
			}
		}
		else{
			beforeIndexNode(index).next = beforeIndexNode(index+2);
			
		}
		size--;
	}

	@Override
	public int size() {
		return this.size;
	}

	@Override
	public void add(T newEn) {
		Node<T> newlast = new Node<T>(newEn ,null);
		last.next = newlast;
		this.last = newlast;
		size++;
	}
	

	@Override
	public T get(int index) {
		if(index == 0) {
			return first.item;
		}
		else
			return beforeIndexNode(index).next.item;
	}
	
	/**
	 * Return a node before the target node
	 * Because the delete method needs to change the next node of the previous node
	 * @param index target index
	 * @return node at index-1 position
	 */
	private Node<T> beforeIndexNode(int index){
		if(index == 1) {
			return first;
		}
		else {
			Node<T> temp = first;
			for(int i=0 ;i<index-1 ;i++) {
				temp = temp.next;
			}
			return temp;
		}
	}
	
	/**
	 * Nodes of a singly linked list
	 * @author shiin
	 * @param <T>
	 */
	private static class Node<T>{
		T item;	
		Node<T> next;
		
		Node(T item ,Node<T> next){
			this.item = item;
			this.next = next;
		}
	}


}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325660544&siteId=291194637