Detailed explanation of the underlying implementation and principle of LinkedList data structure

Detailed explanation of the underlying implementation and principle of LinkedList data structure

[[420549]]

Preface

In daily development, collections are a data structure that we often use. Of course, there is not only one kind of collection, and there is no so-called best collection, only the most suitable one;

Of course, as senior programmers, we must not only know how to use it, but also understand the principles behind it;

Today we will talk about the underlying implementation and principle of LinkedList

1. Introduction to LinkedList

  1. public class LinkedList<E>
  2. extends AbstractSequentialList<E>
  3. implements List<E>, Deque<E>, Cloneable, java.io. Serializable  
  4. {
  5. //length
  6. transient int   size = 0;
  7. //Point to the head node
  8. transient Node<E> first ;
  9. //Point to the end node
  10. transient Node<E> last ;
  11. }

1. Overview of LinkedList

LinkedList implements both List interface and Deque counterparts, which means it can be regarded as a sequential container, a queue, and a stack.

In this way, linkedList is simply invincible. When you need to use a stack or a queue, you can consider using LinkedList. On the one hand, Java officials have stated that they do not recommend the use of the Stack class. What's more regrettable is that there is no class called Queue in Java (it's just the name of an interface).

Regarding stacks or queues, ArrayDeque is now the first choice, which has better performance than LinkedList (when used as a stack or queue);

The implementation of LinkedList determines that all operations related to the following table are linear time, and deleting elements at the beginning or end only takes constant time. In pursuit of efficiency, LinkedList is not synchronized. If multiple threads need to access it concurrently, you can first use Collections.synchronizedList() method to wrap it.

2. Data Structure

Inheritance

  1. java.lang.Object
  2. java.util.AbstractCollection<E>
  3. java.util.AbstractList<E>
  4. java.util.AbstractSequentialList<E>
  5. java.util.LinkedList<E>

Implementing the interface

  1. Serializable , Cloneable, Iterable<E>, Collection<E>, Deque<E>, List<E>, Queue<E>

Basic Properties

  • transient int size = 0; //The number of elements stored in LinkedList
  • transient Node first; //head node
  • transient Node last; //tail node
  • Collection interface: The Collection interface is the root node of all collection classes. Collection represents a rule, and all classes that implement the Collection interface follow this rule.

Inherited classes and implemented interfaces

  • List interface: List is a sub-interface of Collection. It is a collection with ordered elements (maintaining the order of elements in the order of insertion), repeatable, and nullable
  • AbstractCollection class: The skeleton implementation class of the Collection interface, which minimizes the workload required to implement the Collection interface
  • AbstractList class: a skeleton implementation class of the List interface, minimizing the workload required to implement the List interface
  • Cloneable interface: Classes that implement this interface can explicitly call the Object.clone() method to legally copy fields of the class instance. If the Object.clone() method is called on an instance that does not implement the Cloneable interface, a CloneNotSupportException will be thrown. Normally, classes that implement the Cloneable interface will override Object.clone() with a public method.
  • Serializable interface: Implementing this interface indicates that the class can be serialized and deserialized. Detailed explanation of query serialization
  • Deque interface: Deque defines a linear Collection that supports inserting and deleting elements at both ends. Deque is actually the abbreviation of "double ended queue". Most implementations of the Deque interface do not limit the number of elements, but this queue supports both implementations with capacity restrictions and implementations without capacity restrictions. For example, LinkedList is an implementation with capacity restrictions, and its maximum capacity is Integer.MAX_VALUE
  • AbstractSequentialList class: Provides a skeleton implementation of the List interface, minimizing the work required to implement this interface supported by "continuous access" data storage (such as linked lists). For random access data (such as arrays), AbstractList should be used in preference to the AbstractSequentailList class.

2. Analysis of underlying source code

LinkedList is implemented by a doubly linked list. Since it is a linked list, its random access efficiency is lower than that of ArrayList, and its sequential access efficiency is relatively high. Each node has a predecessor (a pointer to the previous node) and a successor (a pointer to the next node).

The effect is as follows:

