1. 程式人生 > >日常學習隨筆-自定義了一個MyArrayListDefin集合(數組擴容+叠代器+JDK1.8新方法+詳細說明)

日常學習隨筆-自定義了一個MyArrayListDefin集合(數組擴容+叠代器+JDK1.8新方法+詳細說明)

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> implements
Iterable<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 public
MyArrayListDefin(){ 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新方法+詳細說明)