zoukankan      html  css  js  c++  java
  • 日常学习随笔-自定义了一个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的学习和自定义在尝试使用单链表或多链表结构的迭代器。一步步来吧!

  • 相关阅读:
    canvas直线学习
    移动端页面练习
    IOS 本地推送(UILocalNotification)
    IOS 社交分享
    IOS 通讯录 (访问,添加,修改)
    IOS 蓝牙(GameKit、Core Bluetooth)
    IOS 获取更多的设备信息
    IOS 摇一摇的方法
    IOS Core Motion、UIAccelerometer(加速计使用)
    IOS UIDevice距离传感器(打开 关闭)
  • 原文地址:https://www.cnblogs.com/newwind/p/9031477.html
Copyright © 2011-2022 走看看