前面,我们已经学习了ArrayList,并了解了fail-fast机制。这一章我们接着学习List的实现类——LinkedList
和学习ArrayList一样,我们先对LinkedList有个整体认识,然后再学习它的源码;最后再通过实例来学会使用LinkedList
内容包括:
LinkedList介绍
LinkedList数据结构
LinkedList源码解析(基于JDK11.0.4)
LinkedList遍历方式(整表遍历, 字表遍历, 并发遍历)
一. LinkedList介绍 LinkedList介绍 LinkedList 是一个继承于AbstractSequentialList的双向链表 。因此它也可以被当作堆栈、队列或双端队列进行操作
LinkedList 实现 List 接口,能对它进行队列操作
LinkedList 实现 Deque 接口,能将LinkedList当作双端队列 使用
LinkedList 实现了Cloneable接口 ,即覆盖了函数clone(),能克隆
LinkedList 实现java.io.Serializable接口 ,这意味着LinkedList支持序列化,能通过序列化去传输
LinkedList 是线程不安全的(非同步的)
LinkedList构造函数 1 2 3 4 5 6 7 8 9 public LinkedList () {} public LinkedList (Collection<? extends E> c) { this (); addAll(c); }
LinkedList的API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 LinkedList的API E getFirst () E getLast () E removeFirst () E removeLast () void addFirst (E object) void addLast (E object) E peek () E element () E poll () E remove () boolean offer (E o) boolean offerFirst (E e) boolean offerLast (E e) E peekFirst () E peekLast () E pollFirst () E pollLast () E pop () void push (E e) boolean removeFirstOccurrence (Object o) boolean removeLastOccurrence (Object o) Iterator <E> descendingIterator () boolean contains (Object object) int size () boolean add (E object) boolean remove (Object o) void add (int location, E object) boolean addAll (Collection<? extends E> collection) boolean addAll (int location, Collection<? extends E> collection) void clear () E get (int location) E set (int index, E element) void add (int index, E element) E remove (int index) int indexOf (Object object) int lastIndexOf (Object object) ListIterator <E> listIterator (int location) <T> T [] toArray (T[] contents) Object [] toArray () Object clone () Spliterator <E> spliterator ()
AbstractSequentialList简介 在介绍LinkedList的源码之前,先介绍一下AbstractSequentialList。毕竟,LinkedList是AbstractSequentialList的子类
AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些方法(这些接口都是随机访问List的)
LinkedList是双向链表;既然它继承于AbstractSequentialList,就相当于已经实现了get(int index)这些接口
备注:
若需要通过AbstractSequentialList自己实现一个列表 ,只需要扩展此类,并提供 listIterator() 和 size() 方法的实现即可
若要实现不可修改的列表,则需要实现列表迭代器的 hasNext、next、hasPrevious、previous 和 index 方法即可
二. LinkedList数据结构 LinkedList的继承关系 1 2 3 4 5 6 7 8 9 java.lang.Object ↳ java.util.AbstractCollection<E> ↳ java.util.AbstractList<E> ↳ java.util.AbstractSequentialList<E> ↳ java.util.LinkedList<E> public class LinkedList <E> extends AbstractSequentialList <E> implements List <E>, Deque<E>, Cloneable, java.io.Serializable {}
LinkedList与Collection关系如图:
说明:
LinkedList的本质是双向链表:
① LinkedList继承于AbstractSequentialList,并且实现了Deque接口
三. LinkedList源码解析(基于JDK11.0.4) 在阅读源码之前,我们先对LinkedList的整体实现进行大致说明:
LinkedList实际上是通过双向链表去实现的。既然是双向链表,那么它的顺序访问会非常高效,而随机访问效率比较低
既然LinkedList是通过双向链表的,但是它也实现了List接口(也就是说,它实现了get(int location)、remove(int location)等根据索引值来获取 、删除节点的方法)
LinkedList是如何实现List的这些接口的,如何将双向链表和索引值联系起来的呢?
实际原理非常简单,它就是通过一个计数索引值 来实现的, 例如:
当我们调用get(int location)时,链表节点所对应的类Entry的实例首先会比较location和双向链表长度的1/2;若前者大,则从链表头开始往后查找,直到location位置;否则,从链表末尾开始先前查找,直到location位置
好了,接下来开始阅读源码(只要理解双向链表,那么LinkedList的源码很容易理解的)
补充:
① 在JDK 11中链表节点所对应的类命名由Entry改为Node
② first和last这两个变量是LinkedList的成员变量,分别指向头结点和尾节点:
如果双端链表为空的时候,两个都必须为null
如果链表不为空,那么first的前驱节点一定是null,first的item一定不为null
同理,last的后继节点一定是null,last的item一定不为null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 public class LinkedList <E> extends AbstractSequentialList <E> implements List <E>, Deque<E>, Cloneable, java.io.Serializable { transient int size = 0 ; transient Node<E> first; transient Node<E> last; public LinkedList () {} public LinkedList (Collection<? extends E> c) { this (); addAll(c); } private void linkFirst (E e) { final Node <E> f = first; final Node <E> newNode = new Node <>(null , e, f); first = newNode; if (f == null ) last = newNode; else f.prev = newNode; size++; modCount++; } void linkLast (E e) { final Node <E> l = last; final Node <E> newNode = new Node <>(l, e, null ); last = newNode; if (l == null ) first = newNode; else l.next = newNode; size++; modCount++; } void linkBefore (E e, Node<E> succ) { final Node <E> pred = succ.prev; final Node <E> newNode = new Node <>(pred, e, succ); succ.prev = newNode; if (pred == null ) first = newNode; else pred.next = newNode; size++; modCount++; } private E unlinkFirst (Node<E> f) { final E element = f.item; final Node <E> next = f.next; f.item = null ; f.next = null ; first = next; if (next == null ) last = null ; else next.prev = null ; size--; modCount++; return element; } private E unlinkLast (Node<E> l) { final E element = l.item; final Node <E> prev = l.prev; l.item = null ; l.prev = null ; last = prev; if (prev == null ) first = null ; else prev.next = null ; size--; modCount++; return element; } E unlink (Node<E> x) { final E element = x.item; final Node <E> next = x.next; final Node <E> prev = x.prev; if (prev == null ) { first = next; } else { prev.next = next; x.prev = null ; } if (next == null ) { last = prev; } else { next.prev = prev; x.next = null ; } x.item = null ; size--; modCount++; return element; } public E getFirst () { final Node <E> f = first; if (f == null ) throw new NoSuchElementException (); return f.item; } public E getLast () { final Node <E> l = last; if (l == null ) throw new NoSuchElementException (); return l.item; } public E removeFirst () { final Node <E> f = first; if (f == null ) throw new NoSuchElementException (); return unlinkFirst(f); } public E removeLast () { final Node <E> l = last; if (l == null ) throw new NoSuchElementException (); return unlinkLast(l); } public void addFirst (E e) { linkFirst(e); } public void addLast (E e) { linkLast(e); } public boolean contains (Object o) { return indexOf(o) >= 0 ; } public int size () { return size; } public boolean add (E e) { linkLast(e); return true ; } public boolean remove (Object o) { if (o == null ) { for (Node <E> x = first; x != null ; x = x.next) { if (x.item == null ) { unlink(x); return true ; } } } else { for (Node <E> x = first; x != null ; x = x.next) { if (o.equals(x.item)) { unlink(x); return true ; } } } return false ; } public boolean addAll (Collection<? extends E> c) { return addAll(size, c); } public boolean addAll (int index, Collection<? extends E> c) { checkPositionIndex(index); Object [] a = c.toArray(); int numNew = a.length; if (numNew == 0 ) return false ; Node<E> pred, succ; if (index == size) { succ = null ; pred = last; } else { succ = node(index); pred = succ.prev; } for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node <E> newNode = new Node <>(pred, e, null ); if (pred == null ) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null ) { last = pred; } else { pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true ; } public void clear () { for (Node <E> x = first; x != null ; ) { Node <E> next = x.next; x.item = null ; x.next = null ; x.prev = null ; x = next; } first = last = null ; size = 0 ; modCount++; } public E get (int index) { checkElementIndex(index); return node(index).item; } public E set (int index, E element) { checkElementIndex(index); Node <E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; } public void add (int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } public E remove (int index) { checkElementIndex(index); return unlink(node(index)); } private boolean isElementIndex (int index) { return index >= 0 && index < size; } private boolean isPositionIndex (int index) { return index >= 0 && index <= size; } private String outOfBoundsMsg (int index) { return "Index: " +index+", Size: " +size; } private void checkElementIndex (int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } private void checkPositionIndex (int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException (outOfBoundsMsg(index)); } Node <E> node (int index) { if (index < (size >> 1 )) { Node <E> x = first; for (int i = 0 ; i < index; i++) x = x.next; return x; } else { Node <E> x = last; for (int i = size - 1 ; i > index; i--) x = x.prev; return x; } } public int indexOf (Object o) { int index = 0 ; if (o == null ) { for (Node <E> x = first; x != null ; x = x.next) { if (x.item == null ) return index; index++; } } else { for (Node <E> x = first; x != null ; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1 ; } public int lastIndexOf (Object o) { int index = size; if (o == null ) { for (Node <E> x = last; x != null ; x = x.prev) { index--; if (x.item == null ) return index; } } else { for (Node <E> x = last; x != null ; x = x.prev) { index--; if (o.equals(x.item)) return index; } } return -1 ; } public E peek () { final Node <E> f = first; return (f == null ) ? null : f.item; } public E element () { return getFirst(); } public E poll () { final Node <E> f = first; return (f == null ) ? null : unlinkFirst(f); } public E remove () { return removeFirst(); } public boolean offer (E e) { return add(e); } public boolean offerFirst (E e) { addFirst(e); return true ; } public boolean offerLast (E e) { addLast(e); return true ; } public E peekFirst () { final Node <E> f = first; return (f == null ) ? null : f.item; } public E peekLast () { final Node <E> l = last; return (l == null ) ? null : l.item; } public E pollFirst () { final Node <E> f = first; return (f == null ) ? null : unlinkFirst(f); } public E pollLast () { final Node <E> l = last; return (l == null ) ? null : unlinkLast(l); } public void push (E e) { addFirst(e); } public E pop () { return removeFirst(); } public boolean removeFirstOccurrence (Object o) { return remove(o); } public boolean removeLastOccurrence (Object o) { if (o == null ) { for (Node <E> x = last; x != null ; x = x.prev) { if (x.item == null ) { unlink(x); return true ; } } } else { for (Node <E> x = last; x != null ; x = x.prev) { if (o.equals(x.item)) { unlink(x); return true ; } } } return false ; } public ListIterator <E> listIterator (int index) { checkPositionIndex(index); return new ListItr (index); } private class ListItr implements ListIterator <E> { private Node<E> lastReturned; private Node<E> next; private int nextIndex; private int expectedModCount = modCount; ListItr(int index) { next = (index == size) ? null : node(index); nextIndex = index; } public boolean hasNext () { return nextIndex < size; } public E next () { checkForComodification(); if (!hasNext()) throw new NoSuchElementException (); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } public boolean hasPrevious () { return nextIndex > 0 ; } public E previous () { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException (); lastReturned = next = (next == null ) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex () { return nextIndex; } public int previousIndex () { return nextIndex - 1 ; } public void remove () { checkForComodification(); if (lastReturned == null ) throw new IllegalStateException (); Node <E> lastNext = lastReturned.next; unlink(lastReturned); if (next == lastReturned) next = lastNext; else nextIndex--; lastReturned = null ; expectedModCount++; } public void set (E e) { if (lastReturned == null ) throw new IllegalStateException (); checkForComodification(); lastReturned.item = e; } public void add (E e) { checkForComodification(); lastReturned = null ; if (next == null ) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } public void forEachRemaining (Consumer<? super E> action) { Objects.requireNonNull(action); while (modCount == expectedModCount && nextIndex < size) { action.accept(next.item); lastReturned = next; next = next.next; nextIndex++; } checkForComodification(); } final void checkForComodification () { if (modCount != expectedModCount) throw new ConcurrentModificationException (); } } private static class Node <E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this .item = element; this .next = next; this .prev = prev; } } public Iterator <E> descendingIterator () { return new DescendingIterator (); } private class DescendingIterator implements Iterator <E> { private final ListItr itr = new ListItr (size()); public boolean hasNext () { return itr.hasPrevious(); } public E next () { return itr.previous(); } public void remove () { itr.remove(); } } @SuppressWarnings("unchecked") private LinkedList <E> superClone () { try { return (LinkedList<E>) super .clone(); } catch (CloneNotSupportedException e) { throw new InternalError (e); } } public Object clone () { LinkedList <E> clone = superClone(); clone.first = clone.last = null ; clone.size = 0 ; clone.modCount = 0 ; for (Node <E> x = first; x != null ; x = x.next) clone.add(x.item); return clone; } public Object [] toArray () { Object [] result = new Object [size]; int i = 0 ; for (Node <E> x = first; x != null ; x = x.next) result[i++] = x.item; return result; } @SuppressWarnings("unchecked") public <T> T [] toArray (T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0 ; Object [] result = a; for (Node <E> x = first; x != null ; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null ; return a; } private static final long serialVersionUID = 876323262645176354L ; private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(size); for (Node <E> x = first; x != null ; x = x.next) s.writeObject(x.item); } @SuppressWarnings("unchecked") private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); int size = s.readInt(); for (int i = 0 ; i < size; i++) linkLast((E)s.readObject()); } @Override public Spliterator <E> spliterator () { return new LLSpliterator <>(this , -1 , 0 ); } static final class LLSpliterator <E> implements Spliterator <E> { static final int BATCH_UNIT = 1 << 10 ; static final int MAX_BATCH = 1 << 25 ; final LinkedList<E> list; Node<E> current; int est; int expectedModCount; int batch; LLSpliterator(LinkedList<E> list, int est, int expectedModCount) { this .list = list; this .est = est; this .expectedModCount = expectedModCount; } final int getEst () { int s; final LinkedList<E> lst; if ((s = est) < 0 ) { if ((lst = list) == null ) s = est = 0 ; else { expectedModCount = lst.modCount; current = lst.first; s = est = lst.size; } } return s; } public long estimateSize () { return (long ) getEst(); } public Spliterator <E> trySplit () { Node<E> p; int s = getEst(); if (s > 1 && (p = current) != null ) { int n = batch + BATCH_UNIT; if (n > s) n = s; if (n > MAX_BATCH) n = MAX_BATCH; Object [] a = new Object [n]; int j = 0 ; do { a[j++] = p.item; } while ((p = p.next) != null && j < n); current = p; batch = j; est = s - j; return Spliterators.spliterator(a, 0 , j, Spliterator.ORDERED); } return null ; } public void forEachRemaining (Consumer<? super E> action) { Node<E> p; int n; if (action == null ) throw new NullPointerException (); if ((n = getEst()) > 0 && (p = current) != null ) { current = null ; est = 0 ; do { E e = p.item; p = p.next; action.accept(e); } while (p != null && --n > 0 ); } if (list.modCount != expectedModCount) throw new ConcurrentModificationException (); } public boolean tryAdvance (Consumer<? super E> action) { Node<E> p; if (action == null ) throw new NullPointerException (); if (getEst() > 0 && (p = current) != null ) { --est; E e = p.item; current = p.next; action.accept(e); if (list.modCount != expectedModCount) throw new ConcurrentModificationException (); return true ; } return false ; } public int characteristics () { return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; } } }
总结 :
② 从LinkedList的实现方式中可以发现,它不存在容量不足 的问题
③ LinkedList的克隆函数clone(),通过源码可知是将全部元素克隆到一个新的LinkedList对象中 (对于对象本身来说是浅复制)
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class Test { public static void main (String[] args) { Test test = new Test (); LinkedList <Student> list = new LinkedList <>(); list.add(test.new Student ("Test" , 5 )); list.add(test.new Student ("Test2" , 10 )); List <Student> clonedList = (List<Student>) list.clone(); clonedList.get(0 ).setAge(100 ); System.out.println(list); System.out.println(clonedList); } class Student { private String name; private int age; public Student (String name, int age) { this .name = name; this .age = age; } @Override public String toString () { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}' ; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } } }
在list中创建两个Student元素, 克隆list为clonedList, 并在clonedList中修改元素, 则原list中元素也被修改
所以想要深复制一个LinkedList还是写代码一个一个复制加入吧!
④ LinkedList实现了java.io.Serializable : 当写入到输出流时,先写入容量,再依次写入每一个节点保护的值;当读出输入流时,先读取容量,再依次读取每一个元素
四. LinkedList遍历方式 LinkedList整表遍历 LinkedList支持多种整表遍历方式, 但不建议采用随机访问的方式去遍历LinkedList ,而采用逐个遍历的方式
① 通过迭代器遍历 , 即通过Iterator去遍历
1 2 for (Iterator iter = list.iterator(); iter.hasNext();) iter.next();
② 通过快速随机访问遍历 LinkedList
1 2 3 for (int i = 0 , size = list.size(); i < size; i++) { list.get(i); }
③ 通过forEach语法 (内部还是迭代器)来遍历LinkedList
1 2 for (Integer integ:list) ;
④ 通过pollFirst()来遍历
1 2 while (list.pollFirst() != null ) ;
⑤ 通过pollLast()来遍历
1 2 while (list.pollLast() != null ) ;
⑥ 通过removeFirst()来遍历
1 2 3 4 5 try { while (list.removeFirst() != null ) ; } catch (NoSuchElementException e) { }
⑦ 通过removeLast()来遍历
1 2 3 4 5 try { while (list.removeLast() != null ) ; } catch (NoSuchElementException e) { }
测试这些遍历方式效率的代码如下 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 public class Test { public static void main (String[] args) { LinkedList <Integer> list = getLinkedList(); iteratorLinkedListThruIterator(list); iteratorLinkedListThruForeach(list); iteratorThroughFor2(list); iteratorThroughPollFirst(list); iteratorThroughPollLast(list); iteratorThroughRemoveFirst(list); iteratorThroughRemoveLast(list); } private static LinkedList <Integer> getLinkedList () { LinkedList <Integer> list = new LinkedList <>(); for (int i = 0 ; i < 100000 ; i++) { list.addLast(i); } return list; } private static void iteratorLinkedListThruIterator (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); for (Iterator iter = list.iterator(); iter.hasNext(); ) { iter.next(); } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iterator LinkedList by Iterator:" + interval + " ms" ); } private static void iteratorLinkedListThruForeach (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); int size = list.size(); for (int i = 0 ; i < size; i++) { list.get(i); } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iterator LinkedList by forEach:" + interval + " ms" ); } private static void iteratorThroughFor2 (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); for (Integer integ : list) { ; } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iteratorThroughFor2:" + interval + " ms" ); } private static void iteratorThroughPollFirst (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); while (list.pollFirst() != null ) { ; } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iterator by PollFirst:" + interval + " ms" ); } private static void iteratorThroughPollLast (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); while (list.pollLast() != null ) { ; } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iterator by PollLast:" + interval + " ms" ); } private static void iteratorThroughRemoveFirst (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); try { while (list.removeFirst() != null ) { ; } } catch (NoSuchElementException e) { } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iterator by RemoveFirst:" + interval + " ms" ); } private static void iteratorThroughRemoveLast (LinkedList<Integer> list) { if (list == null ) { return ; } long start = System.currentTimeMillis(); try { while (list.removeLast() != null ) { ; } } catch (NoSuchElementException e) { } long end = System.currentTimeMillis(); long interval = end - start; System.out.println("iterator by RemoveLast:" + interval + " ms" ); } }
执行结果 :
1 2 3 4 5 6 7 iterator LinkedList by Iterator:6 ms iterator LinkedList by forEach:3333 ms iteratorThroughFor2:5 ms iterator by PollFirst:3 ms iterator by PollLast:0 ms iterator by RemoveFirst:0 ms iterator by RemoveLast:0 ms
结论:
使用removeFist()或removeLast()效率最高, 但它们会删除原始数据;
若单纯只读取,而不删除,应该使用forEach语法(Iterator)遍历方式;
无论如何,千万不要通过随机访问去遍历LinkedList
遍历子列表 ① 正序遍历子列表
LinkedList中提供了public ListIterator<E> listIterator(int index)方法, 可以指定返回从某个index开始的迭代器:
1 2 3 4 5 6 7 8 9 LinkedList <Integer> list = new LinkedList <>();for (int i = 0 ; i < 10 ; i++) { list.add(i); } Iterator <Integer> subList = list.listIterator(5 );while (subList.hasNext()) { System.out.println(subList.next()); }
补充:
① 也可以使用List接口中定义的public List<E> subList(int fromIndex, int toIndex)方法:
1 2 3 4 5 6 7 8 9 10 >LinkedList <Integer> list = new LinkedList <>(); >for (int i = 0 ; i < 10 ; i++) { list.add(i); >} >List <Integer> subList = list.subList(5 , 8 ); >subList.set(2 , 100 ); >System.out.println(subList); >System.out.println(list);
但是这个方法实际上继承自AbstractList的实现, LinkedList中并未重写, 且对于subList的修改会影响原list!
② 反序遍历子列表
LinkedList中还提供了DescendingIterator类, 用于反序遍历列表(但不可指定从某个index开始)
使用descendingIterator()方法返回一个反序遍历的Iterator:
1 2 3 4 5 6 7 8 9 LinkedList <Integer> list = new LinkedList <>();for (int i = 0 ; i < 10 ; i++) { list.add(i); } Iterator <Integer> subList = list.descendingIterator();while (subList.hasNext()) { System.out.println(subList.next()); }
使用Spliterator并发遍历 与ArrayList类似, 在LinkedList中也存在一个实现了Spliterator接口的内部类: LLSpliterator 用于并发遍历整个表以提高性能, 使用方法与ArrayList类似
例如: 创建一个长度为100的list,如果下标能被10整除,则该位置数值跟下标相同,否则值为aaaa。然后多线程遍历list,取出list中的数值(字符串aaaa不要)进行累加求和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package top.jasonkayzk;import java.util.*;import java.util.concurrent.atomic.AtomicInteger;import java.util.regex.Pattern;public class Test { static List<String> list; static AtomicInteger count; public static void main (String[] args) throws InterruptedException { list = new LinkedList <>(); for (int i = 1 ; i <= 100 ; i++) { if (i % 10 == 0 ) { list.add(Integer.toString(i)); } else { list.add("aaaa" ); } } Spliterator <String> spliterator = list.spliterator(); count = new AtomicInteger (0 ); Spliterator <String> s1 = spliterator.trySplit(); Spliterator <String> s2 = spliterator.trySplit(); Thread main = new Thread (new Task (spliterator)); Thread t1 = new Thread (new Task (s1)); Thread t2 = new Thread (new Task (s2)); main.start(); t1.start(); t2.start(); t1.join(); t2.join(); main.join(); System.out.println(count); } public static boolean isInteger (String str) { Pattern pattern = Pattern.compile("^[-+]?[\\d]*$" ); return pattern.matcher(str).matches(); } static class Task implements Runnable { private Spliterator<String> spliterator; public Task (Spliterator<String> spliterator) { this .spliterator = spliterator; } @Override public void run () { String threadName = Thread.currentThread().getName(); System.out.println("线程" + threadName + "开始运行-----" ); spliterator.forEachRemaining(x -> { if (isInteger(x)) { count.addAndGet(Integer.parseInt(x)); System.out.println("数值:" + x + "------" + threadName); } }); System.out.println("线程" + threadName + "运行结束-----" ); } } }
以上代码与之前ArrayList中的代码几乎相同(只是在创建List时使用的是new LinkedList<>()而已)
执行结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 线程Thread-1开始运行----- 线程Thread-2开始运行----- 线程Thread-0开始运行----- 线程Thread-0运行结束----- Exception in thread "Thread-2" java.lang.NullPointerException at top.jasonkayzk.Test$Task.run(Test.java:65) at java.base/java.lang.Thread.run(Thread.java:834) 数值:10------Thread-1 数值:20------Thread-1 数值:30------Thread-1 数值:40------Thread-1 数值:50------Thread-1 数值:60------Thread-1 数值:70------Thread-1 数值:80------Thread-1 数值:90------Thread-1 数值:100------Thread-1 线程Thread-1运行结束----- 550
结果是正确的, 但是爆出了NPE的错误!
但是将100增大, 比如到10000, 则不会报错了!
原因在于:
LLSpliterator和在ArrayList中实现的ArrayListSpliterator的实现方式还是有所区别的
在LLSpliterator中定义了BATCH_UNIT和batch变量:
1 2 3 4 5 static final int BATCH_UNIT = 1 << 10 ; static final int MAX_BATCH = 1 << 25 ; int est; int expectedModCount; int batch;
而在源码的trySplit()方法中可以看到在LLSpliterator中是以batch(或者说是j)来进行拆分的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public Spliterator <E> trySplit () { Node<E> p; int s = getEst(); if (s > 1 && (p = current) != null ) { int n = batch + BATCH_UNIT; if (n > s) n = s; if (n > MAX_BATCH) n = MAX_BATCH; Object [] a = new Object [n]; int j = 0 ; do { a[j++] = p.item; } while ((p = p.next) != null && j < n); current = p; batch = j; est = s - j; return Spliterators.spliterator(a, 0 , j, Spliterator.ORDERED); } return null ; }
并且这个batch固定等于: batch + BATCH_UNIT, 而batch只能被初始化为0(没找到setter方法), 所以batch只能固定为1024!
说明: 在LLSpliterator中
① batch限定了每次差分的大小, 而batch并未通过构造函数或者Setter暴露, 所以只能为: BATCH_UNIT(1024), 即每一批次固定为1024个元素
② MAX_BATCH规定了可处理的总批数, 所以LLSpliterator可处理的最多元素是: MAX_BATCH x BATCH_UNIT
综上, 也就不难得出, 为什么当list长度为100时会报错了: 因为开了四个线程, 但是因为每一批固定为1024个, 所以其实另外三个线程都被分为了null, 所以报出NPE
总结: ArrayListSpliterator和LLSpliterator的底层实现还是不同的
我个人觉得主要是因为LinkedList的随机访问能力远不如ArrayList, 所以差分多个其实性能远远不如ArrayList好, 所以Java官方才这样设计的, 毕竟你拆分LinkedList时, 基本上已经遍历了整个链表了
所以使用Spliterator多线程并发操作List的时候, 一定要选择ArrayList!