LinkedList源码剖析

喜欢ヅ旅行 2022-07-13 11:44 294阅读 0赞

1. LinkedList简介

LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当作链表来操作外,它还可以当作栈,队列和双端队列来使用。

LinkedList同样是非线程安全的,只在单线程下适合使用。

LinkedList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了Cloneable接口,能被克隆。

2. LinkedList源码剖析

LinkedList的源码如下(加入了比较详细的注释)

  1. package java.util;
  2. public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
  3. // 链表的表头,表头不包含任何数据。Entry是个链表类数据结构。
  4. private transient Entry<E> header = new Entry<E>(null, null, null);
  5. // LinkedList中元素个数
  6. private transient int size = 0;
  7. // 默认构造函数:创建一个空的链表
  8. public LinkedList() {
  9. header.next = header.previous = header;
  10. }
  11. // 包含“集合”的构造函数:创建一个包含“集合”的LinkedList
  12. public LinkedList(Collection<? extends E> c) {
  13. this();
  14. addAll(c);
  15. }
  16. // 获取LinkedList的第一个元素
  17. public E getFirst() {
  18. if (size==0)
  19. throw new NoSuchElementException();
  20. // 链表的表头header中不包含数据。
  21. // 这里返回header所指下一个节点所包含的数据。
  22. return header.next.element;
  23. }
  24. // 获取LinkedList的最后一个元素
  25. public E getLast() {
  26. if (size==0)
  27. throw new NoSuchElementException();
  28. // 由于LinkedList是双向链表;而表头header不包含数据。
  29. // 因而,这里返回表头header的前一个节点所包含的数据。
  30. return header.previous.element;
  31. }
  32. // 删除LinkedList的第一个元素
  33. public E removeFirst() {
  34. return remove(header.next);
  35. }
  36. // 删除LinkedList的最后一个元素
  37. public E removeLast() {
  38. return remove(header.previous);
  39. }
  40. // 将元素添加到LinkedList的起始位置
  41. public void addFirst(E e) {
  42. addBefore(e, header.next);
  43. }
  44. // 将元素添加到LinkedList的结束位置
  45. public void addLast(E e) {
  46. addBefore(e, header);
  47. }
  48. // 判断LinkedList是否包含元素(o)
  49. public boolean contains(Object o) {
  50. return indexOf(o) != -1;
  51. }
  52. // 返回LinkedList的大小
  53. public int size() {
  54. return size;
  55. }
  56. // 将元素(E)添加到LinkedList中
  57. public boolean add(E e) {
  58. // 将节点(节点数据是e)添加到表头(header)之前。
  59. // 即,将节点添加到双向链表的末端。
  60. addBefore(e, header);
  61. return true;
  62. }
  63. // 从LinkedList中删除元素(o)
  64. // 从链表开始查找,如存在元素(o)则删除该元素并返回true;
  65. // 否则,返回false。
  66. public boolean remove(Object o) {
  67. if (o==null) {
  68. // 若o为null的删除情况
  69. for (Entry<E> e = header.next; e != header; e = e.next) {
  70. if (e.element==null) {
  71. remove(e);
  72. return true;
  73. }
  74. }
  75. } else {
  76. // 若o不为null的删除情况
  77. for (Entry<E> e = header.next; e != header; e = e.next) {
  78. if (o.equals(e.element)) {
  79. remove(e);
  80. return true;
  81. }
  82. }
  83. }
  84. return false;
  85. }
  86. // 将“集合(c)”添加到LinkedList中。
  87. // 实际上,是从双向链表的末尾开始,将“集合(c)”添加到双向链表中。
  88. public boolean addAll(Collection<? extends E> c) {
  89. return addAll(size, c);
  90. }
  91. // 从双向链表的index开始,将“集合(c)”添加到双向链表中。
  92. public boolean addAll(int index, Collection<? extends E> c) {
  93. if (index < 0 || index > size)
  94. throw new IndexOutOfBoundsException("Index: "+index+
  95. ", Size: "+size);
  96. Object[] a = c.toArray();
  97. // 获取集合的长度
  98. int numNew = a.length;
  99. if (numNew==0)
  100. return false;
  101. modCount++;
  102. // 设置“当前要插入节点的后一个节点”
  103. Entry<E> successor = (index==size ? header : entry(index));
  104. // 设置“当前要插入节点的前一个节点”
  105. Entry<E> predecessor = successor.previous;
  106. // 将集合(c)全部插入双向链表中
  107. for (int i=0; i<numNew; i++) {
  108. Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
  109. predecessor.next = e;
  110. predecessor = e;
  111. }
  112. successor.previous = predecessor;
  113. // 调整LinkedList的实际大小
  114. size += numNew;
  115. return true;
  116. }
  117. // 清空双向链表
  118. public void clear() {
  119. Entry<E> e = header.next;
  120. // 从表头开始,逐个向后遍历;对遍历到的节点执行一下操作:
  121. // (01) 设置前一个节点为null
  122. // (02) 设置当前节点的内容为null
  123. // (03) 设置后一个节点为“新的当前节点”
  124. while (e != header) {
  125. Entry<E> next = e.next;
  126. e.next = e.previous = null;
  127. e.element = null;
  128. e = next;
  129. }
  130. header.next = header.previous = header;
  131. // 设置大小为0
  132. size = 0;
  133. modCount++;
  134. }
  135. // 返回LinkedList指定位置的元素
  136. public E get(int index) {
  137. return entry(index).element;
  138. }
  139. // 设置index位置对应的节点的值为element
  140. public E set(int index, E element) {
  141. Entry<E> e = entry(index);
  142. E oldVal = e.element;
  143. e.element = element;
  144. return oldVal;
  145. }
  146. // 在index前添加节点,且节点的值为element
  147. public void add(int index, E element) {
  148. addBefore(element, (index==size ? header : entry(index)));
  149. }
  150. // 删除index位置的节点
  151. public E remove(int index) {
  152. return remove(entry(index));
  153. }
  154. // 获取双向链表中指定位置的节点
  155. private Entry<E> entry(int index) {
  156. if (index < 0 || index >= size)
  157. throw new IndexOutOfBoundsException("Index: "+index+
  158. ", Size: "+size);
  159. Entry<E> e = header;
  160. // 获取index处的节点。
  161. // 若index < 双向链表长度的1/2,则从前先后查找;
  162. // 否则,从后向前查找。
  163. if (index < (size >> 1)) {
  164. for (int i = 0; i <= index; i++)
  165. e = e.next;
  166. } else {
  167. for (int i = size; i > index; i--)
  168. e = e.previous;
  169. }
  170. return e;
  171. }
  172. // 从前向后查找,返回“值为对象(o)的节点对应的索引”
  173. // 不存在就返回-1
  174. public int indexOf(Object o) {
  175. int index = 0;
  176. if (o==null) {
  177. for (Entry e = header.next; e != header; e = e.next) {
  178. if (e.element==null)
  179. return index;
  180. index++;
  181. }
  182. } else {
  183. for (Entry e = header.next; e != header; e = e.next) {
  184. if (o.equals(e.element))
  185. return index;
  186. index++;
  187. }
  188. }
  189. return -1;
  190. }
  191. // 从后向前查找,返回“值为对象(o)的节点对应的索引”
  192. // 不存在就返回-1
  193. public int lastIndexOf(Object o) {
  194. int index = size;
  195. if (o==null) {
  196. for (Entry e = header.previous; e != header; e = e.previous) {
  197. index--;
  198. if (e.element==null)
  199. return index;
  200. }
  201. } else {
  202. for (Entry e = header.previous; e != header; e = e.previous) {
  203. index--;
  204. if (o.equals(e.element))
  205. return index;
  206. }
  207. }
  208. return -1;
  209. }
  210. // 返回第一个节点
  211. // 若LinkedList的大小为0,则返回null
  212. public E peek() {
  213. if (size==0)
  214. return null;
  215. return getFirst();
  216. }
  217. // 返回第一个节点
  218. // 若LinkedList的大小为0,则抛出异常
  219. public E element() {
  220. return getFirst();
  221. }
  222. // 删除并返回第一个节点
  223. // 若LinkedList的大小为0,则返回null
  224. public E poll() {
  225. if (size==0)
  226. return null;
  227. return removeFirst();
  228. }
  229. // 将e添加双向链表末尾
  230. public boolean offer(E e) {
  231. return add(e);
  232. }
  233. // 将e添加双向链表开头
  234. public boolean offerFirst(E e) {
  235. addFirst(e);
  236. return true;
  237. }
  238. // 将e添加双向链表末尾
  239. public boolean offerLast(E e) {
  240. addLast(e);
  241. return true;
  242. }
  243. // 返回第一个节点
  244. // 若LinkedList的大小为0,则返回null
  245. public E peekFirst() {
  246. if (size==0)
  247. return null;
  248. return getFirst();
  249. }
  250. // 返回最后一个节点
  251. // 若LinkedList的大小为0,则返回null
  252. public E peekLast() {
  253. if (size==0)
  254. return null;
  255. return getLast();
  256. }
  257. // 删除并返回第一个节点
  258. // 若LinkedList的大小为0,则返回null
  259. public E pollFirst() {
  260. if (size==0)
  261. return null;
  262. return removeFirst();
  263. }
  264. // 删除并返回最后一个节点
  265. // 若LinkedList的大小为0,则返回null
  266. public E pollLast() {
  267. if (size==0)
  268. return null;
  269. return removeLast();
  270. }
  271. // 将e插入到双向链表开头
  272. public void push(E e) {
  273. addFirst(e);
  274. }
  275. // 删除并返回第一个节点
  276. public E pop() {
  277. return removeFirst();
  278. }
  279. // 从LinkedList开始向后查找,删除第一个值为元素(o)的节点
  280. // 从链表开始查找,如存在节点的值为元素(o)的节点,则删除该节点
  281. public boolean removeFirstOccurrence(Object o) {
  282. return remove(o);
  283. }
  284. // 从LinkedList末尾向前查找,删除第一个值为元素(o)的节点
  285. // 从链表开始查找,如存在节点的值为元素(o)的节点,则删除该节点
  286. public boolean removeLastOccurrence(Object o) {
  287. if (o==null) {
  288. for (Entry<E> e = header.previous; e != header; e = e.previous) {
  289. if (e.element==null) {
  290. remove(e);
  291. return true;
  292. }
  293. }
  294. } else {
  295. for (Entry<E> e = header.previous; e != header; e = e.previous) {
  296. if (o.equals(e.element)) {
  297. remove(e);
  298. return true;
  299. }
  300. }
  301. }
  302. return false;
  303. }
  304. // 返回“index到末尾的全部节点”对应的ListIterator对象(List迭代器)
  305. public ListIterator<E> listIterator(int index) {
  306. return new ListItr(index);
  307. }
  308. // List迭代器
  309. private class ListItr implements ListIterator<E> {
  310. // 上一次返回的节点
  311. private Entry<E> lastReturned = header;
  312. // 下一个节点
  313. private Entry<E> next;
  314. // 下一个节点对应的索引值
  315. private int nextIndex;
  316. // 期望的改变计数。用来实现fail-fast机制。
  317. private int expectedModCount = modCount;
  318. // 构造函数。
  319. // 从index位置开始进行迭代
  320. ListItr(int index) {
  321. // index的有效性处理
  322. if (index < 0 || index > size)
  323. throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size);
  324. // 若 “index 小于 ‘双向链表长度的一半’”,则从第一个元素开始往后查找;
  325. // 否则,从最后一个元素往前查找。
  326. if (index < (size >> 1)) {
  327. next = header.next;
  328. for (nextIndex=0; nextIndex<index; nextIndex++)
  329. next = next.next;
  330. } else {
  331. next = header;
  332. for (nextIndex=size; nextIndex>index; nextIndex--)
  333. next = next.previous;
  334. }
  335. }
  336. // 是否存在下一个元素
  337. public boolean hasNext() {
  338. // 通过元素索引是否等于“双向链表大小”来判断是否达到最后。
  339. return nextIndex != size;
  340. }
  341. // 获取下一个元素
  342. public E next() {
  343. checkForComodification();
  344. if (nextIndex == size)
  345. throw new NoSuchElementException();
  346. lastReturned = next;
  347. // next指向链表的下一个元素
  348. next = next.next;
  349. nextIndex++;
  350. return lastReturned.element;
  351. }
  352. // 是否存在上一个元素
  353. public boolean hasPrevious() {
  354. // 通过元素索引是否等于0,来判断是否达到开头。
  355. return nextIndex != 0;
  356. }
  357. // 获取上一个元素
  358. public E previous() {
  359. if (nextIndex == 0)
  360. throw new NoSuchElementException();
  361. // next指向链表的上一个元素
  362. lastReturned = next = next.previous;
  363. nextIndex--;
  364. checkForComodification();
  365. return lastReturned.element;
  366. }
  367. // 获取下一个元素的索引
  368. public int nextIndex() {
  369. return nextIndex;
  370. }
  371. // 获取上一个元素的索引
  372. public int previousIndex() {
  373. return nextIndex-1;
  374. }
  375. // 删除当前元素。
  376. // 删除双向链表中的当前节点
  377. public void remove() {
  378. checkForComodification();
  379. Entry<E> lastNext = lastReturned.next;
  380. try {
  381. LinkedList.this.remove(lastReturned);
  382. } catch (NoSuchElementException e) {
  383. throw new IllegalStateException();
  384. }
  385. if (next==lastReturned)
  386. next = lastNext;
  387. else
  388. nextIndex--;
  389. lastReturned = header;
  390. expectedModCount++;
  391. }
  392. // 设置当前节点为e
  393. public void set(E e) {
  394. if (lastReturned == header)
  395. throw new IllegalStateException();
  396. checkForComodification();
  397. lastReturned.element = e;
  398. }
  399. // 将e添加到当前节点的前面
  400. public void add(E e) {
  401. checkForComodification();
  402. lastReturned = header;
  403. addBefore(e, next);
  404. nextIndex++;
  405. expectedModCount++;
  406. }
  407. // 判断 “modCount和expectedModCount是否相等”,依次来实现fail-fast机制。
  408. final void checkForComodification() {
  409. if (modCount != expectedModCount)
  410. throw new ConcurrentModificationException();
  411. }
  412. }
  413. // 双向链表的节点所对应的数据结构。
  414. // 包含3部分:上一节点,下一节点,当前节点值。
  415. private static class Entry<E> {
  416. // 当前节点所包含的值
  417. E element;
  418. // 下一个节点
  419. Entry<E> next;
  420. // 上一个节点
  421. Entry<E> previous;
  422. /** * 链表节点的构造函数。 * 参数说明: * element —— 节点所包含的数据 * next —— 下一个节点 * previous —— 上一个节点 */
  423. Entry(E element, Entry<E> next, Entry<E> previous) {
  424. this.element = element;
  425. this.next = next;
  426. this.previous = previous;
  427. }
  428. }
  429. // 将节点(节点数据是e)添加到entry节点之前。
  430. private Entry<E> addBefore(E e, Entry<E> entry) {
  431. // 新建节点newEntry,将newEntry插入到节点e之前;并且设置newEntry的数据是e
  432. Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
  433. newEntry.previous.next = newEntry;
  434. newEntry.next.previous = newEntry;
  435. // 修改LinkedList大小
  436. size++;
  437. // 修改LinkedList的修改统计数:用来实现fail-fast机制。
  438. modCount++;
  439. return newEntry;
  440. }
  441. // 将节点从链表中删除
  442. private E remove(Entry<E> e) {
  443. if (e == header)
  444. throw new NoSuchElementException();
  445. E result = e.element;
  446. e.previous.next = e.next;
  447. e.next.previous = e.previous;
  448. e.next = e.previous = null;
  449. e.element = null;
  450. size--;
  451. modCount++;
  452. return result;
  453. }
  454. // 反向迭代器
  455. public Iterator<E> descendingIterator() {
  456. return new DescendingIterator();
  457. }
  458. // 反向迭代器实现类。
  459. private class DescendingIterator implements Iterator {
  460. final ListItr itr = new ListItr(size());
  461. // 反向迭代器是否下一个元素。
  462. // 实际上是判断双向链表的当前节点是否达到开头
  463. public boolean hasNext() {
  464. return itr.hasPrevious();
  465. }
  466. // 反向迭代器获取下一个元素。
  467. // 实际上是获取双向链表的前一个节点
  468. public E next() {
  469. return itr.previous();
  470. }
  471. // 删除当前节点
  472. public void remove() {
  473. itr.remove();
  474. }
  475. }
  476. // 返回LinkedList的Object[]数组
  477. public Object[] toArray() {
  478. // 新建Object[]数组
  479. Object[] result = new Object[size];
  480. int i = 0;
  481. // 将链表中所有节点的数据都添加到Object[]数组中
  482. for (Entry<E> e = header.next; e != header; e = e.next)
  483. result[i++] = e.element;
  484. return result;
  485. }
  486. // 返回LinkedList的模板数组。所谓模板数组,即可以将T设为任意的数据类型
  487. public <T> T[] toArray(T[] a) {
  488. // 若数组a的大小 < LinkedList的元素个数(意味着数组a不能容纳LinkedList中全部元素)
  489. // 则新建一个T[]数组,T[]的大小为LinkedList大小,并将该T[]赋值给a。
  490. if (a.length < size)
  491. a = (T[])java.lang.reflect.Array.newInstance(
  492. a.getClass().getComponentType(), size);
  493. // 将链表中所有节点的数据都添加到数组a中
  494. int i = 0;
  495. Object[] result = a;
  496. for (Entry<E> e = header.next; e != header; e = e.next)
  497. result[i++] = e.element;
  498. if (a.length > size)
  499. a[size] = null;
  500. return a;
  501. }
  502. // 克隆函数。返回LinkedList的克隆对象。
  503. public Object clone() {
  504. LinkedList<E> clone = null;
  505. // 克隆一个LinkedList克隆对象
  506. try {
  507. clone = (LinkedList<E>) super.clone();
  508. } catch (CloneNotSupportedException e) {
  509. throw new InternalError();
  510. }
  511. // 新建LinkedList表头节点
  512. clone.header = new Entry<E>(null, null, null);
  513. clone.header.next = clone.header.previous = clone.header;
  514. clone.size = 0;
  515. clone.modCount = 0;
  516. // 将链表中所有节点的数据都添加到克隆对象中
  517. for (Entry<E> e = header.next; e != header; e = e.next)
  518. clone.add(e.element);
  519. return clone;
  520. }
  521. // java.io.Serializable的写入函数
  522. // 将LinkedList的“容量,所有的元素值”都写入到输出流中
  523. private void writeObject(java.io.ObjectOutputStream s)
  524. throws java.io.IOException {
  525. // Write out any hidden serialization magic
  526. s.defaultWriteObject();
  527. // 写入“容量”
  528. s.writeInt(size);
  529. // 将链表中所有节点的数据都写入到输出流中
  530. for (Entry e = header.next; e != header; e = e.next)
  531. s.writeObject(e.element);
  532. }
  533. // java.io.Serializable的读取函数:根据写入方式反向读出
  534. // 先将LinkedList的“容量”读出,然后将“所有的元素值”读出
  535. private void readObject(java.io.ObjectInputStream s)
  536. throws java.io.IOException, ClassNotFoundException {
  537. // Read in any hidden serialization magic
  538. s.defaultReadObject();
  539. // 从输入流中读取“容量”
  540. int size = s.readInt();
  541. // 新建链表表头节点
  542. header = new Entry<E>(null, null, null);
  543. header.next = header.previous = header;
  544. // 从输入流中将“所有的元素值”并逐个添加到链表中
  545. for (int i=0; i<size; i++)
  546. addBefore((E)s.readObject(), header);
  547. }
  548. }