The source code is marked as follows:

  1. public class LinkedList<E>extends AbstractSequentialList<E>
  2. implements List<E>, Deque<E>, Cloneable, java.io. Serializable  
  3. {
  4. transient int   size = 0; //The number of elements stored in LinkedList
  5. transient Node<E> first ; //head node
  6. transient Node<E> last ; // tail node
  7. //Constructor, create an empty list
  8. public LinkedList() {
  9. }
  10. //Add a specified collection to LinkedList, complete initialization first, then call the add operation
  11. public LinkedList(Collection<? extends E> c) {
  12. this();
  13. addAll(c);
  14. }
  15. //Insert the head node
  16. private void linkFirst(E e) {
  17. final Node<E> f = first ; //Assign the head node to the f node
  18. //new a new node, this node's data = e, pre = null , next - > f
  19. final Node<E> newNode = new Node<>( null , e, f);
  20. first = newNode; //Copy the newly created node address to first  
  21. if (f == null ) //f == null , indicating that the LinkedList is empty at this time
  22. last = newNode; //Assign the newly created node to last  
  23. else  
  24. f.prev = newNode; //Otherwise f.prev points to newNode
  25. size ++;
  26. modCount++;
  27. }
  28. //Insert the tail node
  29. void linkLast(E e) {
  30. final Node<E> l = last ;
  31. final Node<E> newNode = new Node<>(l, e, null );
  32. last = newNode;
  33. if (l == null )
  34. first = newNode;
  35. else  
  36. l. next = newNode;
  37. size ++;
  38. modCount++;
  39. }
  40. //Insert the e node before the succ node and modify the predecessor and successor between each node
  41. void linkBefore(E e, Node<E> succ) {
  42. // assert succ != null ;
  43. final Node<E> pred = succ.prev;
  44. final Node<E> newNode = new Node<>(pred, e, succ);
  45. succ.prev = newNode;
  46. if (pred == null )
  47. first = newNode;
  48. else  
  49. pred.next = newNode;
  50. size ++;
  51. modCount++;
  52. }
  53. //Delete the head node
  54. private E unlinkFirst(Node<E> f) {
  55. // assert f == first && f != null ;
  56. final E element = f.item;
  57. final Node<E> next = f. next ;
  58. f.item = null ;
  59. f. next = null ; // help GC
  60. first = next ;
  61. if ( next == null )
  62. last = null ;
  63. else  
  64. next .prev = null ;
  65. size --;  
  66. modCount++;
  67. return element;
  68. }
  69. //Delete the tail node
  70. private E unlinkLast(Node<E> l) {
  71. // assert l == last && l != null ;
  72. final E element = l.item;
  73. final Node<E> prev = l.prev;
  74. l.item = null ;
  75. l.prev = null ; // help GC
  76. last = prev;
  77. if (prev == null )
  78. first = null ;
  79. else  
  80. prev.next = null ;
  81. size --;  
  82. modCount++;
  83. return element;
  84. }
  85. //Delete the specified node
  86. E unlink(Node<E> x) {
  87. // assert x != null ;
  88. final E element = x.item;
  89. final Node<E> next = x.next ; //Get the predecessor of the specified node
  90. final Node<E> prev = x.prev; //Get the successor of the specified node
  91. if (prev == null ) {
  92. first = next ; //If the predecessor is null , it means this node is the head node
  93. } else {
  94. prev.next = next ; //The successor node of the predecessor node points to the successor node of the current node
  95. x.prev = null ; //The predecessor of the current node is empty
  96. }
  97. if ( next == null ) { //If the successor node of the current node is null , it means that this node is the tail node
  98. last = prev;
  99. } else {
  100. next .prev = prev; //The predecessor of the successor node of the current node points to the predecessor node of the current node
  101. x.next = null ; //The successor of the current node is empty
  102. }
  103. x.item = null ; //The element of the current node is set to null , waiting for garbage collection
  104. size --;  
  105. modCount++;
  106. return element;
  107. }
  108. //Get the first node information in LinkedList
  109. public E getFirst() {
  110. final Node<E> f = first ;
  111. if (f == null )
  112. throw new NoSuchElementException();
  113. return f.item;
  114. }
  115. //Get the last node information in LinkedList
  116. public E getLast() {
  117. final Node<E> l = last ;
  118. if (l == null )
  119. throw new NoSuchElementException();
  120. return l.item;
  121. }
  122. //Delete the head node
  123. public E removeFirst() {
  124. final Node<E> f = first ;
  125. if (f == null )
  126. throw new NoSuchElementException();
  127. return unlinkFirst(f);
  128. }
  129. //Delete the tail node
  130. public E removeLast() {
  131. final Node<E> l = last ;
  132. if (l == null )
  133. throw new NoSuchElementException();
  134. return unlinkLast(l);
  135. }
  136. //Set the added element as the head node of LinkedList
  137. public void addFirst(E e) {
  138. linkFirst(e);
  139. }
  140. //Set the added element as the tail node of LinkedList
  141. public void addLast(E e) {
  142. linkLast(e);
  143. }
  144. //Judge whether LinkedList contains the specified element
  145. public boolean contains (Object o) {
  146. return indexOf(o) != -1;
  147. }
  148. // Return the number of elements in the List
  149. public   int   size () {
  150. return   size ;
  151. }
  152. //Add elements to the end of LinkedList
  153. public boolean add (E e) {
  154. linkLast(e);
  155. return   true ;
  156. }
  157. //Delete the specified element
  158. public boolean remove(Object o) {
  159. if (o == null ) {
  160. for (Node<E> x = first ; x != null ; x = x. next ) {
  161. if (x.item == null ) {
  162. unlink(x);
  163. return   true ;
  164. }
  165. }
  166. } else {
  167. for (Node<E> x = first ; x != null ; x = x. next ) {
  168. if (o.equals(x.item)) {
  169. unlink(x);
  170. return   true ;
  171. }
  172. }
  173. }
  174. return   false ;
  175. }
  176. //Add the elements in the collection to the List
  177. public boolean addAll(Collection<? extends E> c) {
  178. return addAll( size , c);
  179. }
  180. //Insert all elements in the collection into the List, starting from the specified position
  181. public boolean addAll( int   index , Collection<? extends E> c) {
  182. checkPositionIndex( index );
  183. Object[] a = c.toArray(); //Convert the collection into an array
  184. int numNew = a.length; //Get the number of elements in the collection
  185. if (numNew == 0) //There is no element in the collection, return false  
  186. return   false ;
  187. Node<E> pred, succ;
  188. if ( index == size ) {
  189. succ = null ;
  190. pred = last ;
  191. } else {
  192. succ = node( index ); //Get the node element at position index and assign it to succ
  193. pred = succ.prev;
  194. }
  195. for (Object o : a) { //Traverse the array to insert. Modify the predecessor and successor of the node
  196. @SuppressWarnings( "unchecked" ) E e = (E) o;
  197. Node<E> newNode = new Node<>(pred, e, null );
  198. if (pred== null )
  199. first = newNode;
  200. else  
  201. pred.next = newNode;
  202. pred = newNode;
  203. }
  204. if (succ == null ) {
  205. last = pred;
  206. } else {
  207. pred.next = succ;
  208. succ.prev = pred;
  209. }
  210. size += numNew;
  211. modCount++;
  212. return   true ;
  213. }
  214. //Delete all elements in the List
  215. public void clear() {
  216. // Clearing all   of the links between nodes is   "unnecessary" , but:
  217. // - helps a generational GC if the discarded nodes inhabit
  218. // more than one generation
  219. // - is sure to   free memory even if there is a reachable Iterator
  220. for (Node<E> x = first ; x != null ; ) {
  221. Node<E> next = x. next ;
  222. x.item = null ;
  223. x.next = null ;
  224. x.prev = null ;
  225. x = next ;
  226. }
  227. first = last = null ;
  228. size = 0;
  229. modCount++;
  230. }
  231. //Get the element at the specified position
  232. public E get( int   index ) {
  233. checkElementIndex( index );
  234. return node( index ).item;
  235. }
  236. //Prevent the node from being in the specified position
  237. public E set ( int   index , E element) {
  238. checkElementIndex( index );
  239. Node<E> x = node( index );
  240. E oldVal = x.item;
  241. x.item = element;
  242. return oldVal;
  243. }
  244. //Place the node at the specified location
  245. public void add ( int   index , E element) {
  246. checkPositionIndex( index );
  247. if ( index == size )
  248. linkLast(element);
  249. else  
  250. linkBefore(element, node( index ));
  251. }
  252. //Delete the element at the specified position
  253. public E remove( int   index ) {
  254. checkElementIndex( index );
  255. return unlink(node( index ));
  256. }
  257. // Check if the index is legal
  258. private boolean isElementIndex( int   index ) {
  259. return   index >= 0 && index < size ;
  260. }
  261. // Check if the location is legal
  262. private boolean isPositionIndex( int   index ) {
  263. return   index >= 0 && index <= size ;
  264. }
  265. //Index overflow information
  266. private String outOfBoundsMsg( int   index ) {
  267. return   "Index: " + index + ", Size: " + size ;
  268. }
  269. // Check if the node is legal
  270. private void checkElementIndex( int   index ) {
  271. if (!isElementIndex( index ))
  272. throw new IndexOutOfBoundsException(outOfBoundsMsg( index ));
  273. }
  274. // Check if the location is legal
  275. private void checkPositionIndex( int   index ) {
  276. if (!isPositionIndex( index ))
  277. throw new IndexOutOfBoundsException(outOfBoundsMsg( index ));
  278. }
  279. //Return the node information of the specified location
  280. //LinkedList cannot be accessed randomly, and the corresponding node can only be found by traversing
  281. //In order to improve efficiency, the current position is first judged from the middle position of the number of elements. If it is smaller than the middle position,
  282. //Start traversing from the head node, if it is greater than the middle position, start traversing from the tail node
  283. Node<E> node( int   index ) {
  284. // assert isElementIndex( index );
  285. if ( index < ( size >> 1)) {
  286. Node<E> x = first ;
  287. for ( int i = 0; i < index ; i++)
  288. x = x.next ;
  289. return x;
  290. } else {
  291. Node<E> x = last ;
  292. for ( int i = size - 1; i > index ; i --)  
  293. x = x.prev;
  294. return x;
  295. }
  296. }
  297. // Returns the position of the first occurrence of the specified element
  298. public   int indexOf(Object o) {
  299. int   index = 0;
  300. if (o == null ) {
  301. for (Node<E> x = first ; x != null ; x = x. next ) {
  302. if (x.item == null )
  303. return   index ;
  304. index ++;
  305. }
  306. } else {
  307. for (Node<E> x = first ; x != null ; x = x. next ) {
  308. if (o.equals(x.item))
  309. return   index ;
  310. index ++;
  311. }
  312. }
  313. return -1;
  314. }
  315. // Return the position of the last occurrence of an element
  316. public   int lastIndexOf(Object o) {
  317. int   index = size ;
  318. if (o == null ) {
  319. for (Node<E> x = last ; x != null ; x = x.prev) {
  320. index --;  
  321. if (x.item == null )
  322. return   index ;
  323. }
  324. } else {
  325. for (Node<E> x = last ; x != null ; x = x.prev) {
  326. index --;  
  327. if (o.equals(x.item))
  328. return   index ;
  329. }
  330. }
  331. return -1;
  332. }
  333. // Pop up the value of the first element
  334. public E peek() {
  335. final Node<E> f = first ;
  336. return (f == null ) ? null : f.item;
  337. }
  338. //Get the first element
  339. public E element() {
  340. return getFirst();
  341. }
  342. // Pop up the first element and delete it
  343. public E poll() {
  344. final Node<E> f = first ;
  345. return (f == null ) ? null : unlinkFirst(f);
  346. }
  347. //Delete the first element
  348. public E remove() {
  349. return removeFirst();
  350. }
  351. //Add to the end
  352. public boolean offer(E e) {
  353. return   add (e);
  354. }
  355. //Add to the header
  356. public boolean offerFirst(E e) {
  357. addFirst(e);
  358. return   true ;
  359. }
  360. //Insert to the last element
  361. public boolean offerLast(E e) {
  362. addLast(e);
  363. return   true ;
  364. }
  365. //Queue operation
  366. //Try to pop the first element, but do not delete the element
  367. public E peekFirst() {
  368. final Node<E> f = first ;
  369. return (f == null ) ? null : f.item;
  370. }
  371. //Queue operation
  372. //Try to pop the last element without deleting it
  373. public E peekLast() {
  374. final Node<E> l = last ;
  375. return (l == null ) ? null : l.item;
  376. }
  377. // Pop up the first element and delete it
  378. public E pollFirst() {
  379. final Node<E> f = first ;
  380. return (f == null ) ? null : unlinkFirst(f);
  381. }
  382. // Pop up the last element and delete it
  383. public E pollLast() {
  384. final Node<E> l = last ;
  385. return (l == null ) ? null : unlinkLast(l);
  386. }
  387. //If the queue is added to the head
  388. public void push(E e) {
  389. addFirst(e);
  390. }
  391. //Delete the first node from the queue
  392. public E pop() {
  393. return removeFirst();
  394. }
  395. //Delete the first occurrence of the specified element
  396. public boolean removeFirstOccurrence(Object o) {
  397. return remove(o);
  398. }
  399. //Delete the last occurrence of the specified element
  400. public boolean removeLastOccurrence(Object o) {
  401. if (o == null ) {
  402. for (Node<E> x = last ; x != null ; x = x.prev) {
  403. if (x.item == null ) {
  404. unlink(x);
  405. return   true ;
  406. }
  407. }
  408. } else {
  409. for (Node<E> x = last ; x != null ; x = x.prev) {
  410. if (o.equals(x.item)) {
  411. unlink(x);
  412. return   true ;
  413. }
  414. }
  415. }
  416. return   false ;
  417. }
  418. //Traversal method
  419. public ListIterator<E> listIterator( int   index ) {
  420. checkPositionIndex( index );
  421. return new ListItr( index );
  422. }
  423. //Internal class, implementing the ListIterator interface
  424. private class ListItr implements ListIterator<E> {
  425. private Node<E> lastReturned = null ;
  426. private Node<E> next ;
  427. private int nextIndex;
  428. private int expectedModCount = modCount;
  429. ListItr( int   index ) {
  430. // assert isPositionIndex( index );
  431. next = ( index == size ) ? null : node( index );
  432. nextIndex = index ;
  433. }
  434. public boolean hasNext() {
  435. return nextIndex < size ;
  436. }
  437. public E next () {
  438. checkForCommodification();
  439. if (!hasNext())
  440. throw new NoSuchElementException();
  441. lastReturned = next ;
  442. next = next . next ;
  443. nextIndex++;
  444. return lastReturned.item;
  445. }
  446. public boolean hasPrevious() {
  447. return nextIndex > 0;
  448. }
  449. public E previous() {
  450. checkForCommodification();
  451. if (!hasPrevious())
  452. throw new NoSuchElementException();
  453. lastReturned = next = ( next == null ) ? last : next .prev;
  454. nextIndex --;  
  455. return lastReturned.item;
  456. }
  457. public   int nextIndex() {
  458. return nextIndex;
  459. }
  460. public   int previousIndex() {
  461. return nextIndex - 1;
  462. }
  463. public void remove() {
  464. checkForCommodification();
  465. if (lastReturned == null )
  466. throw new IllegalStateException();
  467. Node<E> lastNext = lastReturned. next ;
  468. unlink(lastReturned);
  469. if ( next == lastReturned)
  470. next = lastNext;
  471. else  
  472. nextIndex --;  
  473. lastReturned = null ;
  474. expectedModCount++;
  475. }
  476. public void set (E e) {
  477. if (lastReturned == null )
  478. throw new IllegalStateException();
  479. checkForCommodification();
  480. lastReturned.item = e;
  481. }
  482. public void add (E e) {
  483. checkForCommodification();
  484. lastReturned = null ;
  485. if( next == null )
  486. linkLast(e);
  487. else  
  488. linkBefore(e, next );
  489. nextIndex++;
  490. expectedModCount++;
  491. }
  492. final void checkForCommodification() {
  493. if (modCount != expectedModCount)
  494. throw new ConcurrentModificationException();
  495. }
  496. }
  497. //Static inner class, create node
  498. private static class Node<E> {
  499. E item;
  500. Node<E> next ;
  501. Node<E> prev;
  502. Node(Node<E> prev, E element, Node<E> next ) {
  503. this.item = element;
  504. this.next = next ;
  505. this.prev = prev;
  506. }
  507. }
  508. /**
  509. * @since 1.6
  510. */
  511. public Iterator<E> descendingIterator() {
  512. return new DescendingIterator();
  513. }
  514. /**
  515. * Adapter to provide descending iterators via ListItr.previous
  516. */
  517. private class DescendingIterator implements Iterator<E> {
  518. private final ListItr itr = new ListItr( size ());
  519. public boolean hasNext() {
  520. return itr.hasPrevious();
  521. }
  522. public E next () {
  523. return itr.previous();
  524. }
  525. public void remove() {
  526. itr.remove();
  527. }
  528. }
  529. @SuppressWarnings( "unchecked" )
  530. private LinkedList<E> superClone() {
  531. try {
  532. return (LinkedList<E>) super.clone();
  533. } catch (CloneNotSupportedException e) {
  534. throw new InternalError();
  535. }
  536. }
  537. /**
  538. * Returns a shallow copy of this {@code LinkedList}. (The elements
  539. * themselves are not cloned.)
  540. *
  541. * @ return a shallow copy of this {@code LinkedList} instance
  542. */
  543. public Object clone() {
  544. LinkedList<E> clone = superClone();
  545. // Put clone into   "virgin" state
  546. clone.first = clone.last = null ;
  547. clone.size = 0;
  548. clone.modCount = 0;
  549. // Initialize clone with our elements
  550. for (Node<E> x = first ; x != null ; x = x. next )
  551. clone.add (x.item);
  552. return clone;
  553. }
  554. public Object[] toArray() {
  555. Object[] result = new Object[ size ];
  556. int i = 0;
  557. for (Node<E> x = first ; x != null ; x = x. next )
  558. result[i++] = x.item;
  559. return result;
  560. }
  561. @SuppressWarnings( "unchecked" )
  562. public <T> T[] toArray(T[] a) {
  563. if (a.length < size )
  564. a = (T[])java.lang.reflect.Array.newInstance(
  565. a.getClass().getComponentType(), size );
  566. int i = 0;
  567. Object[] result = a;
  568. for (Node<E> x = first ; x != null ; x = x. next )
  569. result[i++] = x.item;
  570. if (a.length > size )
  571. a[ size ] = null ;
  572. return a;
  573. }
  574. private static final long serialVersionUID = 876323262645176354L;
  575. //Write the object to the output stream
  576. private void writeObject(java.io.ObjectOutputStream s)
  577. throws java.io.IOException {
  578. // Write out   any hidden serialization magic
  579. s.defaultWriteObject();
  580. // Write out   size  
  581. s.writeInt( size );
  582. // Write out   all elements in the proper order .
  583. for (Node<E> x = first ; x != null ; x = x. next )
  584. s.writeObject(x.item);
  585. }
  586. //Read the object from the input stream
  587. @SuppressWarnings( "unchecked" )
  588. private void readObject(java.io.ObjectInputStream s)
  589. throws java.io.IOException, ClassNotFoundException {
  590. // Read   in   any hidden serialization magic
  591. s.defaultReadObject();
  592. // Read   in   size  
  593. int   size = s.readInt();
  594. // Read   in   all elements in the proper order .
  595. for ( int i = 0; i < size ; i++)
  596. linkLast((E)s.readObject());
  597. }
  598. }

