Analyze the source code of ArrayList and LinkedList list structure in Java _java

Source: Internet
Author: User
Tags array length int size prev static class concurrentmodificationexception

First, ArrayList source analysis (JDK7)

ArrayList internal maintenance of a dynamic object array, ArrayList dynamic additions and deletions is to the group's dynamic additions and deletions.

1, ArrayList Construction and initialization

ArrayList instance variable
//arraylist default capacity
private static final int default_capacity = ten;
The default null Object array, which is used to define the empty ArrayList
private static final object[] Empty_elementdata = {};
ArrayList stores an Object array that holds elements
private transient object[] elementdata;
Number of elements in ArrayList
private int size;

ArrayList constructor:

Parameterless constructor: Constructing an empty object[]

Public ArrayList () {
  super ();
  This.elementdata = Empty_elementdata;
}

Specify capacity size constructs:

Public ArrayList (int initialcapacity) {
  super ();
  if (initialcapacity < 0)
    throw new IllegalArgumentException ("illegal Capacity:" +
                      initialcapacity);
  This.elementdata = new object[initialcapacity];
}

Specifies a collection construct for an implementation collection interface:

Public ArrayList (COLLECTION< extends e> c) {
  elementdata = C.toarray ();
  size = Elementdata.length;
  C.toarray might (incorrectly) not return object[] (= 6260652)
  if (Elementdata.getclass ()!= object[].class) 
   elementdata = arrays.copyof (elementdata, size, object[].class);
}

This also explains the role of collection in Java-collection-framwork designing collection interfaces rather than directly using interfaces such as List,set.

2, ArrayList capacity allocation mechanism

ArrayList capacity limit: ArrayList capacity is capped, the theory allows the allocation of integer.max_value-8 size capacity. But how much can be allocated is also related to stack settings, you need to set VM parameters

private static final int max_array_size = integer.max_value-8;

Expansion rule when calling Add method

Public boolean Add (E e) {
    ensurecapacityinternal (size + 1);//increments modcount!!
    elementdata[size++] = e;
    return true;
  }

The ensurecapacityinternal (int) method actually determines a minimum expansion size.

private void ensurecapacityinternal (int mincapacity) {
    if (Elementdata = = 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);
  }

With regard to the MODCOUNT:MODCOUNT definition in the abstract class abstratlist, the source annotation basically explains its usefulness: to check whether the elements in the list have structural changes (a count of changes in the number of list elements) when iterating through the iterator. Mainly in a multi-threaded environment to use, to prevent one thread is iterating, another thread modified the structure of the list.
The Grow method is the real expansion method

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, and so is a win:
    Elementdata = arrays.copyof (Elementdata, newcapacity); 
   
    }

   

And there's a hugecapacity method for how much capacity is enlarged.

private static int hugecapacity (int mincapacity) {
    if (mincapacity < 0)//overflow
      throw new OutOfMemoryError ( );
    Return (Mincapacity > max_array_size)?
      Integer.max_value:
      max_array_size;
  }

Summarize:
Each expansion will be accompanied by an array of replication operations, so a given proper capacity at a time can improve performance.
The diagram below is the entire expansion process I've summed up:

3.ArrayList iterators

ArrayList's iterator has two main itr and Listitr, but in jDK1.8 also added a arraylistspliterator, below learn ITR and LISTITR source code analysis.

(1) Itr: can only traverse backwards

