日常学习随笔-自定义了一个MyArrayListDefin集合(数组扩容+迭代器+JDK1.8新方法+详细说明)

Posted 小风微灵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常学习随笔-自定义了一个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
集合中的for

以上是关于日常学习随笔-自定义了一个MyArrayListDefin集合(数组扩容+迭代器+JDK1.8新方法+详细说明)的主要内容,如果未能解决你的问题,请参考以下文章

日常学习随笔-自定义了一个MyArrayListDefin集合(数组扩容+迭代器+JDK1.8新方法+详细说明)

Vue 学习随笔四 - 路由介绍

程序员委托事件学习随笔

linux 学习随笔-系统日常管理常用命令

日常学习随笔-数组单链表双链表三种形式实现栈结构的基本操作

日常学习随笔-数组单链表双链表三种形式实现队列结构的基本操作(源码注释)