您好,登錄后才能下訂單哦!
這篇文章主要介紹“ArrayList和LinkedList源碼是什么”,在日常操作中,相信很多人在ArrayList和LinkedList源碼是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”ArrayList和LinkedList源碼是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在java中,集合這一數據結構應用廣泛,應用最多的莫過于List接口下面的ArrayList和LinkedList;
我們先說List,
1 public interface List<E> extends Collection<E> { 2 //返回list集合中元素的數量,若數量大于Integer.MAX_VALUE,則返回Integer.MAX_VALUE 3 int size(); 4 5 //判讀集合內是否沒有元素,若沒有元素返回true 6 boolean isEmpty(); 7 8 //判斷集合內是否包含指定的元素o 9 boolean contains(Object o); 10 11 //以適當的序列,返回該集合元素中的一個迭代器12 Iterator<E> iterator(); 13 14 //返回一個數組,該數組包含該集合中所有的元素(以從first到last的順序)15 Object[] toArray(); 16 17 //把集合中的元素放到數組a中,并返回18 <T> T[] toArray(T[] a); 19 20 21 //向集合末尾中添加一個元素22 boolean add(E e); 23 24 //從集合中刪除第一個出現的元素o25 boolean remove(Object o); 26 27 28 //判斷集合中是否包含 指定集合c中的所有元素29 boolean containsAll(Collection<?> c); 30 31 //將指定集合c中的所有元素都追加到 集合的末尾32 boolean addAll(Collection<? extends E> c); 33 34 //將指定集合c中的所有元素都插入到 集合中,插入的開始位置為index35 boolean addAll(int index, Collection<? extends E> c); 36 37 //將指定集合c中的所有元素從本集合中刪除38 boolean removeAll(Collection<?> c); 39 40 //本集合和 集合c的交集41 boolean retainAll(Collection<?> c); 42 43 //清除集合中的元素44 void clear(); 45 46 //比較指定對象o和本集合是否相等,只有指定對象為list,size大小和本集合size一樣,且每個元素equal一樣的情況下,才返回true47 boolean equals(Object o); 48 49 50 int hashCode(); 51 52 //返回指定位置index的元素53 E get(int index); 54 55 //將元素element設置到集合的index位置(替換)56 E set(int index, E element); 57 58 //將元素element插入到集合的index位置59 void add(int index, E element); 60 61 //移除指定位置index的元素62 E remove(int index); 63 64 //返回指定對象o在本集合中的第一個索引位置65 int indexOf(Object o); 66 67 //返回指定對象o在本集合中的最后一個索引位置68 int lastIndexOf(Object o); 69 70 //返回一個ListIterator的迭代器71 ListIterator<E> listIterator(); 72 73 //從指定位置index開始返回一個ListInterator迭代器74 ListIterator<E> listIterator(int index); 75 76 //返回從位置fromIndex開始到位置toIndex結束的子集合77 List<E> subList(int fromIndex, int toIndex); 78 }
下面我們看一看ArrayList,ArrayList是基于數組的方式來實現數據的增加、刪除、修改、搜索的。
ArrayList內部維護者兩個變量:
//該數組緩存者集合中的元素,集合的容量就是該數組的長度,elementData用transient修飾,說明在序列化時,數組elementData不在序列化范圍內。private transient Object[] elementData;//集合的大小 (集合中元素的數量)private int size;
我們再看一看ArrayList的構造器:
ArrayList( initialCapacity) { (); if (initialCapacity < 0) IllegalArgumentException("Illegal Capacity: "+ initialCapacity); .elementData = new Object[initialCapacity]; } ArrayList() { (10); } ArrayList(Collection<? E> c) { elementData = c.toArray(); size = elementData.length; (elementData.getClass() != Object[].) elementData = Arrays.copyOf(elementData, size, Object[].); }
從上面的源碼中我們看到,先將c.toArray()方法的返回值賦值給elementData,將elementData.length賦值給size, 然后進行了一個判斷 if(elementData.getClass() != Object[].class),若為真,則調用Arrays.copyOf()方法創建一個新Object[]數組,將原來elementData中的元素copy到新建的Object[]數組中,最后將新建的數組賦值給elementData。
我們看一下Arrays.copyOf()方法的源碼:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
如果newType類型為Object[].class的話,則直接創建一個長度為newLength的Object數組,否則使用Array.newInstance(Class<?> componentType, int length)方法創建一個元素類型為newType.getComponentType() (該方法返回數組中元素的類型)類型的,長度為newLength的數組,這是一個native方法,然后使用System.arraycopy() 這個native方法將original數組中的元素copy到新創建的數組中,并返回該數組。
我們注意 c.toArray might (incorrectly) not return Object[],按理說一個c.toArray()返回的是一個Object[]類型,其getClass()返回的也一定是Object[].class,那為什么還要進行逐個判斷呢? 可真實情況真的是這樣嗎?我們看下面的示例:
//定義一個父類Animalpublic class Aniaml { }//定義Animal的子類Personpublic class Person extends Aniaml{ private int id; private String name; private Date birthday; public Person(int id, String name, Date birthday) { this.id = id; this.name = name; this.birthday = birthday; } public static void main(String[] args) { test1(); test2(); test3(); } public static void test1(){ Person[] persons = {new Person(100,"lewis",new Date()),new Person(100,"lewis",new Date())}; //class [Lcom.lewis.guava.Person; Person的數組類型 System.out.println(persons.getClass()); Aniaml[] aniamls = persons; //class [Lcom.lewis.guava.Person; Person的數組類型 System.out.println(aniamls.getClass()); //class com.lewis.guava.Person Person類型 System.out.println(aniamls[0].getClass()); //java.lang.ArrayStoreException animals[]數組中實際存儲的是Peron類型,當運行時放入非Person類型時會報錯ArrayStoreException aniamls[0] = new Aniaml(); } public static void test2(){ List<String> list = Arrays.asList("abc"); //class java.util.Arrays$ArrayList 由此可見該類型不是ArrayList類型 System.out.println(list.getClass()); Object[] objects = list.toArray(); //class [Ljava.lang.String; 返回的是String數組類型 System.out.println(objects.getClass()); //java.lang.ArrayStoreException: java.lang.Object 當我們將一個Object放入String數組時報錯ArrayStoreException objects[0] = new Object(); } public static void test3(){ List<String> dataList = new ArrayList<String>(); dataList.add(""); dataList.add("hehe"); //class java.util.ArrayList System.out.println(dataList.getClass()); Object[] objects1 = dataList.toArray(); //class [Ljava.lang.Object; System.out.println(objects1.getClass()); objects1[0]="adf"; objects1[0]=123; objects1[0]=new Object(); } }
我們由上面test2()方法可知,一個List,調用list.toArray()返回的Object數組的真實類型不一定是Object數組([Ljava.lang.Object;),當我們調用 Object[] objArray = collection.toArray(), objArray并不一定能夠存放Object對象,所以上面的源碼中對這種情況進行了判斷。
我們接著看ArrayList中的方法:
add(E),當我們添加數據的時候,會遇到的一個問題就是:當里面的數組滿了,沒有可用的容量的怎么辦?
add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; true; } ensureCapacity( minCapacity) { modCount++; oldCapacity = elementData.length; (minCapacity > oldCapacity) { Object oldData[] = elementData; newCapacity = (oldCapacity * 3)/2 + 1; (newCapacity < minCapacity) newCapacity = minCapacity; elementData = Arrays.copyOf(elementData, newCapacity); } }
add( index, E element) { (index > size || index < 0) IndexOutOfBoundsException( "Index: "+index+", Size: "+size); ensureCapacity(size+1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
E set( index, E element) { RangeCheck(index); E oldValue = (E) elementData[index]; elementData[index] = element; oldValue; } RangeCheck( index) { (index >= size) IndexOutOfBoundsException( "Index: "+index+", Size: "+size); }
addAll(Collection<? E> c) { Object[] a = c.toArray(); numNew = a.length; ensureCapacity(size + numNew); // Increments modCount System.arraycopy(a, 0, elementData, size, numNew); size += numNew; numNew != 0; }
我們再看刪除的方法
remove(Object o) { (o == null) { for ( index = 0; index < size; index++) (elementData[index] == null) { fastRemove(index); true; } } { ( index = 0; index < size; index++) (o.equals(elementData[index])) { fastRemove(index); true; } } false; } fastRemove( index) { modCount++; numMoved = size - index - 1; (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work }
E remove( index) { RangeCheck(index); modCount++; E oldValue = (E) elementData[index]; numMoved = size - index - 1; (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // Let gc do its work return oldValue; }
元素的搜索:
/** *獲取指定下標index處的元素,先進行邊界檢查,然后直接返回elementData數組中對應位置index處的元素。 */public E get(int index) { RangeCheck(index); return (E) elementData[index]; }
contains(Object o) { indexOf(o) >= 0; } indexOf(Object o) { (o == null) { ( i = 0; i < size; i++) (elementData[i]==null) return i; } { ( i = 0; i < size; i++) (o.equals(elementData[i])) return i; } -1; }
與indexOf(Object o)方法類似的是 lastIndexOf(Object o)方法,不同的是 后者返回的是最后一次出現指定元素o的位置下標。
public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; }
我們再看一下ArrayList的迭代器方法如何實現的:
/** *該方法是有ArrayList的父類AbstractList持有的,返回的是一個Itr對象 */public Iterator<E> iterator() { return new Itr(); }
我們看看Itr是個什么鬼:
Itr implements Iterator<E> { cursor = 0; lastRet = -1; expectedModCount = modCount; public boolean hasNext() { return cursor != size(); } on,若modcount != expectedModCount,則拋出ConcurrentModificationException E next() { checkForComodification(); { E next = get(cursor); lastRet = cursor++; return next; } (IndexOutOfBoundsException e) { checkForComodification(); NoSuchElementException(); } } remove() { (lastRet == -1) IllegalStateException(); checkForComodification(); { AbstractList.this.remove(lastRet); (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } (IndexOutOfBoundsException e) { ConcurrentModificationException(); } } checkForComodification() { (modCount != expectedModCount) ConcurrentModificationException(); } }
我們在看一個方法trimToSize
/** *將elementData數組的容量 縮小為該集合實際包含的元素數量 */public void trimToSize() { modCount++; int oldCapacity = elementData.length; if (size < oldCapacity) { elementData = Arrays.copyOf(elementData, size); } }
使用ArrayList的注意事項:
1.ArrayList是基于數組的方式實現的,可以通過下標索引直接查找到指定位置的元素,因此查找效率高,但每次插入或刪除元素,就要大量地移動元素,插入刪除元素的效率低。
2.ArrayList在插入元素時,可能會進行數組的擴容,但是在刪除元素時卻不會減小數組的容量,如果希望減小數組的容量,可使用trimToSize方法,在查找元素要遍歷數組時,對非 null元素使用equals方法,對null元素使用==。
3.擴充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1個,也可能是一組)時,都要調用該方法來確保足夠的容量。當 容量不足以容納當前的元素個數時,就設置 新的容量為舊的容量的1.5倍加1,如果設置后的新容量還不夠,則直接新容量設置為傳入的參數(也就是所需的容 量),而后用Arrays.copyof()方法將元素拷貝到新的數組。從中 可以看出,當容量不夠時,每次增加元素,都要將原來的元 素拷貝到一個新的數組中,非常之耗時,也因此建議在事先能確定元素數量的情況下,才使用ArrayList,否則建議使用 LinkedList
4.ArrayList不是線程安全的。
接著我們看下LinkedList,LinkedList是基于雙向鏈表的方式來實現的,雙向鏈表就是集合中的每個元素都知道其前面的一個元素的位置和它后的一個元素位置。
在LinkedList中,使用一個內部類Entry來表示集合中的節點,元素的值賦值給element屬性,節點的next屬性指向下一個節點,節點的previous屬性指向前一個節點。
Entry<E> { E element; Entry<E> next; Entry<E> previous; Entry(E element, Entry<E> next, Entry<E> previous) { .element = element; .next = next; .previous = previous; } }
/** *維護了一個header節點,header節點的element屬性為null,previouse屬性為null,next屬性為null,并且header節點是不存儲數據的 */private transient Entry<E> header = new Entry<E>(null, null, null);//size表示集合包含的元素個數private transient int size = 0;/** * 構造一個空的集合,將header節點的next屬性、previous屬性都指向header節點,這樣形成了一個雙向鏈表的閉環 */public LinkedList() { header.next = header.previous = header; }
新增操作
/** *追加指定的元素e到集合尾部,其效果和方法 addLast(E e)是一致的,都是調用addBefore(e,header)方法。 */public boolean add(E e) { addBefore(e, header); return true; }/** *將數據e 插入到元素entry前面(private級別,LinkedList內部調用,意味著不能直接在自己的應用程序中調用此方法,但是可以利用反射API來間接的調用(好想沒必要這樣做)) */private Entry<E> addBefore(E e, Entry<E> entry) { //創建一個新節點newEntry,newEntry.element屬性設置為e,newEntry.next屬性設置為entry,newEntry.previous屬性設置為entry.previous Entry<E> newEntry = new Entry<E>(e, entry, entry.previous); ///將newEntry.previous節點的next屬性指向newEntry本身 newEntry.previous.next = newEntry; //將newEntry.next節點的previous屬性指向newEntry本身 newEntry.next.previous = newEntry; //將集合大小size + 1 size++; //集合大小影響次數modCount + 1 modCount++; //返回新創建的節點 return newEntry; }
下面我們看一下新增一個節點,在集合中的直接視圖情況:
假設我們一開始創建一個LinkedList時,只有一個header節點(不存儲數據的),如下圖所示:
有一個元素A1插入到了header的前面。
現在要插入一個元素A2,在執行完下面代碼后,
Entry<E> newEntry = new Entry<E>(A2, header, header.previous); //將newEntry的next屬性指向header節點,newEntry.previous屬性指向header.previous指向的節點(A1);
newEntry.previous.next = newEntry; //將newEntry.previous節點(A1)的next屬性指向newEntry,即將A1.previous屬性指向A2。
newEntry.next.previous = newEntry;//將newEntry.next節點(header)的previous屬性指向newEntry,即將header.previous屬性指向A2.
圖形變成了下面的樣子:
我們看一下LinkedList的一個帶參的構造函數:
/** *以指定集合c的迭代器返回的節點順序,構造一個包含指定集合c中所有元素的集合 */public LinkedList(Collection<? extends E> c) { //這里this()調用了LinkedList的無參構造器,使header.next=header.previous=header,即此時僅有一個header節點 this(); //調用addAll(c)方法 addAll(c); }/** *將指定集合c中所有的元素,按照其迭代器返回的順序全部追加到集合的結尾。 */public boolean addAll(Collection<? extends E> c) { return addAll(size, c); }/** *將指定集合c中所有的元素,按照其迭代器返回的順序,從指定的位置index開始全部插入到集合中。 */public boolean addAll(int index, Collection<? extends E> c) { //對參數index做邊界檢查,無效拋出IndexOutOfBoundsException if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); //將集合c 轉換為數組 Object[] a = c.toArray(); //獲取數組的長度 int numNew = a.length; //若數組的長度為0,說明數組是空的,沒有可以插入的元素,則直接返回false if (numNew==0) return false; //程序走到這,說明有可以插入的數據了,集合大小肯定會改變,因此modCount+1 modCount++; //如果入參index==size,則將header節點設置為后繼節點successor,否則調用entry(int)方法求出位置index的節點,并將該節點設置為后繼節點successor. Entry<E> successor = (index==size ? header : entry(index)); //獲取后繼節點successor的前驅節點predecessor Entry<E> predecessor = successor.previous; //循環數組中的元素,進入數據的插入 for (int i=0; i<numNew; i++) { //創建一個新節點e,將數組a的第i個元素a[i]作為新節點e的element屬性,successor作為e的next節點,predescessor作為e的previous節點 Entry<E> e = new Entry<E>((E)a[i], successor, predecessor); //將predecessor的next屬性指向新節點e predecessor.next = e; //由于還要進行后續的插入,因此將新節點e設置為前驅節點predecessor,以便繼續循環 predecessor = e; } //在循環數組中的元素插入完成后,將sucessor.previous屬性指向predecessor節點,此時已經完成了雙向鏈表的閉環了。 successor.previous = predecessor; //將集合大小size 增加numNew個 size += numNew; return true; }/** *返回指定位置index的節點 */private Entry<E> entry(int index) { //對參數index做邊界檢查,無效拋出IndexOutOfBoundsException if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); //將頭結點header設置為e Entry<E> e = header; //如果入參index小于數組大小size的一半,即index< size/2的話,則從前往后查找(從下標為0開始,到index),否則從后往前查找(從下標為size開始,到index+1) if (index < (size >> 1)) { for (int i = 0; i <= index; i++) //從前往后遍歷 e = e.next; } else { for (int i = size; i > index; i--) //從后往前遍歷 e = e.previous; } //找到后返回 return e; }
其他的添加操作:
/** *追加指定元素e到集合的結尾,效果和調用add(E e)方法是一樣的(都是調用方法addBefore(e,header)),只是該方法沒返回值 */public void addLast(E e) { addBefore(e, header); }/** *將指定元素e插入到集合的開始位置,調用方法addBefore(e,header.next)實現 */public void addFirst(E e) { //這里插入在header.next節點的前面,因此可以認為是集合的開始位置 addBefore(e, header.next); }/** *在指定位置index處插入指定的元素e */public void add(int index, E element) { //若index== size,即要追加到集合的結尾處,即插入到header之前;否則調用entry(int)方法獲取index處的節點,插入到該節點之前 addBefore(element, (index==size ? header : entry(index))); }/** *將元素element設值到位置為index處(即將原index處的值替換為element),并返回原來index處的值 */public E set(int index, E element) { Entry<E> e = entry(index); E oldVal = e.element; e.element = element; return oldVal; }/** *追加元素e到集合結尾 */public boolean offer(E e) { return add(e); }/** *將元素e插入集合的開始位置,調用addFirst(E e)方法實現 */public boolean offerFirst(E e) { addFirst(e); return true; }/** *將元素e插入集合的結束位置,調用addLast(E e)方法實現 */public boolean offerLast(E e) { addLast(e); return true; }/** *將元素e推入(push)進入棧 (LinkedList也可以當做棧使用) */public void push(E e) { addFirst(e); }
刪除操作:
/** *刪除指定元素在集合中的第一個出現的節點(下標index最小的) */public boolean remove(Object o) { //分為兩種情況:1.入參o 為null,使用== 比較 2.入參o不為null,使用equals比較 //若o == null if (o==null) { //從前往后開始遍歷(從header節點的下一個節點開始) for (Entry<E> e = header.next; e != header; e = e.next) { //使用 == 比較 if (e.element==null) { //找到第一個為null的節點,調用方法remove(Entry e)刪除該節點 remove(e); //返回true,表示刪除成功 return true; } } } else { //從前往后開始遍歷(從header節點的下一個節點開始) for (Entry<E> e = header.next; e != header; e = e.next) { //使用 equals 比較 if (o.equals(e.element)) { //找到第一個equals為true的節點,調用方法remove(Entry e)刪除該節點 remove(e); //返回true,表示刪除成功 return true; } } } //返回false,表示沒有找到要刪除的元素o return false; }/** *刪除指定的節點e(該方法是私有的,供LinkedList內部調用,不對外提供),與ArrayList的刪除操作而言,LinkedList的刪除操作不需要進行數組的移動,而是僅僅改變下被刪除元素的前后兩元素的指向而已,因此LinkedList刪除操作效率更高。 */private E remove(Entry<E> e) { //判斷節點是否是頭結點header,我們知道header節點不存儲數據,若入參e被發現實header節點,則拋出NoSuchElementException異常。 if (e == header) throw new NoSuchElementException(); //獲取節點e的存儲的數據result E result = e.element; //將節點e的前一個節點的next屬性直接指向e的下一個節點(斷開e.previous.next=e這個鏈條) e.previous.next = e.next; //將節點e的下一個節點的previous屬性直接指向e的前一個節點(斷開e.next.previous=e這個鏈條) e.next.previous = e.previous; //將e的next屬性和previous屬性都設置為null(斷開e對前一個節點和后一個節點的指向的鏈條) e.next = e.previous = null; //將e的本身存儲的數據元素element置為null e.element = null; //size 大小減一 size--; //由于remove操作影響了集合的結構(大小),因此modCount+1 modCount++; //返回被刪除節點的存儲數據result return result; }
清除集合中的所有節點clear()方法:
/** *清除集合中所有的節點 */public void clear() { //獲取header節點的下一個節點e Entry<E> e = header.next; //只要遍歷一遍集合即可 while (e != header) { //e節點的下一個節點next Entry<E> next = e.next; //將節點e的next屬性和previous屬性都設置為null(不指向任何節點) e.next = e.previous = null; //將節點e的存儲數據元素置為null e.element = null; //將next節點設置為當前循環的節點e e = next; } //循環刪除了集合中的所有有數據的元素后,只保留header節點(不存儲數據),并組成一個閉環 header.next = header.previous = header; //由于清理了所有的元素,此時size 設置為0 size = 0; //此操作涉及到了集合結構大小的改變,因此modCount + 1 modCount++; }
查找元素在集合中的下標indexOf()方法和lastIndexOf()
/** *返回元素o在集合中首次出現的位置下標 */public int indexOf(Object o) { //維護一個位置索引index用了記錄變量了多少個元素,從0開始 int index = 0; //若o == null,則使用 == 比較,從前往后 if (o==null) { for (Entry e = header.next; e != header; e = e.next) { if (e.element==null) //找到了第一個為null的,則返回其下標index return index; //在當前循環中沒有找到,則將下標index+1 index++; } } else { //若o不為 null,則使用 equals 比較,從前往后 for (Entry e = header.next; e != header; e = e.next) { if (o.equals(e.element)) //找到了第一個equals為truel的,則返回其下標index return index; //在當前循環中沒有找到,則將下標index+1 index++; } } //遍歷完整個鏈表沒有找到的話,返回-1 return -1; }/** *返回元素o在集合中最后出現的位置下標 */public int lastIndexOf(Object o) { //維護一個位置索引index用了記錄變量了多少個元素,從最大size開始 int index = size; //若o == null,則使用 == 比較,從后往前 if (o==null) { for (Entry e = header.previous; e != header; e = e.previous) { //先將index-1 index--; if (e.element==null) //找到了第一個遇見為null的,則返回其下標index return index; } } else { //若o != null,則使用 equals 比較,從后往前 for (Entry e = header.previous; e != header; e = e.previous) { //先將index-1 index--; if (o.equals(e.element)) //找到了第一個遇見equals為truel的,則返回其下標index return index; } } //在遍歷集合一遍之后,沒有找到元素的,返回-1 return -1; }
判斷集合是否包含某個元素:
/** *判斷集合是否包含指定元素o,調用indexOf(Object o)來實現的 */public boolean contains(Object o) { return indexOf(o) != -1; }
/** *獲取指定位置index的元素,調用entry(int )來實現,entry(int)這個方法上面已講過 */public E get(int index) { return entry(index).element; }
將集合轉換為數組:
Object[] toArray() { Object[] result = Object[size]; int i = 0; (Entry<E> e = header.next; e != header; e = e.next) result[i++] = e.element; result; } <T> T[] toArray(T[] a) { (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); i = 0; Object[] result = a; (Entry<E> e = header.next; e != header; e = e.next) result[i++] = e.element; (a.length > size) a[size] = null; a; }
LinkedList的迭代器:
/** *返回一個list的迭代器,直接new了一個內部類ListItr來實現的 */public ListIterator<E> listIterator(int index) { return new ListItr(index); }/** *ListItr是LinkedList的私有內部類,實現了ListIterator接口 */private class ListItr implements ListIterator<E> { //將header設置為最近一次返回的節點 private Entry<E> lastReturned = header; //下一次要返回的節點 private Entry<E> next; //下次要返回節點的下標 private int nextIndex; //將目前修改集合結構大小的記錄數賦值給expectedModCount ,用于比較在一個線程遍歷集合時,是否有其他的線程在同步修改該集合結構 private int expectedModCount = modCount; //入參為int的構造函數 ListItr(int index) { //對入參進行邊界檢查,如無效則拋出IndexOutOfBoundsException if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); //若index < size/2,則從前往后遍歷nextIndex從0到index-1 if (index < (size >> 1)) { //將header節點的next屬性賦值給next,即下一次要返回的節點 next = header.next; //獲取指定位置的節點 for (nextIndex=0; nextIndex<index; nextIndex++) next = next.next; } else { //若index >= size/2,則從后往前遍歷nextIndex從size到index next = header; //獲取指定位置的節點 for (nextIndex=size; nextIndex>index; nextIndex--) next = next.previous; } } //根據nextIndex是否等于size來判斷是否還有沒有遍歷的節點 public boolean hasNext() { return nextIndex != size; } //獲取下一個元素 public E next() { //檢查本集合在遍歷的過程中,是否有其他線程對本集合的結構大小做了修改 checkForComodification(); //nextIndex == size,說明鏈表已經被遍歷了一遍,不存在沒有被遍歷的節點了 if (nextIndex == size) throw new NoSuchElementException(); //將next節點設置為最近一次返回的節點 lastReturned = next; //將next節點往后移動一位 next = next.next; //將nextIndex+1 nextIndex++; //返回最近一次返回節點的數據元素 return lastReturned.element; } //從后往前遍歷的過程中,判斷是否還有未被遍歷的元素 public boolean hasPrevious() { return nextIndex != 0; } //獲取上一個元素 public E previous() { //若nextIndex==0,說明在從后往前遍歷(下邊從size 到 0)過程中,已經遍歷到頭了,不存在沒有被遍歷的節點了,則拋出NoSuchElementException if (nextIndex == 0) throw new NoSuchElementException(); //將next往前移動一位,并將next節點賦值給最近返回的節點lastReturned lastReturned = next = next.previous; //將nextIndex-1 nextIndex--; //檢查本集合在遍歷的過程中,是否有其他線程對本集合的結構大小做了修改 checkForComodification(); //返回最近一次返回節點的數據元素 return lastReturned.element; } public int nextIndex() { return nextIndex; } public int previousIndex() { return nextIndex-1; } //移除當前迭代器持有的節點 public void remove() { checkForComodification(); Entry<E> lastNext = lastReturned.next; try { LinkedList.this.remove(lastReturned); } catch (NoSuchElementException e) { throw new IllegalStateException(); } if (next==lastReturned) next = lastNext; else nextIndex--; lastReturned = header; expectedModCount++; } //將當前迭代器持有的元素 替換為元素e public void set(E e) { if (lastReturned == header) throw new IllegalStateException(); checkForComodification(); lastReturned.element = e; } //在當前節點后面插入元素e public void add(E e) { checkForComodification(); lastReturned = header; addBefore(e, next); nextIndex++; expectedModCount++; } //檢查本集合在遍歷的過程中,是否有其他線程對本集合的結構大小做了修改,如果別的線程對集合做出了修改,則拋出ConcurrentModificationException final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }/** *接口ListIterator,繼承了迭代器接口Iterator */public interface ListIterator<E> extends Iterator<E> { //在遍歷集合時(按照從前往后的順序),是否還存在沒有遍歷的元素 boolean hasNext(); //返回下一個元素 E next(); //在遍歷集合時(按照從后往前的順序),是否還存在沒有遍歷的元素 boolean hasPrevious(); //返回上一個元素 E previous(); //返回下一個元素的位置下標 int nextIndex(); //返回上一個元素的位置下標 int previousIndex(); //刪除正在遍歷的元素 void remove(); //將正在遍歷的元素替換為 元素e void set(E e); //插入元素e void add(E e); }
使用LinkedList的注意事項:
1.LinkedList是基于雙向鏈表實現的。
2.LinkedList在插入元素時,必須創建Entry對象,并修改相應元素的前后元素的引用;在查找元素時,必須遍歷鏈表;在刪除元素時,遍歷鏈表找到要刪除的元素,修改被刪除元素的前后元素的引用;
到此,關于“ArrayList和LinkedList源碼是什么”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。