I. Overview of the principle of linkedlist implementation
Like LinkedList and ArrayList, the List interface is implemented, but its internal data structure is fundamentally different. LinkedList is based on a linked list (which can be distinguished by name), so its insert and delete operations are more efficient than ArrayList. But also because it is based on the linked list, so the efficiency of random access is worse than ArrayList.
Ii. Definition of LinkedList class
public class linkedlist <e > Extends abstractsequentiallist <e > Span class= "Hljs-keyword" >implements list <E , deque <e ; cloneable , java . io . serializable
- LinkedList is a doubly linked list that inherits from Abstractsequentiallist. It can also be manipulated as a stack, queue, or double-ended queue.
- The LinkedList implements the List interface and can queue operations on it.
- LinkedList implements the Deque interface, which means that linkedlist can be used as a double-ended queue.
- LinkedList implements the Cloneable interface, which covers the function clone () and can be cloned.
- The LinkedList implements the Java.io.Serializable interface, which means that the LinkedList supports serialization and can be transmitted by serialization.
- The LinkedList is non-synchronous.
Why Inherit from Abstractsequentiallist?
Abstractsequentiallist implements the backbone functions of Get (int index), set (int index, e Element), add (int index, e Element), and remove (int index). Reduces the complexity of the list interface. These interfaces are random access list, LinkedList is a doubly linked list, since it inherits from Abstractsequentiallist, it is equivalent to already implemented "get (int index) these interfaces."
In addition, if we need to implement a list by abstractsequentiallist ourselves, we just need to extend this class and provide implementations of the Listiterator () and size () methods. To implement a list that is not modifiable, you need to implement the Hasnext, Next, Hasprevious, Previous, and index methods of the list iterator.
Class diagram relationships for LinkedList:
Three, LinkedList data structure principle
LinkedList the underlying data structure is based on a two-way cyclic linked list, and the head node does not store the information, as follows:
Since it is a doubly linked list, there must be a data structure--we can call it nodes, the node instance holds the business data, the previous node's location information, and the next node location information, as shown in:
Iv. Private Properties
Two attributes are defined in LinkedList:
privatetransientnew Entry<E>(nullnullnull);2privatetransientint0;
The header is the head node of the doubly linked list, which is an instance of the class entry corresponding to the doubly linked list node. Entry contains member variables: Previous, Next, element. Where previous is the node's previous node, next is the node's next node, and element is the value that the node contains.
Size is the number of node instances in a doubly linked list.
First, learn the code for the node class entry class.
privatestaticclass Entry<E> { E element; Entry<E> next; Entry<E> previous; Entry(E element, Entry<E> next, Entry<E> previous) { this.element = element; this.next = next; this.previous = previous; } }
The node class is very simple, element holds the business data, previous and next respectively store the information of the front and back nodes (in the data structure we often call the front and back node pointer).
}
Five, the construction method
The LinkedList provides two construction methods.
publicLinkedList() { header.next = header.previous = header; }publicLinkedList(Collection<? extends E> c) { this(); addAll(c);
The first constructor does not accept parameters, and the header instance's previous and next all point to the header instance (note that this is a two-way loop linked list, if it is not a circular linked list, the case of the empty list should be the header node of the previous node and the latter node are null), So the entire list is actually only the header of a node, used to represent an empty linked list.
After the constructor is executed, the header instance itself forms a closed loop, as shown in:
The second constructor receives a collection parameter, C, calls the first constructed method to construct an empty linked list, and then adds all the elements in C to the linked list by AddAll.
Vi. element Additions
The following illustrates the principle of adding an element add () to a doubly linked list:
//add Element (E) to LinkedList Public Boolean Add(e) {//Before the node (node data is e) is added to the header (header). //That is, add the node to the end of the doubly linked list. Addbefore (E, header); return true; } Public void Add(intIndex, E Element) {Addbefore (element, (Index==size-header:entry (index))); }PrivateEntry<e>Addbefore(e E, entry<e> Entry) {Entry<e> NewEntry =NewEntry<e> (E, Entry, entry.previous); NewEntry.previous.next = NewEntry; newEntry.next.previous = NewEntry; size++; modcount++;returnNewEntry;}
The Addbefore (E e,entry Entry) method is a private method, so it cannot be called in an external program (this is, of course, the general case, which you can do by reflecting the above or can be called).
Addbefore (e e,entry Entry) first creates the node newentry of E through the Entry construction method (which includes setting its next node to Entry, The previous node is set to Entry.previous, which is equivalent to modifying the Newentry "pointer"), after modifying the next reference of the previous node newentry the insertion position and the previous reference of the latter node, so that the referential relationship between the list nodes remains correct. After modifying and resizing the size and record Modcount, then return the newly inserted node.
The steps to "add First Data" are broken down below:
The first step: the case of the LinkedList instance after initialization:
Step Two: Initialize a pre-added entry instance (newEntry).
Entry newEntry = NewEntry (E, Entry, entry.previous);
Step three: Adjust the front and back pointers of the new join node and Head node (header).
NewEntry.previous.next = NewEntry;
Newentry.previous is the header,newentry.previous.next that is the header and next points to the NewEntry instance. In the middle should be "line Line 4" pointing to Newentry.
newEntry.next.previous = NewEntry;
The Newentry.next is header,newentry.next.previous, which is the header previous points to the NewEntry instance. In the middle should be "line Line 3" pointing to Newentry.
As shown in the following adjustments:
Figure--LinkedList after joining the first node
The steps to "add a second data" are broken down below:
First step: Create a new node.
Figure--Adding a second node
Step two: Adjust the front and back pointer information of the new node and the head node.
Figure--Adjusting the pointer information before and after
Adding subsequent data is consistent with the above, and there is no capacity limit for linkedlist instances.
In summary, the Addbefore (e e,entry Entry) implementation inserts a new node constructed by E before the Entry. The Add (e-E) Implementation inserts a new node constructed by E before the header node. For ease of understanding, the insertion node is given below.
publicvoidaddFirst(E e) { publicvoidaddLast(E e) { addBefore(e, header); }
Looking at the above, combined with the Addbefore (e e,entry Entry) method, it is easy to understand that addfrist (e) Simply implements the insertion before the next element of the header element, that is, before a number in. AddLast (e) Just before the header node is implemented (because it is a circular list, the previous node of the header is the last node of the linked list) and the node is inserted (after being inserted at node 2nd).
Vii. Delete data Remove ()
PublicERemove(intIndex) {Entry E =Get(index); Remove (e);returnE.element; }Private void Remove(e) {if(E = = header)Throw NewNosuchelementexception ();//assigns the next reference of the previous node to the next node of eE.previous.next = E.next;//assigns the previous of the next node of E to the previous node of Ee.next.previous = e.previous;the execution of the above two statements has resulted in the inability to access the E node in the linked list, and the following removes the reference to the front and back nodes of the e nodeE.next = E.previous =NULL;//Set the contents of the removed node to nullE.element =NULL;//Modify Sizesize--; }
As a result of deleting a node, adjust the front and back pointer information of the corresponding node as follows:
E.previous.next = e.next;//The back pointer of the previous node of the pre-deleted node points to the next node of the pre-deleted node.
E.next.previous = e.previous;//The front pointer of the next node of the pre-deleted node points to the previous node of the pre-deleted node.
To empty a pre-deleted node:
E.next = e.previous = null;
E.element = null;
The GC completes the resource collection and the delete operation ends.
In contrast to ArrayList, LinkedList's delete action does not need to "move" a lot of data, making it more efficient.
Viii. Data acquisition Get ()
The implementation of the Get (int) method has already been involved in remove (int). First, determine if the location information is legal (greater than or equal to 0, less than the size of the current LinkedList instance), and then traverse to the specific location to obtain the node's business data (element) and return.
Note: To improve efficiency, you need to determine whether you want to iterate from the beginning or from the end, depending on the location obtained.
//Gets the node at the specified position in the doubly linked list PrivateEntry<e> Entry (int Index) {if(Index<0||Index>= size)Throw NewIndexoutofboundsexception ("Index:"+Index+", Size:"+size); entry<e> E = header;//Gets the node at index. //If the index < two-way list length of 1/2, then the previous search; //Otherwise, look forward from behind. if(Index< (size >>1)) { for(inti =0; I <=Index; i++) e = E.next; }Else{ for(inti = size; i >Index; i--) e = e.previous; }returnE }
Attention to detail: the difference between bitwise arithmetic and direct division. Compare index to half of the length size first, if index
Ix. Clear Data ()
Public voidClear () {Entry<E>E= Header.Next//E can be understood as a moving "pointer" because it is a circular linked list, so when you go back to the header, it means there are no nodes. while(E!= Header) {//Keep references to the next node of eEntry<E>Next=E.Next//Unbind node E from reference to the front and back nodesE.Next=E.Previous= NULL;//Empty the contents of node EE.Element= NULL;//Move E to the next nodeE=Next }//The header is constructed as a circular linked list, and the same construction method constructs an empty LinkedList Header.Next= Header.Previous= Header;//Modify SizeSize= 0; Modcount++; }
X. Data contains contains (Object o)
Public BooleanContains (Object o) {returnIndexOf (o)! =-1; }///Backward lookup, returns the index of the node corresponding to the value object (o) does not exist, returns 1 Public intIndexOf (Object o) {int Index=0;if(o==NULL) { for(Entry e = Header.next; E! = header; e = e.next) {if(e.element==NULL)return Index;Index++; } }Else{ for(Entry e = Header.next; E! = header; e = e.next) {if(O.equals (e.element))return Index;Index++; } }return-1; }
IndexOf (Object o) determines whether the element and O of the node exist in the O list, and if so, returns the index position of the node in the linked list, or 1 if it does not exist.
The contains (object O) method determines whether the linked list contains object o by judging whether the value returned by the IndexOf (object O) method is.
XI. data replication clone () vs. ToArray ()
Clone ()
PublicObjectClone() {linkedlist<e>Clone=NULL;Try{Clone= (linkedlist<e>) Super.Clone(); }Catch(Clonenotsupportedexception e) {Throw NewInternalerror (); }Clone. Header =NewEntry<e> (NULL,NULL,NULL);Clone. Header.next =Clone. header.previous =Clone. Header;Clone. Size =0;Clone. Modcount =0; for(entry<e> e = Header.next; E! = header; e = e.next)Clone. Add (E.element);return Clone; }
Call the parent class's clone () method to initialize the object list clone, construct clone as an empty two-way loop linked list, and then add the next node of the header to clone as a node. Finally, the cloned clone object is returned.
ToArray ()
publicObject[] toArray() { ObjectnewObject[size]; int0; for (Entry<E> e = header.next; e != header; e = e.next) result[i++] = e.element; return result; }
Creates an array of equal size and linkedlist result, iterates through the list, copies element elements of each node into the array, and returns an array.
ToArray (t[] a)
Public <T> t[] ToArray (t[]a) {if(a. length < size)a= (t[]) java.lang.reflect.Array.newInstance (a. GetClass (). Getcomponenttype (), size);int i =0;Object[] result =a;for (entry<e> E = Header.next; E! = header; e = e.next)result[i++] = e.element; if(a. length > Size)a[Size] = NULL; return a;}
Determine whether the size of the array A is sufficient, if the size is not enough to expand. The method of launching is used here, and an array of size is re-instantiated. The array A is then assigned to the array result, which iterates through the elements added to the result of the list. Finally, determine if the length of array A is greater than size, and if it is greater, set the content of the size position to NULL. Returns a.
As can be seen from the code, when the length of array A is less than or equal to size, all the elements in a are overwritten, the contents of the expanded space are null, and if the length of array A is greater than size, the contents of the 0 to size-1 position are overwritten. The element of the size position is set to Null,size after the element is not changed.
Why not operate directly on the array A, to assign a to the result array and then manipulate it?
12. Traverse data: Iterator ()
LinkedList's iterator
In addition to entry,linkedlist there is an inner class: Listitr.
LISTITR implements the Listiterator interface, it is known as an iterator through which you can iterate through the modification of the LinkedList.
A method for getting Listitr objects is provided in LinkedList: listiterator (int index).
publiclistIterator(int index) { returnnew ListItr(index); }
The method simply returns a Listitr object.
LinkedList also has the Listiterator () method obtained through integration, which simply calls the Listiterator (int index) and passes in 0.
13.
LinkedList Learning Summary In "data structure" Java