日常学习随笔-自定义了一个MyArrayListDefin集合(数组扩容+迭代器+JDK1.8新方法+详细说明)
一、自定义了一个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的学习和自定义在尝试使用单链表或多链表结构的迭代器。一步步来吧!
研究技术需要静下心来,一点一点地深究.......