java ArrayList 實現,javaarraylist實現
關於ArrayList的實現和原理,原文出處:http://www.cnblogs.com/ITtangtang/p/3948555.html
我覺得他寫的非常好,真的很好.
做一個記錄和總結吧
public class arraylist<E> { /** * 存放集合的元素 * */ private transient Object[] elementData; /** 元素的大小 */ private int size;
定義了一個泛型類,一個object的數組和一個私人變數來記錄該集合的元素數量,原文多了一個私人變數,我也不知道幹嘛用的,作者也沒解釋也沒提及到,我沒使用也沒事
1 /** 2 * 根據指定大小初始化 3 * @param initialCapacity 4 */ 5 public arraylist(int initialCapacity){ 6 super(); 7 if(initialCapacity<=0){ 8 //拋異常 9 throw new IllegalArgumentException("初始化參數不能小於0");10 }else{11 //初始化數組12 this.elementData=new Object[initialCapacity];13 }14 }15 /**16 * 預設初始化17 */18 public arraylist(){19 this(10);20 }21 /**22 * 根據一個集合類初始化23 * @param c 一個必須繼承了Collection介面的類24 */25 public arraylist(Collection<? extends E> c){26 //初始化27 elementData=c.toArray();28 size=elementData.length;29 //如果不是任意類型的數組就轉換Objec類型30 if (elementData.getClass() != Object[].class){31 elementData=Arrays.copyOf(elementData,size, Object[].class);32 }33 }34
3個初始化方法,根據預設大小進行數組的初始化,給定大小初始化和傳遞一個繼承了Collection集合介面的類進行轉換賦值初始化
1 /** 2 * 擴容集合 3 * @param minCapacity 4 */ 5 public void ensureCapacity(int minCapacity){ 6 /** 當前數組的大小 */ 7 int oldCapacity = elementData.length; 8 if (minCapacity > oldCapacity) { 9 /**10 * oldData 雖然沒有被使用,但是這是關於記憶體管理的原因和Arrays.copyOf()方法不是安全執行緒11 * oldData在if的生命週期內引用elementData這個變數,所以不會被GC回收掉12 * 當Arrays.copyOf()方法在把elementData複製到newCapacity時,就可以防止新的記憶體或是其他線程分配記憶體是elementData記憶體被侵佔修改13 * 當結束是離開if,oldData周期就結束被回收14 */15 Object oldData[] = elementData; 16 int newCapacity = (oldCapacity * 3)/2 + 1; //增加50%+117 if (newCapacity < minCapacity) 18 newCapacity = minCapacity; 19 //使用Arrays.copyOf把集合的元素複製並產生一個新的數組20 elementData = Arrays.copyOf(elementData, newCapacity); 21 } 22 }
這是一個核心的方法,集合的擴容,其實是對數組的擴容,minCapacity集合的大小,進行對比判斷是否應該進行擴容,使用了Arrays.copyOf()方法進行擴容,
原文有進行詳細的解釋,這個方法把第一個參數的內容複寫到一個新的數組中,數組的大小是第二個參數,並返回一個新的數組,關於oldData的變數上文有詳細的注釋
1 /**2 * 檢查索引是否出界3 * @param index4 */5 private void RangeCheck(int index){6 if(index > size || index < 0){7 throw new IndexOutOfBoundsException("下標超出,Index: " + index + ", Size: " +size);8 }9 }
一個下標的檢索是否出 1 /**
2 * 添加元素 3 * 將指定的元素添加到集合的末尾 4 * @param e 添加的元素 5 * @return 6 */ 7 public boolean add(E e){ 8 ensureCapacity(size+1); 9 elementData[size]=e;10 size++;11 return true;
12 }
添加元素,先進行擴容,在賦值,然後元素加一,注意 size+1 欄位size並沒有加一,這裡進行的是算術的運算,所以在後面才需要進行自增
1 /** 2 * 添加元素 3 * 將元素添加到指定的位置 4 * @param index 指定的索引下標 5 * @param element 元素 6 * @return 7 */ 8 public boolean add(int index, E element){ 9 RangeCheck(index);10 ensureCapacity(size+1);11 // 將 elementData中從Index位置開始、長度為size-index的元素, 12 // 拷貝到從下標為index+1位置開始的新的elementData數組中。 13 // 即將當前位於該位置的元素以及所有後續元素右移一個位置。14 System.arraycopy(elementData, index, elementData, index+1, size-index);15 elementData[index]=element;16 size++;//元素加一17 return true;18 }
這裡不同的是 System.arraycopy(elementData, index, elementData, index+1, size-index);
這是一個c的內部方法,詳細的原文有解釋,這裡就不說了,這個也是整個ArrayList的核心所在,也Arrays.copyOf()的內部實現原理
1 /** 2 * 添加全部元素 3 * 按照指定collection的迭代器所返回的元素順序,將該collection中的所有元素添加到此列表的尾部。 4 * @param c 5 * @return 6 */ 7 public boolean addAll(Collection < ? extends E>c){ 8 Object[] newElement=c.toArray(); 9 int elementLength=newElement.length;10 ensureCapacity(size+elementLength);11 //從newElement 0的下標開始,elementLength個元素,elementData size的下標 12 System.arraycopy(newElement, 0, elementData, size, elementLength);13 size+=elementLength;14 return elementLength!=0;15 }
基本上其他方法都只是根據不同的情況進行不同的處理,比如通過介面把資料對象傳遞進來然後擷取長度進行擴容,在把資料使用System,arraycopy複製到新的數組中
1 /** 2 * 指定位置,添加全部元素 3 * @param index 插入位置的下標 4 * @param c 插入的元素集合 5 * @return 6 */ 7 public boolean addAll(int index, Collection<? extends E> c){ 8 if(index > size || index < 0){ 9 throw new IndexOutOfBoundsException("Index: " + index + ", Size: " +size); 10 } 11 Object[] newElement=c.toArray(); 12 int elementLength=newElement.length; 13 ensureCapacity(size+elementLength); 14 int numMoved=size-index; 15 //判斷插入的位置是否在數組中間 16 if(numMoved>0){ 17 //把index插入位置的後面的所有元素往後移 18 //elementData index下標開始的numMoved個元素插入到elementData 的index+elementLength位置 19 System.arraycopy(elementData, index, elementData, index+elementLength, numMoved); 20 } 21 //把newElement裡從0開始的elementLength個元素添加到elementData index開始的位置 22 System.arraycopy(newElement, 0, elementData, index, elementLength); 23 size += elementLength; 24 return elementLength != 0; 25 } 26 27 /** 28 * 指定下標賦值 29 * @param index 30 * @param element 31 * @return 32 */ 33 public E set(int index,E element){ 34 RangeCheck(index); 35 E oldElement=(E)elementData[index]; 36 elementData[index]=element; 37 return oldElement; 38 } 39 40 /** 41 * 根據下標取值 42 * @param index 43 * @return 44 */ 45 public E get(int index){ 46 RangeCheck(index); 47 return (E)elementData[index]; 48 } 49 50 /** 51 * 根據下標移除元素 52 * @param index 53 */ 54 public E remove(int index){ 55 RangeCheck(index); 56 E oldElement=(E)elementData[index]; 57 /** 移除的下標後面的元素數量 */ 58 int numMoved=size-index-1; 59 //如果在數組範圍內就進行移動 60 if(numMoved>0) 61 System.arraycopy(elementData, index+1, elementData, index, numMoved); 62 //移除 63 elementData[--size]=null; 64 return oldElement; 65 } 66 67 /** 68 * 根據元素移除 69 * @param obj 70 * @return 71 */ 72 public boolean remove(Object obj){ 73 //Arraylist允許存放null,所以也要進行判斷處理 74 if(obj==null){ 75 for(int index=0;index<size;index++){ 76 if(elementData[index]==null){ 77 remove(index); 78 return true; 79 } 80 } 81 }else{ 82 for(int index=0;index<size;index++){ 83 if(obj.equals(elementData[index])){ 84 remove(index); 85 return true; 86 } 87 } 88 } 89 return false; 90 } 91 92 /** 93 * 根據下標移除指定範圍內的元素 94 * @param fromIndex 開始 95 * @param toIndex 結束 96 */ 97 protected void removeRange(int fromIndex, int toIndex){ 98 RangeCheck(fromIndex); 99 RangeCheck(toIndex);100 //要移動的元素數101 int numMoved = size - toIndex; 102 //把toIndex後面的元素移動到fromIndex103 System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);104 //要移除的元素數量105 int newSize=size-(toIndex-fromIndex);106 while(size!=newSize){107 elementData[--size]=null;108 } 109 }110 111 /**112 * 把數組容量調整到實際的容量113 */114 public void trimToSize(){115 int leng=elementData.length;116 if(size<leng){117 Object[] old=elementData;118 elementData=Arrays.copyOf(elementData, size);119 }120 }121 /**122 * 把集合元素轉換成數組123 * @return124 */125 public Object[] toArray(){126 return Arrays.copyOf(elementData, size);127 }128 129 public <T>T[] toArray(T[] a){130 if(a.length<size){131 return (T[]) Arrays.copyOf(elementData,size, a.getClass());132 }133 //把集合元素複製到a數組中134 System.arraycopy(elementData, 0, a, 0, size);135 if (a.length > size){136 for(int index=size;index<a.length;index++){137 a[index] = null;138 }139 }140 return a; 141 }
基本上都是對數組進行操作和使用c的方法進行賦值移動等,詳細的可以查看原文,原文中除了那個私人變數外也沒多少問題,代碼可以完美運行,這李要注意的和痛點就會是System,arraycopy和Arrayist.copy()這2個方法
和在擴容方法裡oldData這個變數的使用,這個變數真的很好,一開始我也不知道為什麼要這麼使用,在原文的末尾會進行解釋。