1. Construction method

  1. LinkedList()
  2. LinkedList(Collection<? extends E> c)

LinkedList has no concept of length, so there is no problem of insufficient capacity, so there is no need to provide a constructor to initialize the size. Therefore, two methods are provided, one is a no-parameter constructor to initialize a LinkedList object, and the other is a constructor that converts the specified collection element into a LinkedList.

2. Add nodes

  1. public boolean add (E e) {
  2. linkLast(e);
  3. return   true ;
  4. }
  5. void linkLast(E e) {
  6. final Node<E> l = last ;
  7. final Node<E> newNode = new Node<>(l, e, null );
  8. last = newNode;
  9. if (l == null )
  10. first = newNode;
  11. else  
  12. l.next = newNode ;
  13. size ++;
  14. modCount++;
  15. }

From the source code, we can see that the adding process is as follows:

  • Record the current end node by constructing another pointer l pointing to the end node
  • Generate a new node: Note that since it is added at the end of the linked list, next is null
  • last points to the new node
  • There is a judgment here. My understanding is to judge whether it is the first element (when l==null, it means there is no node in the linked list).
  • Then it is easy to understand this judgment. If it is the first node, use first to point to this node. If not, the next of the current node points to the newly added node.
  • Increase the size: Add element "D" to the LinkedList["A","B","C"] mentioned above. The process is roughly as shown in the figure:

