Graphviz draw linked list
Writing purpose
Originally, I wanted to be lazy. For the drawing of linked list, I wanted to draw it on A4 paper, then take a photo as electronic data, and then store it. However, considering my font "catch chicken", I didn't want to save it in the photo album or upload the photo to a hardware medium for storage, It is also impossible to save the A4 value as a material (considering that the paper data cannot be carried with you, and it is impossible to bring all the paper data with notes with you), so through the study of this article, we can draw the basic linked list, and then integrate the pictures drawn by the software into the documents supporting markdown syntax, In this way, a more beautiful electronic data has been completed.
Development environment preparation
-
Visual Studio Code
-
install Graphviz , because my system is windows, I can choose the installation of Windows version. After installation, you need to add dot.com under the path of the environment variable of the system The path to the EXE executable.
-
Then install the corresponding support libraries and languages on Visual Studio Code
-
Node visualization preparation
-
Ctrl+Shift+P, find Graphviz:Open Preview to the Side in the open window, and click to open the window, as shown below
-
Code example analysis
Background introduction
Originally, I came across MessageQueue when learning the message mechanism of Android. I always wanted to know the specific implementation of "how messages enter the queue", so I checked its internal principle, mainly the enqueueMessage function in MessageQueue. After analysis, I found that a linked list is maintained inside, Compare the when value of the new message to be added to the linked list with the when value of other messages in the linked list, and then add it to the linked list according to the rules. The specific code is as follows:
1 boolean enqueueMessage(Message msg, long when) { 2 if (msg.target == null) { 3 throw new IllegalArgumentException("Message must have a target."); 4 } 5 if (msg.isInUse()) { 6 throw new IllegalStateException(msg + " This message is already in use."); 7 } 8 9 synchronized (this) { 10 if (mQuitting) { 11 IllegalStateException e = new IllegalStateException( 12 msg.target + " sending message to a Handler on a dead thread"); 13 Log.w(TAG, e.getMessage(), e); 14 msg.recycle(); 15 return false; 16 } 17 18 msg.markInUse(); 19 msg.when = when; 20 Message p = mMessages; 21 boolean needWake; 22 if (p == null || when == 0 || when < p.when) { 23 // New head, wake up the event queue if blocked. 24 msg.next = p; 25 mMessages = msg; 26 needWake = mBlocked; 27 } else { 28 // Inserted within the middle of the queue. Usually we don't have to wake 29 // up the event queue unless there is a barrier at the head of the queue 30 // and the message is the earliest asynchronous message in the queue. 31 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 32 Message prev; 33 for (;;) { 34 prev = p; 35 p = p.next; 36 if (p == null || when < p.when) { 37 break; 38 } 39 if (needWake && p.isAsynchronous()) { 40 needWake = false; 41 } 42 } 43 msg.next = p; // invariant: p == prev.next 44 prev.next = msg; 45 } 46 47 // We can assume mPtr != 0 because mQuitting is false. 48 if (needWake) { 49 nativeWake(mPtr); 50 } 51 } 52 return true; 53}
Among them, I focus on how messages enter the queue. Therefore, when looking at the code, I can ignore some status flags and exception checks, such as if (msg.target == null), if (msg.isInUse()), mqitting and MSG Markinuse (), needWake, etc. just focus on line19 to line45.
-
Note: mMessages is a member variable of type Message.
-
For Message class, we only need to pay attention to the following contents
public final class Message implements Parcelable{ long when; Message next; // Other member variables and methods are omitted below }
Draw it with Graphviz as follows:
digraph { label = "\n msg"; rankdir = LR; // Layout from left to right node [shape = record]; //< S10 "{> next"} list [label = "<s0> when | <s1> next"] //Arrangement order of linked list elements: vertical listnode0 [label = "<s0>null"]; // Declare an empty node list:s1 -> listnode0 // Specifies the point of the node }
Next, let's look at the first part of enqueueMessage, that is, line22 to line27, which can be considered in three cases
- p == null
- when == 0
- when < p.when
Consider three special cases first
Note: the following pink nodes represent our newly added nodes msg
p==null
The following is the execution of codes line20 to line26 when p==null.
-
At first, the nodes of mMessages are as follows:
-
After executing line24 in enqueueMessage, the situation is as follows:
-
After executing code line25, the situation is as follows:
when == 0 || when < p.when
The premise of this situation is that the condition of p == null is not tenable, so there is at least one node in mMessages. At the same time, both cases add the node msg to be inserted to the head of the linked list. Therefore, the analysis of new node insertion is divided into one node and multiple nodes 2 as follows:
-
The initial situation is as follows, which is divided into single node and multiple nodes:
-
After executing line24 in enqueueMessage, the situation is as follows:
-
After executing code line25, the situation is as follows:
Consider enqueueMessageline32 to line44
This situation refers to the situation that there is at least one node in mMessages and the node msg to be added is not added to the head of the linked list.
According to the analysis of "when = = 0 | when < p.when", we know that the insertion logic of single node and multi node is actually the same, so we use multi node method to analyze the code below.
In the for loop, line36 to line38 are the termination conditions of the for loop, indicating that the node msg to be added has been compared with the when variables of other nodes in the linked list. If the when value of MSG node is smaller than that of a node in the linked list, and a node has not been found in the linked list until the end of the linked list (its when value is larger or equal to that of MSG node), the loop will be ended.
The first execution of the for loop is used to draw the node diagram, and other execution can be analogized:
-
prev = p
-
p = p.next
At this time, assuming that the value of when of the node msg to be added meets the loop termination conditions of line36 to line38 in the for loop, we will jump out of the loop and execute line43 and line44. The node diagram is shown below
-
msg.next = p
-
prev.next = msg