3. 几点总结

关于LinkedList的源码,给出几点比较重要的总结:

1、从源码中很明显可以看出,LinkedList的实现是基于双向循环链表的,且头结点中不存放数据,如下图;

20140629153056171

2、注意两个不同的构造方法。无参构造方法直接建立一个仅包含head节点的空链表,包含Collection的构造方法,先调用无参构造方法建立一个空链表,然后将Collection中的数据加入到链表的尾部后面。

3、在查找和删除某元素时,源码中都划分为该元素为null和不为null两种情况来处理,LinkedList中允许元素为null。

4、LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容的方法。

5、注意源码中的Entry entry(int index)方法。该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index

  1. // 双向链表的节点所对应的数据结构。
  2. // 包含3部分:上一节点,下一节点,当前节点值。
  3. private static class Entry<E> {
  4. // 当前节点所包含的值
  5. E element;
  6. // 下一个节点
  7. Entry<E> next;
  8. // 上一个节点
  9. Entry<E> previous;
  10. /** * 链表节点的构造函数。 * 参数说明: * element —— 节点所包含的数据 * next —— 下一个节点 * previous —— 上一个节点 */
  11. Entry(E element, Entry<E> next, Entry<E> previous) {
  12. this.element = element;
  13. this.next = next;
  14. this.previous = previous;
  15. }
  16. }

7、LinkedList是基于链表实现的,因此插入删除效率高,查找效率低(虽然有一个加速动作)。

8、要注意源码中还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用。

原文链接:https://github.com/GeniusVJR/LearningNotes

数据结构与算法

发表评论

表情:
评论列表 (有 0 条评论,294人围观)

还没有评论,来说两句吧...

相关阅读

    相关 LinkedList剖析

    1. LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当作链表来操作外,它还可以当作栈,队列和双端队列来使用。

    相关 LinkedList剖析

    简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈、队列和双端队列来使用。 由于是链表结构,Linke

    相关 LinkedList

    LinkedList底层是基于双向链表,链表在内存中不是连续的,而是通过引用来关联所有的元素,所以链表的优点在于添加和删除元素比较快,因为只是移动指针,并且不需要判断是否需要扩