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