Graphic set 2: Explain list, graphic set explain list
First-recognized shortlist
The previous article explains ArrayList. This article describes how to implement the rule list.
The tranquility list is based onLinked ListSo let's first explain what a linked list is. The linked list is originally a concept of C/C ++ and is a linear storage structure, meaning that the data to be stored is stored in a storage unit, in addition to data to be stored, this storage unit also stores the address of its next storage unit (the address of the next storage unit is necessary, some storage structures also store the address of the previous storage unit). Each time you search for data, you can find the storage unit that follows the address of the next storage unit in a storage unit.
In this case, it may be a bit abstract. First, the sort list isTwo-way linked listTwo-way linked list I think has two meanings:
1. Any storage unit in the linked list can be obtained through forward or backward addressing.
2. The next node of the End Node of the linked list is the head node of the linked list, and the first node of the head node of the linked list is the end node of the linked list.
A linked list is a two-way linked list. It must have a storage unit. Let's take a look at the basic storage unit of the linked list. It is an internal class in the linked list:
private static class Entry<E> {E element;Entry<E> next;Entry<E> previous;...}
We can see that the "E element" in the Entry of the shortlist is the data it actually stores. "Entry <E> next" and "Entry <E> previous" indicate the reference address of the previous storage unit and the reference address of the next storage unit. Shown in the figure below:
The answer to the four concerns on the explain list
Close note |
Conclusion |
Allow blank Referer list |
Allow |
Whether repeated data is allowed in the shortlist |
Allow |
Sorted list |
Ordered |
Thread-safe writable list |
Non-thread security |
Add Element
First, let's take a look at how to add an element to the listing list. If I have a piece of code:
1 public static void main(String[] args)2 {3 List<String> list = new LinkedList<String>();4 list.add("111");5 list.add("222");6 }
Perform a line-by-line analysis on how the three lines of code in the main function are executed. The first line is 3rd. Let's take a look at the source code of the listing list:
1 public class LinkedList<E> 2 extends AbstractSequentialList<E> 3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable 4 { 5 private transient Entry<E> header = new Entry<E>(null, null, null); 6 private transient int size = 0; 7 8 /** 9 * Constructs an empty list.10 */11 public LinkedList() {12 header.next = header.previous = header;13 }14 ...15 }
We can see that a new Entry is named header. The previous, element, and next values in the Entry are both null. When executing the constructor, set the values of previous and next to the reference address of the header, or use the drawing method. The length of a 32-bit JDK is 4 bytes. Currently, a 64-bit JDK uses a 4-byte length, which is measured in 4 characters. The header reference address is 4 bytes long. If it is 0x00000000, it can be expressed after "List <String> list = new referlist <String> ()" is executed:
Next, let's take a look at what the "4th" character string has done:
1 public boolean add(E e) {2 addBefore(e, header);3 return true;4 }
1 private Entry<E> addBefore(E e, Entry<E> entry) {2 Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);3 newEntry.previous.next = newEntry;4 newEntry.next.previous = newEntry;5 size++;6 modCount++;7 return newEntry;8 }
Line 3 has a new Entry, which may not be easy to understand. According to the Entry constructor, I have translated this sentence and may be easy to understand:
1. newEntry. element = e;
2. newEntry. next = header. next;
3. newEntry. previous = header. previous;
We can see in header. next and header. previous that both are 0x00000000. If the address of the new Entry is 0x00000001, continue to draw the picture:
There are five steps in total. The operation steps in each step are represented by numbers:
1. The element value of the new entry is 111;
2. the next of the new entry is the next of the header, and the next of the header is 0x00000000, so the next of the new entry is 0x00000000;
3. The previous of the new entry is the previous of the header, and the previous of the header is 0x00000000. Therefore, the next of the new entry is 0x00000000;
4. "newEntry. previous. next = newEntry ", the first is newEntry's previous, because newEntry's previous is 0x00000000, so newEntry. previous indicates the header. The next of the header is newEntry, that is, the next of the header is 0x00000001;
5. "newEntry. next. previous = newEntry", set the header previous to 0x00000001, just like 4;
Why? Do you still remember the two features of a two-way linked list? One is that any node can be forward and backward addressable, and the other is that the previous of the entire linked list header represents the end Entry of the linked list, next at the end of the linked list indicates the head Entry of the linked list. Now the linked list header is the Entry 0x00000000, and the end of the linked list is 0x00000001. You can view the graph and check whether the two conditions are met.
Finally, let's take a look at what the added string "222" has done. Assume that the address of the new Entry is 0x00000002, and the image is shown as follows:
Or the five steps to be executed, each step in the figure is marked out, as long as you want to know which node previous and next each represent, there will be no problems.
So far, adding a string "111" and a string "222" to a listing is complete. It is easier to understand the two-way linked list from this figure:
1. For the Entry in the middle, the value of previous is 0x00000000, that is, header; the value of next is 0x00000002, that is, tail. This is an Entry that can be searched forward, you can also search for the Entry
2. The previous value of the header Entry is 0x00000002, that is, tail. In the two-way linked list, the previous value of the header Entry points to the end Entry.
3. The next value of the end Entry is 0x00000000, that is, the header. This means that the next of the end Entry in the two-way linked list points to the header Entry.
View Elements
Let's take a look at how the code for listing is written:
public E get(int index) { return entry(index).element;}
1 private Entry<E> entry(int index) { 2 if (index < 0 || index >= size) 3 throw new IndexOutOfBoundsException("Index: "+index+ 4 ", Size: "+size); 5 Entry<E> e = header; 6 if (index < (size >> 1)) { 7 for (int i = 0; i <= index; i++) 8 e = e.next; 9 } else {10 for (int i = size; i > index; i--)11 e = e.previous;12 }13 return e;14 }
This Code demonstrates the benefits of a two-way linked list. The two-way linked list adds a little space consumption (each Entry also needs to maintain its pre-Entry reference). It also increases programming complexity, but greatly improves efficiency.
Because the linked list is a two-way linked list, the linked list can be searched either forward or backward, with 6th rows ~ The role of row 12th is:When the index is less than half the size of the array (size> 1 indicates size/2, and the code running efficiency is improved using the shift operation), search backward; otherwise, search forward.
In this way, there are 10000 elements in my data structure, and when it happens to be 10,000th elements, you don't need to traverse 10000 times from the beginning, just traverse backwards, the elements I want can be found at one time.
Delete Element
After adding an element, let's see how to delete an element. Like ArrayList, List objects can be deleted by element or by subscript. The former deletes the first element matching from the beginning. The following example shows how to delete a file by Subscript:
1 public static void main(String[] args)2 {3 List<String> list = new LinkedList<String>();4 list.add("111");5 list.add("222");6 list.remove(0);7 }
That is, I want to delete the "111" element. Let's take a look at how row 6th is executed:
1 public E remove(int index) {2 return remove(entry(index));3 }
1 private E remove(Entry<E> e) { 2 if (e == header) 3 throw new NoSuchElementException(); 4 5 E result = e.element; 6 e.previous.next = e.next; 7 e.next.previous = e.previous; 8 e.next = e.previous = null; 9 e.element = null;10 size--;11 modCount++;12 return result;13 }
Of course, the first step is to find the element, which is the same as get. Next, it is relatively simple to describe using the drawing method:
It is relatively simple. You only need to find the reference address, and the operations in each step are also detailed on the figure.
Here, I will mention that step 1, step 2, and step 2 set the previous, element, and next values of the Entry to be deleted to null,The role of these three steps is to allow the virtual machine to recycle the Entry.
However, I have extended this question a little: According to the garbage collection detection algorithm adopted by Java Virtual Machine HotSpot-root node search algorithm, this Entry can be recycled even if the values of previous, element, and next are not set to null, because at this time this Entry will no longer point to it, tail's previous and header's next have all been changed, so this Entry will be treated as "junk. The reason for setting previous, element, and next to null is probably to be compatible with another garbage collection detection algorithm-reference counting method, which is a garbage collection detection algorithm, as long as there are mutual references between objects, this memory will not be treated as "junk.
Delete Element
The deletion of elements will not be detailed. Let's take a look at the source code of the elements to be deleted:
public void add(int index, E element) { addBefore(element, (index==size ? header : entry(index)));}
private Entry<E> addBefore(E e, Entry<E> entry) {Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);newEntry.previous.next = newEntry;newEntry.next.previous = newEntry;size++;modCount++;return newEntry;}
If my friends understand the previous content, I think these two methods should be easy for you to understand.
Comparison between the ArrayList and ArrayList
I have been talking about this question for a long time. Here I try to clarify this question with my own understanding. By the way, I will also explain the advantages and disadvantages of listing.
1. Sequential insert speed: ArrayList is faster, because ArrayList is implemented based on arrays, And the array is new in advance. You only need to insert a data to the specified position. The sorted list is different, each time a sequence is inserted, the sequence list will display a new object. If the object is relatively large, the new time will inevitably be longer. In addition, some reference assignment operations will be added, so the sequence list insertion will inevitably be slower than the ArrayList.
2. Based on the above point, because the writable list not only maintains the elements to be inserted, but also maintains the Entry's pre-Entry and post-Entry. If there are many entries in a writable list, in this case, the tranquility list consumes more memory than the ArrayList.
3. For the speed of data traversal, see the last part. I will not elaborate on it here. The conclusion is:The ArrayList traversal efficiency is higher than that of the sorted list
4. Some statements that the insert and delete operations on the shortlist are faster are inaccurate:
(1) When the consumer list is inserted or deleted, it is slow in addressing, and you only need to change the reference address of the front and back entries.
(2) When ArrayList is inserted or deleted, it is slow in the batch copy of array elements and fast in addressing.
Therefore, if the elements to be inserted or deleted are in the first half of the data structure, especially in the front, the efficiency of the sorted list will be much faster than that of the ArrayList, because ArrayList will copy a large number of elements in batches, the more backward, for the sorted list, because it is a two-way linked list, therefore, inserting a data entry after 2nd elements is basically the same as inserting an element after the last 2nd elements. However, ArrayList has fewer and fewer elements to copy in batches, the operation speed will inevitably catch up with or even exceed the limit list.
From this analysis, we can see that if you are very sure that the elements you insert and delete are in the first half of the segment, you can use the sorted list; if you are sure that the deleted or deleted elements are at the back of the list, you can use ArrayList. If you are not sure where you want to insert or delete it? We recommend that you use the sort list, because the execution efficiency of the overall insert and delete operations of the sort list is relatively stable, and there is no ArrayList, which is faster and later. Second, when inserting elements, if the ArrayList cannot be created, you need to resize it once. Remember,ArrayList underlying array resizing is an operation that consumes both time and space.In my article on Java code optimization, I will give a detailed explanation of the 9th points.
Finally, everything is on paper. After selecting List, it is best to perform some performance tests with the conditions, for example, record the time consumption of List operations in your code context..
Iteration of the tranquility list and ArrayList
In my article on Java code optimization, 19th points specifically mentioned that ArrayList uses the most common for loop traversal, while the forward List uses the foreach loop faster. Let's take a look at the definitions of the two lists:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
Note that ArrayList implements the RandomAccess interface, while the RandomAccess interface is not implemented. For the role of the RandomAccess interface, let's take a look at the jdk api statement:
For this reason, I write a piece of code to prove this. Note that although the Iterator in the above example is used, the compiler will use the Iterator of this set by default during the foreach loop, for more information, see the foreach loop principle. The test code is as follows:
Public class TestMain {private static int SIZE = 111111; private static void loopList (List <Integer> list) {long startTime = System. currentTimeMillis (); for (int I = 0; I <list. size (); I ++) {list. get (I);} System. out. println (list. getClass (). getSimpleName () + "use normal for loop Traversal Time is" + (System. currentTimeMillis ()-startTime) + "ms"); startTime = System. currentTimeMillis (); for (Integer I: list) {} System. out. println (list. getClass (). getSimpleName () + "use foreach loop Traversal Time To" + (System. currentTimeMillis ()-startTime) + "ms");} public static void main (String [] args) {List <Integer> arrayList = new ArrayList <Integer> (SIZE ); list <Integer> struct List = new struct List <Integer> (); for (int I = 0; I <SIZE; I ++) {arrayList. add (I); Lists List. add (I);} loopList (arrayList); loopList (partition list); System. out. println ();}}
I intercepted three running results:
ArrayList uses a general for loop Traversal Time of 6msArrayList use foreach loop Traversal Time of 12ms0000list use a general for loop Traversal Time of 38482ms0000list use foreach loop Traversal Time of 11 ms
ArrayList uses a common for loop Traversal Time of 5msArrayList use foreach loop Traversal Time of 12ms1_list use a common for loop Traversal Time of 43287ms1_list use foreach loop Traversal Time of 9 ms
ArrayList uses a general for loop Traversal Time of 4msArrayList use foreach loop Traversal Time of 12ms1_list use a general for loop Traversal Time of 22370ms1_list use foreach loop Traversal Time of 5 ms
With the explanation of jdk api, this result is not surprising. The most important thing to come up with is: if you use a common for loop to traverse the sorted list, the traversal speed will be very slow.