3. Delete nodes

LinkedList provides two methods to delete nodes

  1. //Method 1. Delete the node at the specified index
  2. public E remove( int   index ) {
  3. // Check if the index is correct
  4. checkElementIndex( index );
  5. //There are two steps here, first locate the node by index, second delete the node
  6. return unlink(node( index ));
  7. }
  8. //Method 2. Delete the node with the specified value
  9. public boolean remove(Object o) {
  10. // Check if the deleted element is null  
  11. if (o == null ) {
  12. //If it is null , traverse and delete
  13. for (Node<E> x = first ; x != null ; x = x. next ) {
  14. if (x.item == null ) {
  15. unlink(x);
  16. return   true ;
  17. }
  18. }
  19. } else {
  20. //If it is not a traversal deletion
  21. for (Node<E> x = first ; x != null ; x = x. next ) {
  22. if (o.equals(x.item)) {
  23. unlink(x);
  24. return   true ;
  25. }
  26. }
  27. }
  28. return   false ;
  29. }

From the source code, we can see that both methods are deleted through unlink()

  • Get the current value of the element to be deleted, a reference to the node before it, and a reference to the node after it.
  • Determine whether the deleted node is the first node. If so, first moves backward. If not, point the previous node of the current node, next, to the next node after the current node.
  • Determine whether the deleted node is the last node. If so, move last forward. If not, point the prev of the node after the current node to the node before the current node.
  • Set the value of the current node to null
  • The size is reduced and the value of the deleted node is returned