Private class Itr implements iterator<e> {int cursor; Index of next element to return int lastret =-1; Index of the last element returned;
    -1 If no such//expectedmodcount is a copy of modcount int expectedmodcount = Modcount;
    public Boolean Hasnext () {return cursor!= size;
      @SuppressWarnings ("unchecked") public E Next () {checkforcomodification ();
      Record current position int i = cursor;
      if (i >= size) throw new nosuchelementexception ();
      object[] Elementdata = ArrayList.this.elementData;
      if (i >= elementdata.length) throw new Concurrentmodificationexception ();
      The position of the next element cursor = i + 1;
    Return (E) Elementdata[lastret = i];
      ///Use the Remove method of the iterator public void remove () {if (Lastret < 0) throw new IllegalStateException ();
      Checkforcomodification ();
        try {//Note the way the inner class calls the outer class ArrayList.this.remove (Lastret); You need to resize each pointer after remove.Position cursor = Lastret;
        Lastret =-1;
      Expectedmodcount = Modcount;
      The catch (Indexoutofboundsexception ex) {throw new concurrentmodificationexception (); } final void Checkforcomodification () {if (Modcount!= expectedmodcount) throw new Concurrentmodi
    Ficationexception ();
 }
  }

From the source, you can see that the ITR iterator is a forward iterator, and it provides a next method to get the elements in the ArrayList.
Checkforcomodification is a fail-fast error detection mechanism in Java-collection-framwork. In a multithreaded environment to the same set operation, it may trigger the fail-fast mechanism, throw concurrentmodificationexception exception.

The ITR iterator defines a copy of a expectedmodcount record modcount. The value of the Modcount is changed when the ArrayList performs an operation that alters the structure, such as the Add, remove, and clear methods.

The ITR source code shows that the call next and remove methods trigger the Fail-fast check. At this point, if the collection is traversed, an exception occurs when another thread is performing an operation that changes the structure of the collection.

(2) Listitr: Support forward and backward traversal, the following look at the source of Listitr:

Private class Listitr extends Itr implements listiterator<e> {listitr (int index) {super ();
    cursor = index;
    public Boolean hasprevious () {return cursor!= 0;
    public int Nextindex () {return cursor;
    public int Previousindex () {return cursor-1;
      @SuppressWarnings ("unchecked") public E Previous () {checkforcomodification ();
      ArrayList the position of the previous element int i = cursor-1;
      if (I < 0) throw new nosuchelementexception ();
      object[] Elementdata = ArrayList.this.elementData;
      if (i >= elementdata.length) throw new Concurrentmodificationexception ();
      cursor = i;
    Return (E) Elementdata[lastret = i];
      The Set method public void set (E e) {if (Lastret < 0) throw new IllegalStateException () was added to the iterator.
      Checkforcomodification ();
      try {ArrayList.this.set (Lastret, E);
    catch (Indexoutofboundsexception ex) {    throw new Concurrentmodificationexception ();
      The Add method public void Add (e) {checkforcomodification () was added by the iterator.
        try {int i = cursor;
        ArrayList.this.add (i, E);
        Re-mark pointer position cursor = i + 1;
        Lastret =-1;
      Expectedmodcount = Modcount;
      The catch (Indexoutofboundsexception ex) {throw new concurrentmodificationexception ();
 }
    }
  }

LISTITR implementations are basically consistent with ITR, adding methods that can be traversed earlier and add and set methods.

(3) using the copyonwritearraylist in java.util.concurrent to solve the fast-fail problem

Copyonwritearraylist is thread-safe, specifically look at its Add method source code:

Public boolean Add (E e) {
    final reentrantlock lock = This.lock;
    Lock.lock ();
    try {
      object[] elements = GetArray ();
      int len = elements.length;
      object[] newelements = arrays.copyof (elements, Len + 1);
      Newelements[len] = e;
      SetArray (newelements);
      return true;
    } finally {
      lock.unlock ();
    }
  }

Copyonwritearraylist is the ArrayList that is copied at the time of writing. When you start to write the data, arrays.copyof a new array, which does not affect the read operation.
The cost is the loss of memory, resulting in performance problems. When Copyonwritearraylist writes, a replica object is generated in memory while the original object still exists.
Copyonwritearraylist can not guarantee the real-time consistency of data, only to ensure the consistency of results. Applies to concurrent read and write less scenes, such as caching.

(4) Other methods of ArrayList source code:

A private method Batchremove (Collection<?>c, Boolean complement), a bulk removal operation

 private Boolean batchremove (Collection<?> C, Boolean complement) {//The following refers to the use of fi
    The reason of nal final object[] Elementdata = this.elementdata;
    int r = 0, w = 0;
    Boolean modified = false;
          try {//traverse the elements in the list for validation for (; r < size; r++) if (C.contains (elementdata[r)) = = complement)
    elementdata[w++] = Elementdata[r];
                 Finally {//try If an exception occurs, ensure data consistency perform the following copy operation if (r!= size) {system.arraycopy (Elementdata, R,
        Elementdata, W, size-r);
      W + + size-r; }//Clean up useless elements, notify GC to recycle if (w!= size) {//clear to let-let GC do's work for (int i = w; i < Siz E
        i++) elementdata[i] = null;
        Modcount + = size-w;
        size = W;
      Modified = true;
  } return modified; }

The final modified variable refers to the same reference, in order to keep the data consistent later.
This method, when you want to preserve the elements in collection C, the complement value is true, and when you want to remove the elements in C, the complement value is false. This then becomes the Retainall and RemoveAll method respectively.

Swap: A arraylist of two places in a swap.

Second, LinkedList source analysis (JDK7)

LinkedList is a linked list, relative to the order table, the linked list storage data does not need to use the address contiguous memory unit. Reduces the problem of moving elements that are caused by modifying the container structure, and sequential access is relatively efficient.

1. Definition of node (node)

The LinkedList in the JDK are doubly linked lists, each of which contains information about the last node and the next node. It is defined as follows:

private static class Node<e> {
  E item;
  Node<e> Next;
  Node<e> prev;
  Node<e> (node<e> prev, E element, node<e> next) {
    This.item = element;
    This.next = Next;
    This.prev = prev;
  }

2, LinkedList Construction and initialization

Members: LinkedList maintains 3 member variables to record the number of nodes in the list, the precursors of the nodes, and the successor

transient int size = 0;
Transient node<e>;
Transient node<e> last;

Constructor: The default constructor constructs an empty LinkedList

Public LinkedList () {}

or based on other containers, we'll write ourselves an orderly list of linked lists.

Public LinkedList (COLLECTION<, extends e> c) {this
    ();
    AddAll (c);
}

Here's a little something to add, about generic modifiers? Super T with? The difference between extends T, see this article generics? Super T and? The difference between extends T

3, LinkedList structure operation

Head interpolation: Insert an element in the header of a linked table

private void Linkfirst (E e) {
  final node<e> f = i;
  Final node<e> NewNode = new node<> (null, E, f);
  i = NewNode;
  Determines whether an empty list if
  (f = = null) Last
    = NewNode;
  else
    f.prev = NewNode;
  size++;
  modcount++;
  }

Tail interpolation method: That is, at the end of the linked list inserts an element

  void Linklast (e e) {
    final node<e> l = last;
    Final node<e> NewNode = new node<> (l, E, null);
    last = NewNode;
    if (L = = null),
      NewNode;
    else
      l.next = NewNode;
    size++;
    modcount++;
  }

Before inserting to current node: Find the precursor to the current node

void Linkbefore (e E, node<e> succ) {
    //Determine of course the node is not empty
    final node<e> pred = Succ.prev;
    Final node<e> NewNode = new Node<> (pred, E, succ);
    Succ.prev = NewNode;
    Determines whether the current node is the first node
    if (pred = = null) A/
      NewNode;
    else
      pred.next = NewNode;
    size++;
    modcount++;
  }

Head deletion: Delete the first node of a linked list

  Private E Unlinkfirst (node<e> f) {
    //assert F = = i && f!= null;
    Final E element = F.item;
    Final node<e> next = F.next;
    F.item = null;
    F.next = null; Help GC-I
    = next;
    if (next = null) last
      = null;
    else
      next.prev = null;
    size--;
    modcount++;
    return element;
  }

Tail Deletion method: Delete the last node of the linked list

  Private E Unlinklast (node<e> l) {
    //guaranteed l==last and L!= null
    final E element = L.item;
    Final node<e> prev = L.prev;
    L.item = null;
    L.prev = null; Help GC last
    = prev;
    if (prev = null) is a
      = null;
    else
      prev.next = null;
    size--;
    modcount++;
    return element;
  }

4, keep the list interface and deque consistency

The list interface allows random access to the container using subscripts, which is easy to achieve with random access to the array. For a linked list, the JDK also logically uses the count of nodes in the list to give the realization of random access.

node<e> node (int index) {
    //ensures the correctness of 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;
    }
  }

   

The index belongs to the first half of the count, traversing the lookup from the beginning. Index belongs to the second half of the count, traversing the lookup from the end. Make full use of the characteristics of two-way linked lists.
Therefore, add (int index, T t), get (int), set (int) and so on can be easily implemented.

LinkedList implements the Deque interface, which is the linkedlist implementation of the two-terminal queue container method, the following gives a summary of some APIs.

5, the LinkedList traversal

Since LinkedList is a two-way linked list, it is natural to traverse it backwards and forwards. As with ArrayList, when it comes to multithreaded operations, the LinkedList also appears fail-fast problems.
As for the fail-fast question, the article has already been explained, here does not say.

With regard to iterators, LinkedList has listiterator bidirectional iterators, and descendingiterator reverse iterators. are very simple. Source is not analyzed

If you traverse elements, the cost of random access is relatively large.

Iii. Linkedlist,arraylist, Vector summary

1, LinkedList and ArrayList

ArrayList is to implement the data structure based on dynamic array, linkedlist the data structure based on the linked list.

For random access get and set,arraylist feel better than LinkedList because linkedlist to move the pointer.

Add and Remove,linedlist are more advantageous for new and delete operations because ArrayList want to move the data. This depends on the actual situation. If only the single data inserted or deleted, ArrayList speed is better than LinkedList. But if the batch random inserts deletes the data, the LinkedList speed is much better than ArrayList. Because ArrayList each insertion of data, you move the insertion point and all subsequent data.

2. ArrayList and Vector

Vector is thread-synchronized, so it is also thread-safe, and ArrayList is thread-asynchronous and unsafe. If the thread security factor is not taken into account, the ArrayList efficiency is generally higher.

If the number of elements in the collection is greater than the length of the current collection array, the vector growth rate is 100% of the current array length, while the ArrayList growth rate is 50% of the current array length. If you use data in a set that is larger than the amount of data, vector has some advantages.

If you look for data at a given location, the vector and ArrayList use the same time, both 0 (1), and this time using both vector and ArrayList. And if you move data at a specified location for 0 (n-i) n for the total length, you should consider using linklist at this time, because it takes 0 (1) to move data at a given location, and it takes 0 (i) to query the data at a given location.

ArrayList and vectors store data in an array that is larger than the actual stored data in order to add and insert elements, allowing direct ordinal indexing of elements, but inserting data to design memory operations such as array element movement, so the index data is fast inserting data slowly, Vector because of the use of the Synchronized method (thread safety), so the performance is worse than ArrayList, LinkedList use a two-way linked list to store, indexed data by ordinal need to go forward or backward traversal, but when inserting data only need to log the item before and after, So insert several times faster!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.