• 一点一点看JDK源码(四)java.util.ArrayList 中篇


     一点一点看JDK源码(四)java.util.ArrayList 中篇

     

    liuyuhang原创,未经允许禁止转载

    本文举例使用的是JDK8的API

    目录:一点一点看JDK源码(〇)

    1.综述

      在前篇中,对于java.util.ArrayList进行了一些源码注释,能坚持看完的估计都是神一般的存在。

      不过看源码并需要一个艰苦的过程,枯燥是很正常的。

      但是不是要一直都很枯燥,本文将对此类进行分类解析。

    2.关注点

      2.0.ArrayList是如何构成的?

        在java中,一个类的构成并不是十分复杂,列举出来,一页纸应该是足够的,我尝试下。

        2.0.1.类的定义 

          类的定义中,会声明是class,AbstractClass,或者interface。

          该类是否含有该类没有显示定义的方法,取决于向上有多少个extends的父类的存在。

          该类是否有必须实现的方法,取决于向上有多少个implments的接口的存在。

        2.0.2.类的构造

          无参构造器,带参构造器。

          构造器的方法名是和类名同名的,并且没有返回值。

          构造器不管是无参的,还是带参的,不管使用何种方式调用,

          都说明该类已经被实例化了。

          构造器内也可以写很多奇葩的代码的,当然也许是常用手段。

          在构造器内写代码以实现自己想实现的功能,相当于一些容器的init,实际上就是初始化。

        2.0.3.类的成员变量

          类的成员变量,不管如何定义,都是为该类内使用该变量提供一定的便利性

          类的成员变量,理论上就是该类内的全局变量,如果是public,或者default,protected,

          都是一种对该全局变量的开放性。

          

          成员变量,可以是static的(静态,类加载即加载),可以是final的(只允许实例化一次)

        2.0.4.类的成员方法

          类的成员方法,在不论其使用范围的情况下,都是一种方法,根据实例化使用的构造不同,

          可调用的方法范围不同。

          若使用父类构造器来接收子类实例(如:Object obj = new ArrayList()),

          会发现obj可使用的方法就变得很少了(只有Object的方法允许使用了)。如下图:

          (Object是java中所有类的基类,没有显示继承也是被继承的)

           

          因此,即使List list = new ArrayList();

          用起来,字数更少,写起来更方便,但是失去了一些功能。

          当然,简单的使用List接口来操作ArrayList实例化对象也是能满足一定要求的,

          也并非不可以使用。

        2.0.5.类的内部类

          ArrayList也有一些内部类,内部类使用成员方法进行实例化,返回的是其Implments接口的对象。

          由于其接口的方法在内部类中被复写,所以直接调用接口的方法,实际上是调用其内部类的方法。

          关于内部类,下文中有列举。

        2.0.6.类的实例化

          

          类的成员方法,随着实例化时接收的对象类型不同而不同,因为我们只能调用对象所在类提供

          的方法,所以了解ArrayList实例化后的特性,就应该使用ArrayList类来接收实例化对象。

            (上文已有,不再赘述)

        

      2.1.ArrayList提供了什么?

        提供了什么?我也并非十分清楚。在第一篇中,我认为Collection下都是容器,因此作为一个容器,

        应该提供容器应该有的特性吧,比如:

          容器存储结构(底层存储结构)

          容积计算(定容和扩容)

          增删改查方法

          特性方法(取决于储存结构和要实现的特性)

        实际上这个内容还是能够进行一些分类的。ArrayList提供了如下具体内容:

          2.1.1.常量和成员变量(无法直接访问,允许调用public方法访问)

          

            常量包括容量(size,MAX。。),底层存储结构(Object数组),序列化版本,默认储存等。

          2.1.2.构造器和初始化(可调用构造器)

            

            clinit是该类在VM装载的时候初始化用的,暂不深究。

            

            它提供了三个构造器:

              一个无参构造器ArrayList();

              两个带参构造器ArrayList(int)和ArrayList(Collection< ? extends E>)

            初学的时候总有一种迷惑的感觉,构造器无非就是实例化的,为什么要提供好几个构造器?

            这三个构造器都应该在什么时候使用呢?

              如果不确定你定义这个容器的时候,容量多大,容器内容是什么,那么应该使用无参构造器。

              如果确定你定义这个构造器的容量,或者至少容量会有多少,可以使用ArrayList(int)构造。

              因为在ArrayList底层是Object数组,数组的容量是确定的,因此每次增加内容都需要对数组

              进行扩容,扩容过程中要用新数组接收拷贝后的旧数组,所以节约计算资源效率,在能确定

              容量的情况下,最好使用定容构造器ArrayList(int)。

              如果一个容器内将直接增加数据,那么该数据最好是来自集合,也就是说向上两层的接口

              Collection之下的所有结构,都可以直接转化为ArrayList的,此时就应该选择使用

              ArrayList(Collection< ? extends E>)构造器了。

              ArrayList查询快,增删慢,这个是官方说法。快慢实际上是相对而言的,相对于谁呢?

              一般说到数组的相对性,都指的是链表。

              即,接收参数的时候,使用链表,查询和加工参数的时候,使用数组。

        2.1.3.成员方法

            ArrayList提供的成员方法很多,主要分为三类:

              1.增删改查对容器直接操作,归为一类。

              2.内部保护方法,内部处理数据中调用的方法,或只暴露给uitl包的方法,无法公开调用。

              3.其余的,对于容器特性的操作,或数据转化的操作,归为一类。

            增:add,addAll,分别对应添加单个元素和添加多个元素,其中有对于index的指定参数时,

              就是针对指定index后插入实参对象。位指定的时候默认加在末尾。

              add和addAll的时候有进行扩容判断,扩容倍数为1.5倍。(先扩容,增加后再去掉空元素)

            删:remove,removeAll,removeIf,分别对应删除单个元素,删除多个元素,按条件匹配删除。

              传入参数有指定的index(按指定index删除),双index(按指定index范围删除),

              Object(尝试找到此元素并删除,返回操作标识),Collection(删除指定集合内容)

              Predicate接口(作为filter来进行是否删除的过滤,功能类似于Compare接口)。

              有些删除的方法是带有返回值的,为boolean,或被删除的内容,应当接收,作为是否成功,

              或者操作可能需要回滚的判断。

            改:set,改指定index的值为实参对象。

            查:get,根据index获取元素。indexOf,lastIndexOf,分别正序或倒叙根据元素查index

            

            内部保护方法:

              一点点去找内部保护方法去看定义,比较麻烦,可以直接看结构。

              若该方法有红色方框标记,就是不对外公开调用的了。如下图:

            

              内部保护方法,之所以不对外公开,是因为外部调用的时候,因为考虑不周,或调用方式错误,

              或者其他原因吧,将导致有错误出现,本身可能也并非是一个完整的操作链,所以保护起来。

              如fastRemove(int)方法,util包下都可以调用,有和remove有区别在于,它省略掉了index校验

              还有rangeCheck(int),是专门用于index校验的方法,也没有必要对外公开,它属于

              其他方法,如add,addAll的一部分,这种方法抽取出来的原因,多数因为出现次数超过三次,

              因此就有必要进行重新封装了。

            特性操作:

              特性操作细分下来,也可以按照功能进行细分。如:

                清空(clear),克隆(clone),容量(size),判空(isEmpty),判断包含(cantains),

                容量优化(ensureCapacity),遍历(forEach),迭代器(iterator等),拆分(subList),

                拆分迭代(spliterator),比较排序(sort),转数组(toArray),替换(replace),

                去空(trimToSize),求交集(retainAll)

              

              其中,清空,替换,排序,容量优化,都是对ArrayList自身的操作。

              克隆,容量,判断,都是对ArrayList的一种特性或内容查询方式。

              而遍历,迭代,拆分,迭代拆分,就纯粹是数据加工,而获得其他对象了。

                其中拆分,迭代拆分,Collection接口下的Stream(聚合)都是1.8新增的了。

        2.1.4.成员方法调用的内部类

              

              ArrayList中一共有四个内部类,都是要用成员方法来调用的。内部类如下图:

              

              分别是ArrayListSpliterator,Itr,ListItr,SubList。调用的方法分别如下:

              

              具体用法,下篇再研究吧我!!

    3.其他关注点

        发现了一些奇葩关注点,不知道有用没,说下而已。

        内部类的类名展示是使用$做连接符的,mybatis中的mapper.xml要使用内部类来接收的话,该内部类必须是静态的。

        貌似VM在编译的时候是拆分编译的,但是命名不是,还是按照内部处理的,如下图:

            

        成员变量elementData前有关键字transient进行修饰,表示该变量不参与实例化,应该是作为缓存的意思了。

         

        fastRemove不仅本类可用,util包下其他的类也可以调用还。

        要使用Collection下的Stream(聚合)方法的话,必须要将ArrayList用Collection来做对象的类型来接收,然后才可以使用。

        ArrayList是线程不安全的,那么modCount真的那么有用么,就不理解了,不会只用在序列化上吧。也没见到有回滚方法。

        官方介绍中,ArrayList在List接口下,List接口下的实例定义为随机存取不支持,感觉应该写的出来吧,只是压根没写。随

        机存有影响,随机取还不容易咩?没谁会考虑自己继承ArrayList然后重新扩展吧,估计也有可能,我没见过而已。

        ArrayList中对Arrays工具类,和System类都有应用。

        retainAll调用了内部方法batchRemove,作为一个交集判断操作,使用了缓冲区elementData。

          如果要提供取交集操作该多好!!

    以上!

  • 相关阅读:
    centos Cannot allocate memory for the buffer pool
    hive query with field is json
    doubleclick video notes
    shell command
    最简单好用的免费录屏软件
    mysql export query result
    浏览器-前端网络
    vue-main.js中new vue()的解析
    webpack-从零搭建vuecli环境
    【js重学系列】call-apply-bind
  • 原文地址:https://www.cnblogs.com/liuyuhangCastle/p/9643671.html
Copyright © 2020-2023  润新知