3. Differences between ArrayList and LinkedList

1. Bottom-level implementation: ArrayList is implemented as an array, while LinkedList is implemented as a doubly linked list structure;

2. Interface implementation: Both implement the List interface and are linear list implementations. ArrayList implements RandomAccess to support random element access, while LinkedList implements Deque to be used as a queue.

3. Performance: ArrayList needs to copy the original array when adding or deleting elements, while LinkedList only needs to move the pointer to find elements. ArrayList supports random element access, while LinkedList can only traverse one node at a time.

4. Thread safety: All are thread unsafe;

5. The performance of inserting and deleting in LinkedList is better than that in ArrayList. This is because ArrayList needs extra overhead to move and copy elements when inserting and deleting elements (but the traversal and deletion shown in the removeElements2 method is very fast. This method deletes from the end, without moving or copying elements, so the speed is very fast);

6.ArrayList is inferior to LinkedList in terms of performance in querying elements;

Summarize

LinkedList is a very powerful class that can be used as a List collection, a double-ended queue, and a stack. LinkedList uses a linked list at the bottom to store the elements in the collection, so the performance of random access is poor, but the performance of insertion and deletion is very good.

This article is reproduced from the WeChat public account "Android Development Programming"

<<:  Sales volume of branded smartphones in the Chinese market in July: OPPO ranked first, Honor ranked third, and Xiaomi ranked fourth

>>:  New version of Glance released, a better Android database debugging assistant

Recommend

So this is the secret of Dujiangyan!

Dujiangyan Irrigation Project Located on the Minj...

Counterpoint: XR headset shipments to drop 19% in 2023

According to Counterpoint’s Global XR (AR and Hea...

Encountering the "flame" of the starry sky: exploring the mystery of the sun

Produced by: Science Popularization China Author:...

How to create Alipay mini program and join Taobao mini program?

Q: How to create Alipay mini program and join Tao...

A comprehensive guide to optimizing information flow ads!

Q1. My advertising has always been low in volume....

Grasp 20 human weaknesses and you will be an operation master

What weakness marketing does is to lead people to...

Why do plant leaves change color?

When autumn comes, the leaves in the parks and on...

Douyin operation: How to create a popular Douyin account?

Is TikTok still in its bonus period? Douyin has e...