跪拜 Guibai
← All articles
Backend · AI Programming · Java

A Swing GUI That Shows Exactly How LinkedList Rewires Nodes on add() and remove()

By 金銀銅鐵 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The demo turns an invisible data structure into something watched rather than inferred, so the exact cost of node(i)'s O(n) walk and the meaning of next and prev become observable in a debugger-like interface. Because it reflects into JDK internals, it also demonstrates the --add-opens workaround that Java 9+ module encapsulation requires.

Summary

LinkedListBehaviorDemo visualizes java.util.LinkedList's private fields while add(E), addFirst(E), add(i, E), removeFirst(), removeLast(), and remove(i) run. Reflection reads first, last, size, item, next, and prev after each call, then the Swing canvas redraws two views: a logical chain with navy next arrows, magenta prev arrows, and dashed null terminators, and a heap scatter that places each Node at a pseudo-position derived from its identity hash. The node touched by the last mutation is outlined in orange.

The tool maps buttons directly to Java method calls and logs each operation's effect. add(E) links after last, addFirst(E) links before first, and add(i, E) walks node(i) before re-linking two neighboring nodes. removeFirst() and removeLast() unlink an end, while remove(i) uses the same node(i) walk. A status panel shows size, first, and last after every call.

Java's LinkedList itself declares only four fields: transient int size, transient Node<E> first, transient Node<E> last, and static serialVersionUID. Each Node is an independent heap object, so list order exists only in references. The scatter view makes that concrete: nodes adjacent in the list land at unrelated pseudo-addresses. A linked reflection snippet lists LinkedList's declared fields, and a JDK 21 PlantUML class diagram shows LinkedList extending AbstractSequentialList and implementing List and Deque.

Takeaways
— LinkedList declares only four fields: transient int size, transient Node<E> first, transient Node<E> last, and static final long serialVersionUID.
— Each LinkedList node is an independent heap object; list order exists only in next and prev references, not in a contiguous array.
— add(E) and addLast(E) link the new node after last; addFirst(E) links it before first.
— add(i, E) calls node(i) to walk to the target, then re-links two adjacent nodes; when index equals size, it skips the walk and appends at the tail.
— node(i) walks forward from first via next when i < size/2, and backward from last via prev otherwise, making indexed access O(n) and giving ArrayList the random-access advantage.
— removeFirst() and removeLast() unlink one end; remove(i) finds the node with node(i) and unlinks it.
— The demo needs JDK 16+ because it uses record syntax, and running it requires --add-opens=java.base/java.util=ALL-UNNAMED to read LinkedList's private fields via reflection.
— The heap scatter view assigns each node a slot from its identity hash with linear probing, so adjacent list nodes appear at unrelated pseudo-positions.
Conclusions

Visualizing next and prev after every mutation turns LinkedList's O(n) indexing from an abstract complexity claim into a visible walk; the node(i) annotations in the log are more instructive than a benchmark.

The heap scatter view makes the memory story part of the picture: scattered identity-hash positions show why LinkedList's links are cache-unfriendly in a way ordinary chain diagrams cannot.

Putting each GUI button next to the exact Java call it makes collapses the distance between API and implementation, which works better for teaching collections than prose alone.

Reflection into java.util is version-sensitive and needs --add-opens on JDK 9+; the demo treats that as a first-class setup requirement rather than hiding it, which matches what real tooling faces.

Concepts & terms
identity hash
A hash value computed by System.identityHashCode from an object's identity rather than its equals/hashCode logic; the demo uses it as a stable pseudo-position for drawing nodes in the heap scatter view.
node(i) bidirectional search
LinkedList's internal method for finding the node at index i. It starts from first and follows next when i < size/2, otherwise starts from last and follows prev, so reaching an index costs O(n).
--add-opens JVM option
A JVM flag that opens a JDK module package to reflection at runtime; needed here because java.util's private fields are encapsulated by the module system since JDK 9.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