日常學習隨筆-自定義了一個MyArrayListDefin集合(數組擴容+叠代器+JDK1.8新方法+詳細說明)
阿新 • • 發佈:2018-05-13
fin array rgs def spl 三種 叠代 ldd ner
一、自定義了一個ArrayList的模擬集合(源碼+詳細說明)
前段時間分析了下ArrayList集合的源碼,總覺得如果不自己定義一個的話,好像缺了點什麽,所以有了如下的代碼。
代碼可以說是逐行註釋了,所以就不做過多的分析了。
類結構展示圖:
自定義集合:MyArrayListDefin.java
1 package com.xfwl.algorithmAnalysis.linklsit; 2 3 import java.util.Arrays; 4 import java.util.Iterator; 5 import java.util.NoSuchElementException;6 import java.util.Objects; 7 import java.util.Spliterator; 8 import java.util.Spliterators; 9 import java.util.function.Consumer; 10 11 /** 12 * 學習整理-模擬一個ArrayList(數組容器) 13 * @function 自定義ArrayList 14 * @author 小風微涼 15 * @time 2018-5-13 上午7:25:28 16 */ 17 public class MyArrayListDefin<T> implementsIterable<T>{ 18 /** 19 * 定義一個默認擴展容量 20 */ 21 private static final int DEFAULT_CAPACITY=10; 22 /** 23 * 當前集合的容量 24 */ 25 private int intSize; 26 /** 27 * 當前集合容器 28 */ 29 private T[] theItems; 30 /** 31 * 構造器 32 */ 33 publicMyArrayListDefin(){ 34 //重置集合初始數據 35 36 this.clear(); 37 } 38 /** 39 * 清除容器中數據 40 */ 41 private void clear(){ 42 //設置初始化數據 43 this.intSize=0; 44 //重置數組容器:默認容量設置為初始值:10 45 this.ensureCapacity(DEFAULT_CAPACITY); 46 } 47 /** 48 * 設置容器大小 49 * @param newCapacity 新容量大小 50 * 說明:這裏參考ArrayList源碼中的一套擴展規則">>" 51 */ 52 public void ensureCapacity(int newCapacity){ 53 //大小範圍檢查 54 if(newCapacity<=this.intSize){//不超過了當前容器的容量大小 55 return;//則不需要擴展容器容量 56 } 57 //數組初始值判斷 58 if(this.theItems==null){ 59 theItems=(T[]) new Object[newCapacity]; 60 return;//第一次初始化進來 61 } 62 //需要擴展容器容量 63 T[] newItems=(T[]) Arrays.copyOf(this.theItems, newCapacity,this.theItems.getClass()); 64 this.theItems=newItems; 65 //分析說明下: 66 /** 67 * Array.copyOf();內部封裝了 68 * System.arraycopy(this.theItems, 0, newItems, 0, this.theItems.length);這個方法 69 */ 70 //效果等效於=》:System.arraycopy(this.theItems, 0, newItems, 0, this.theItems.length); 71 } 72 /** 73 * 返回容器容量大小 74 * @return 75 */ 76 public int size(){ 77 return this.intSize; 78 } 79 /** 80 * 判斷容器是否為空 81 * @return 82 */ 83 public boolean isEmpty(){ 84 return this.size()==0; 85 } 86 /** 87 * 按照容器現有的數據所在的容量,瘦身容器容量 88 */ 89 public void trimCapacity(){ 90 this.ensureCapacity(this.size()); 91 } 92 /** 93 * 獲取指定位置的數據 94 * @param index 位置索引值 95 * @return 96 */ 97 public T get(int index){ 98 //範圍合法性檢查 99 if(index<0 ||index>=size()){ 100 throw new ArrayIndexOutOfBoundsException("獲取的位置不合法!"); 101 } 102 //驗證通過 103 return this.theItems[index]; 104 } 105 /** 106 * 給容器中的指定位置設置數據 107 * @param index 位置索引值 108 * @param newData 數據 109 */ 110 public void set(int index,T newData){ 111 //位置合法性判斷 112 if(index<0 || index>size()){ 113 throw new ArrayIndexOutOfBoundsException("設置的位置不合法!"); 114 } 115 //驗證通過 116 T oldData=this.theItems[index]; 117 System.out.println("集合中索引值="+index+",的數據【"+oldData+"】即將被重置為:【"+newData+"】"); 118 this.theItems[index]=newData; 119 } 120 /** 121 * 添加一個數據到集合中(追加到末尾) 122 * @param newData 數據 123 * @return true 添加成功 false 添加失敗 124 */ 125 public boolean add(T newData){ 126 this.set(this.size(), newData); 127 this.intSize++; 128 return true; 129 } 130 /** 131 * 在指定位置添加一個新數據 132 * @param index 指定的位置索引值 133 * @param newData 新的數據 134 */ 135 public void add(int index,T newData){ 136 //位置合法性檢查 137 if(this.theItems.length==this.size()){//當前數組長度剛好被占滿了,那麽就急需要擴容 138 //設置擴容規則:這裏參考ArrayList源碼中的一套擴展規則">>1" 139 int newCapacity=this.size()+this.size()>>1;//擴展量:取當前容量的一半,且向下取整 140 this.ensureCapacity(newCapacity); 141 } 142 //繼續添加新數據,同時後續位置的數據向後挪移一位 143 /** 144 * System.arraycopy(...) 145 * 參數說明: 146 * 第一位:復制的源數組 147 * 第二位:從index的索引值處開始 148 * 第三位:目標數組 149 * 第四位:復制的數據目標數組的第(index+1)索引值處開始存放 150 * 第五位:總共需要復制(this.size()-index)個數據:有效數據大小-索引值前的數據個數(包含數值處的數據) 151 */ 152 System.arraycopy(this.theItems, index, this.theItems, index+1, this.size()-index); 153 //開始設置新數據 154 this.theItems[index]=newData; 155 //當前集合容器有效數據大小+1 156 this.intSize++; 157 } 158 /** 159 * 從集合數組中移除指定位置的數據 160 * @param index 數據的位置索引值 161 */ 162 public void remove(int index){ 163 //位置的合法性檢查 164 if(index<0 || index>this.size()){ 165 throw new ArrayIndexOutOfBoundsException("要移除的數據的位置索引值不合法!"); 166 } 167 //檢查通過:要移除的位置之後的數據前移一位 168 System.arraycopy(this.theItems, index+1, this.theItems, index, this.size()-index-1); 169 //末尾的數據需要置空 170 this.theItems[this.size()-1]=null; 171 //當前有效數據大小-1 172 this.intSize--; 173 } 174 /** 175 * 從集合中移除指定的數據 176 * @param data 數據 177 */ 178 public void remove(T data){ 179 //需要找到這個數據的所在位置 180 int index=0; 181 for(T t:this.theItems){ 182 if(data==t){ 183 remove(index); 184 break; 185 } 186 index++; 187 } 188 } 189 /** 190 * 獲取叠代器的方法 191 * --用於拿到自定義的叠代器 192 */ 193 @Override 194 public Iterator<T> iterator() { 195 return new ArrayListIterator(); 196 } 197 /** 198 * JDK1.8新增的方法 199 * 循環處理集合中的數據, 200 * 此處的規則是:把當前集合的數據容器中的每一條數據,交給action來處理 201 */ 202 @Override 203 public void forEach(Consumer<? super T> action) { 204 Objects.requireNonNull(action); 205 206 for (T t : this.theItems) { 207 action.accept(t); 208 } 209 } 210 /** 211 * 打印當前集合的基本信息 212 */ 213 @Override 214 public String toString() { 215 return "集合總容量:"+this.theItems.length+",集合當前使用的容量:"+this.intSize+",下一次容量擴展值:"+(this.theItems.length>>1); 216 } 217 /** 218 * JDK1.8新增的方法 219 * splitable iterator可分割叠代器)接口是Java為了並行遍歷數據源中的元素而設計的叠代器, 220 * 這個可以類比最早Java提供的順序遍歷叠代器Iterator,但一個是順序遍歷,一個是並行遍歷。 221 */ 222 @Override 223 public Spliterator<T> spliterator() { 224 return Spliterators.spliteratorUnknownSize(iterator(), 0); 225 } 226 /** 227 * 內置一個叠代器 228 * @function 可以在自定義的叠代器中,按照自己的方式實現功能 229 * 這裏說明一下: 230 * 我們都知道:(1)數組的查詢、修改 快於新增和刪除,因為一般的刪除和新增基本上都會牽涉到數據的後移或遷移,比較消費資源。 231 * (2)單鏈表結構的查詢和修改,則慢於新增和刪除,因為一般的查詢和修改,需要從頭結點開始向下遍歷,直到找到要查詢的結點方可修改。而刪除則不需要遍歷。 232 * (3)雙鏈表結構的查詢、修改、新增、刪除則要快於單鏈表,因為雙鏈表的結點中,包含了前驅結點和後驅結點的引用,雙鏈表是環形結構。 233 * 所以呀,這個叠代器的實現就至少可以有上面三種的實現方式,三種方式代表叠代器會用三種方式去處理集合的數據,其中就優劣勢就需要仔細考量了! 234 * 特別說明:下面的叠代器使用的是第一種:數組的方式,至於後面的2種方式,在之後的學習中,會慢慢研究。 235 * @author 小風微涼 236 * @time 2018-5-13 上午9:16:56 237 * @param <T> 238 */ 239 private class ArrayListIterator<T> implements Iterator<T>{ 240 /** 241 * 當前叠代器,叠代的位置:默認值為0 242 */ 243 private int currPos=0; 244 /** 245 * 判斷是否有下一個數據 246 */ 247 @Override 248 public boolean hasNext() { 249 return currPos<MyArrayListDefin.this.size(); 250 } 251 /** 252 * 拿到集合中的下一個數據 253 */ 254 @Override 255 public T next() { 256 if(!hasNext()){ 257 throw new NoSuchElementException("已經沒有下一個數據了!"); 258 } 259 return (T) MyArrayListDefin.this.theItems[currPos++]; 260 } 261 /** 262 * 使用叠代器移除集合中的當前叠代到的數據 263 */ 264 @Override 265 public void remove() { 266 MyArrayListDefin.this.remove(--currPos); 267 } 268 /** 269 * jdk1.8新增的方法 270 * 針對性地處理下一個叠代數據 271 * 此處定義的規則是:把當前叠代器要處理的下一個數據,交給action來處理 272 */ 273 @Override 274 public void forEachRemaining(Consumer<? super T> action) { 275 action.accept(next()); 276 } 277 } 278 }
運行類:Test.java
1 package com.xfwl.algorithmAnalysis.linklsit; 2 import java.util.Arrays; 3 import java.util.Iterator; 4 import java.util.List; 5 import java.util.function.Consumer; 6 7 /** 8 * 測試自定義集合:MyArrayListDefin 9 * @function 10 * @author 小風微涼 11 * @time 2018-5-13 上午11:01:54 12 */ 13 public class Test { 14 public static void main(String[] args) { 15 //拿到一個集合對象 16 System.out.println("--------創建集合對象並添加數據-----------------"); 17 MyArrayListDefin<Object> arrList=new MyArrayListDefin<Object>(); 18 //添值 19 arrList.add(1); 20 arrList.add("String_2"); 21 arrList.add(‘3‘); 22 arrList.add("小風微涼"); 23 System.out.println(arrList.toString()); 24 //開始叠代器遍歷 25 System.out.println("--------開始打印數據-----------------"); 26 Iterator<Object> it=arrList.iterator(); 27 for(;it.hasNext();){ 28 Object eachObj=it.next(); 29 System.out.println(eachObj); 30 31 } 32 System.out.println(arrList.toString()); 33 System.out.println("--------END-----------------"); 34 //指定位置修改一個數據 35 arrList.set(0, "第一個位置的數據被修改"); 36 Iterator<Object> it2=arrList.iterator(); 37 System.out.println("--------開始打印數據-----------------"); 38 for(;it2.hasNext();){ 39 Object eachObj=it2.next(); 40 System.out.println(eachObj); 41 42 } 43 System.out.println(arrList.toString()); 44 System.out.println("--------END-----------------"); 45 //指定位置添加一個數據 46 arrList.add(1, "第二個位置的數據被添加"); 47 Iterator<Object> it3=arrList.iterator(); 48 System.out.println("--------開始打印數據-----------------"); 49 for(;it3.hasNext();){ 50 Object eachObj=it3.next(); 51 System.out.println(eachObj); 52 } 53 System.out.println(arrList.toString()); 54 System.out.println("--------END-----------------"); 55 //刪除一個指定位置的數據 56 arrList.remove(1); 57 Iterator<Object> it4=arrList.iterator(); 58 System.out.println("--------開始打印數據-----------------"); 59 for(;it4.hasNext();){ 60 Object eachObj=it4.next(); 61 System.out.println(eachObj); 62 } 63 System.out.println(arrList.toString()); 64 System.out.println("--------END-----------------"); 65 //刪除一個數據 66 arrList.remove("String_2"); 67 Iterator<Object> it5=arrList.iterator(); 68 System.out.println("--------開始打印數據-----------------"); 69 for(;it5.hasNext();){ 70 Object eachObj=it5.next(); 71 System.out.println(eachObj); 72 } 73 System.out.println(arrList.toString()); 74 System.out.println("--------END-----------------"); 75 //測試forEach()方法 76 arrList.forEach(new Consumer1("集合中的forEach方法:")); 77 //測試叠代器中的Consumer1Consumer1方法 78 Iterator<Object> it6=arrList.iterator(); 79 it6.forEachRemaining(new Consumer1("集合中叠代器的forEachRemaining方法:")); 80 81 } 82 private static class Consumer1<T> implements Consumer<T>{ 83 private String info; 84 public Consumer1(String info){ 85 this.info=info; 86 } 87 @Override 88 public void accept(T t) { 89 System.out.println(info+t); 90 } 91 @Override 92 public Consumer andThen(Consumer after) { 93 // TODO Auto-generated method stub 94 return null; 95 } 96 97 } 98 }
運行測試結果:(正常)
--------創建集合對象並添加數據----------------- 集合中索引值=0,的數據【null】即將被重置為:【1】 集合中索引值=1,的數據【null】即將被重置為:【String_2】 集合中索引值=2,的數據【null】即將被重置為:【3】 集合中索引值=3,的數據【null】即將被重置為:【小風微涼】 集合總容量:10,集合當前使用的容量:4,下一次容量擴展值:5 --------開始打印數據----------------- 1 String_2 3 小風微涼 集合總容量:10,集合當前使用的容量:4,下一次容量擴展值:5 --------END----------------- 集合中索引值=0,的數據【1】即將被重置為:【第一個位置的數據被修改】 --------開始打印數據----------------- 第一個位置的數據被修改 String_2 3 小風微涼 集合總容量:10,集合當前使用的容量:4,下一次容量擴展值:5 --------END----------------- --------開始打印數據----------------- 第一個位置的數據被修改 第二個位置的數據被添加 String_2 3 小風微涼 集合總容量:10,集合當前使用的容量:5,下一次容量擴展值:5 --------END----------------- --------開始打印數據----------------- 第一個位置的數據被修改 String_2 3 小風微涼 集合總容量:10,集合當前使用的容量:4,下一次容量擴展值:5 --------END----------------- --------開始打印數據----------------- 第一個位置的數據被修改 3 小風微涼 集合總容量:10,集合當前使用的容量:3,下一次容量擴展值:5 --------END----------------- 集合中的forEach方法:第一個位置的數據被修改 集合中的forEach方法:3 集合中的forEach方法:小風微涼 集合中的forEach方法:null 集合中的forEach方法:null 集合中的forEach方法:null 集合中的forEach方法:null 集合中的forEach方法:null 集合中的forEach方法:null 集合中的forEach方法:null 集合中叠代器的forEachRemaining方法:第一個位置的數據被修改
總結:
上面這個自定義的(仿ArrayList)集合,還有不完善的地方,原本打算是把叠代器寫成單鏈表,最好是雙鏈表結構的,但是考慮現在是學習階段,所以還是從最基礎的方式學起,後續的LinkedList的學習和自定義在嘗試使用單鏈表或多鏈表結構的叠代器。一步步來吧!
日常學習隨筆-自定義了一個MyArrayListDefin集合(數組擴容+叠代器+JDK1.8新方法+詳細說明)