JDK源码阅读-------自学笔记(二十一)(java.util.ArrayList详细版集合类)

一、前景提要

本人经历了IT行业的多种类型企业,外包/创业小公司/中型公司,一步步成长起来,现在可以给大家透露下为什么进大企业在IT行业是重要的:

  • 在外包公司,你要做的就是对接别人写好的接口,然后按照其要求编写一个业务的接口或者几个接口,那么你的技术也就是会写接口,而且还不是全面的,因为只会让你做那部分接口,而不是项目的全部,你对于数据库如何链接,数据库类型,数据库业务逻辑的操作都不会很清楚,所以你做的工作就是跟前段或者没有前段的做单独业务的对接,很多部分都是别人写好的,但是你又看不到,所以你根本不清楚整个业务逻辑是什么.
  • 创业公司小公司(50-500),由于人员少,企业的资金有限,所以很多时候你需要一个人兼职多项工作,多项工作,作为后端就需要会运维服务(Linux),项目部署,系统架构的基本架构搭建,后端的数据库连接,数据库设计,数据库建表,SQL语句的书写,后端三层架构的数据传递等等技能,这里需要的是一种多功能工,虽然你可以会很多技能,但是你也没有一项是精通的技能,所以你也只是一个能干活的员工而已,距离成为架构师,技术总监没有发展起来的可能性,除非你的企业三五年内能增长到几千,上万的员工,但是,按照我国的情况,几乎是不可能的.
  • 中型企业(1000-5000),这时候的项目就已经有了一些架构的雏形,否则的话项目越写越大,越写越冗余,总会写到崩溃的,而这时候很多的设计模式,就会应用到框架当中,代码的抽象,分布式设计,也会在代码中体现出来,架构上也会分出应用层和中台服务架构,会有一些设计的灵活且便于扩展的代码.

二、学习方案

  • 由于上述原因,所以开始决定学习源码,但是源码并不是那么好学,很多需要反复查看,反复推敲,才能理解源码的含义,主要经历一下三步来阅读源码:
  • 1.对接源码接口,仿写已有的代码设计,加上自己理解的标注,运行通畅,知晓源码的写法
  • 2.改写实现的接口和父类,知晓一个方法是怎么实现,然后再拓展查看其他的方法是如何实现的
  • 3.自定义源码的接口写法,以便可以独立开发出接口相同的方法,也是对算法理解的实现
  • 在源码阅读上,本人的一个想法就是当作古诗词学习,古诗词众所周知是讲的经典的词汇和语言,而在编程当中,源码就相当于古诗词在汉语中的地位,所以学习和记忆源码,就相当于学习记忆古诗词一样,而仿写就相当于为古诗词做注,就是加上自己的解释和说明,便于理解和记忆.

三、常用集合类

1、ArrayList简述

  • (1) ArrayList基于List实现的,数组长度是有限的,但是,ArrayList是可以存放人任意数量的对象,长度不受限制
  • (2),底层是数组,通过属性Object数组可知,实现了可以再分配的功能,当添加元素的时候,会自动增加,即扩容机制
  • (3)是线程不安全的,查找速度快,增删改速度慢

2、ArrayList和Collection的关系:

3、ArrayList数据结构:

  • (1)底层结构是数组, Object类型, 源码

      // 默认空数组的初始化
      private static final Object[] EMPTY_ELEMENT_DATA = {};
  • (2)因为是Object类型和使用了泛化,所以可以存储所有类型.默认数组长度是10

4、ArrayList继承关系:

二、核心方法

1、add()方法仿写

(1)ArrayList仿写(其中一些命名与现在的代码规范不符合,所以改写成了现在的规范)

  1 package com.synway.test.collections.version001.addmethod.impl;
  2     import java.util.*;
  3     /**
  4     * 仿写List
  5     *
  6     * @author liuyangos8888
  7     */
  8     public class MyArrayList<E> extends AbstractList<E>
  9         implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
 10 
 11     /**
 12      * 默认的数组大小,数组默认大小是10
 13      */
 14     private static final int DEFAULT_CAPACITY = 10;
 15 
 16 
 17     /**
 18      * 默认空数组的初始化
 19      */
 20     private static final Object[] EMPTY_ELEMENT_DATA = {};
 21 
 22 
 23     /**
 24      * 默认共享数组空间,用于区分添加第一个元素空间扩容多少
 25      */
 26     private static final Object[] DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA = {};
 27 
 28 
 29     /**
 30      * 存储ArrayList元素的数组缓冲区。
 31      * ArrayList的容量是此数组缓冲区的长度.
 32      * 添加第一个元素时,任何具有elementData == DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA的
 33      * 空ArrayList都将扩展为DEFAULT_CAPACITY。
 34      * <p>
 35      * <p>
 36      * 缓冲区数组,任何被赋值的数组都会变为容量为默认10的数组
 37      */
 38     transient Object[] elementData;
 39 
 40 
 41     /**
 42      * 包含元素的数组的大小
 43      */
 44     private int size;
 45 
 46 
 47     /**
 48      * 要分配的最大数组大小。
 49      * 一些虚拟机在数组中保留一些标题字。
 50      * 尝试分配更大的阵列可能会导致*
 51      * OutOfMemoryError:请求的阵列大小超出VM限制
 52      */
 53     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 54 
 55 
 56     /**
 57      * 构造一个初始容量为10的空数组
 58      */
 59     public MyArrayList() {
 60 
 61         this.elementData = DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA;
 62     }
 63 
 64 
 65     /**
 66      * 构造一个具有指定初始容量的空数组
 67      *
 68      * @param initialCapacity 列表的初始容量
 69      * @throws IllegalArgumentException 如果指定的初始容量为负数时
 70      */
 71     public MyArrayList(int initialCapacity) {
 72 
 73         // 输入容量大于零
 74         if (initialCapacity > 0) {
 75 
 76             this.elementData = new Object[initialCapacity];
 77 
 78             // 输入容量等于零
 79         } else if (initialCapacity == 0) {
 80 
 81             this.elementData = EMPTY_ELEMENT_DATA;
 82 
 83             // 输入容量小于零
 84         } else {
 85             throw new IllegalArgumentException("Illegal Capacity: " +
 86                     initialCapacity);
 87         }
 88     }
 89 
 90 
 91     /**
 92      * 添加元素
 93      * 将指定的元素追加到此数组的末尾
 94      *
 95      * @param e 要添加到此数组的元素
 96      * @return <tt>true</tt> 判断添加是否成功
 97      */
 98     @Override
 99     public boolean add(E e) {
100 
101         // 验证容器大小     增加计数器
102         ensureCapacityInternal(size + 1);
103         elementData[size++] = e;
104         return true;
105     }
106 
107 
108     /**
109      * 确保内部容量(验证容器长度是否符合)
110      *
111      * @param minCapacity 最小容量
112      */
113     private void ensureCapacityInternal(int minCapacity) {
114 
115         // 容器是否满了,不满返回较大值
116         if (elementData == DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA) {
117 
118             // 比较默认数组大小和最小容量
119             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
120 
121         }
122 
123         // 扩容数组大小
124         ensureExplicitCapacity(minCapacity);
125     }
126 
127 
128     /**
129      * 扩容容器大小
130      *
131      * @param minCapacity 最小容量
132      */
133     private void ensureExplicitCapacity(int minCapacity) {
134 
135         // 数组被修改次数
136         modCount++;
137 
138         // 容器大小范围限制,防止溢出
139         if (minCapacity - elementData.length > 0)
140         // 设置数组大小
141         {
142             grow(minCapacity);
143         }
144     }
145 
146 
147     /**
148      * 确保数组大小的值
149      * <p>
150      * 增加容量以确保它至少可以容纳最小容量参数指定的元素数
151      *
152      * @param minCapacity 所需的最小容量
153      */
154     private void grow(int minCapacity) {
155 
156         // 设置数组大小防止溢出
157         int oldCapacity = elementData.length;
158 
159         // 如果容器大小是10,即oldCapacity=10,则公式为: 1010(二进制)+101(右移一位)就是15
160         int newCapacity = oldCapacity + (oldCapacity >> 1);
161 
162         if (newCapacity - minCapacity < 0)
163 
164             newCapacity = minCapacity;
165 
166         if (newCapacity - MAX_ARRAY_SIZE > 0)
167 
168             newCapacity = hugeCapacity(minCapacity);
169 
170         // 老数组拷贝到新数组中
171         elementData = Arrays.copyOf(elementData, newCapacity);
172     }
173 
174 
175     /**
176      * 使用较大的容量
177      *
178      * @param minCapacity 所需的最小容量
179      * @return 容量大小
180      */
181     private static int hugeCapacity(int minCapacity) {
182 
183         // 所需容量是负值
184         if (minCapacity < 0) {
185             throw new OutOfMemoryError();
186         }
187 
188         // 所需容量是否大于最大容量
189         return (minCapacity > MAX_ARRAY_SIZE) ?
190                 Integer.MAX_VALUE :
191                 MAX_ARRAY_SIZE;
192     }
193 
194 
195     /**
196      * 返回此列表中的元素数(必须有否则元素加不进去)
197      *
198      * @return 此列表中的元素数
199      */
200     @Override
201     public int size() {
202         return size;
203     }
204 
205     /**
206      * 必须写要不加不进去数组
207      *
208      * @param index 数组索引
209      * @return 数组元素
210      */
211     @Override
212     public E get(int index) {
213         rangeCheck(index);
214 
215         return elementData(index);
216     }
217 
218     /**
219      * 核查索引
220      *
221      * @param index 此列表中的元素数
222      */
223     private void rangeCheck(int index) {
224         if (index >= size) {
225             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
226         }
227     }
228 
229     /**
230      * 数组越界
231      *
232      * @param index 数组长度
233      * @return 数组长度和尺度
234      */
235     private String outOfBoundsMsg(int index) {
236         return "Index: " + index + ", Size: " + size;
237     }
238 
239     /**
240      * @param index 数组索引
241      * @return 数组元素
242      */
243     private E elementData(int index) {
244         return (E) elementData[index];
245     }
246  
247 }
View Code

这里就是去除了所有不属于add(),方法的代码,单独查阅,实现add()方法的代码,和add()方法使用的相关内容.

(2)ArrayList仿写测试

  • 仿写ArrayList的add()方法为MyArrayList,那么,这个方法是否是正确的呢,需要通过测试,得知其是否是跟以前的版本一样是好使的.

 1 package com.synway.test.version001;
 2     import com.synway.test.collections.version001.addmethod.impl.MyArrayList;
 3     import java.util.ArrayList;
 4     import java.util.List;
 5     /**
 6      *  测试仿写的ArrayList
 7      *
 8      * @author liuyangos8888
 9     */
10 
11     public class MyListTest {
12     public static void main(String[] args) {
13 
14         getLikeArrayList();
15 
16     }
17 
18     private static void getLikeArrayList() {
19         List<String> stringList = new MyArrayList<>();
20 
21         stringList.add("A");
22         stringList.add("B");
23         stringList.add("C");
24         stringList.add("D");
25         stringList.add("E");
26 
27         System.out.println("stringList:" + stringList);
28     }
29  }
View Code

结果: 

(3)正常ArrayList调用add()

 1 java   
 2 package com.synway.test.version001;
 3 
 4 
 5 import com.synway.test.collections.version001.addmethod.impl.MyArrayList;
 6 
 7 import java.util.ArrayList;
 8 import java.util.List;
 9 
10 /**
11  * 测试仿写的ArrayList
12  *
13  * @author liuyangos8888
14  */
15 public class MyListTest {
16 
17 
18     public static void main(String[] args) {
19 
20        getOriginArrayList();
21 
22     }
23 
24     private static void getOriginArrayList() {
25         List<String> stringList = new ArrayList<>();
26 
27         stringList.add("A");
28         stringList.add("B");
29         stringList.add("C");
30         stringList.add("D");
31         stringList.add("E");
32 
33         System.out.println("stringList:" + stringList);
34     }
35 }
View Code

结果: 

(4)去除函数方法,整合查阅

  • 因为源码都是根据函数方法的优化方式写的,你会看到其每几行就跳一下,每几行就跳一下,对于查阅的开始者是很难看出整个逻辑的,所以,这里本人使用了将多个函数方法合并的方式,代码这样多个函数方法,是根据优化代码的方式(《代码整洁之道》)中描述的,最好的构造方法不要超过10行,一个函数方法如果超过10行,就要对其进行函数方法的封装,每个函数方法体内只做一件事.
  • 本身的函数调用关系:

 

  • 合并方法后
      1 package com.synway.test.collections.version001.addmethod.all;
      2 
      3 import java.util.AbstractList;
      4 import java.util.Arrays;
      5 import java.util.List;
      6 import java.util.RandomAccess;
      7 
      8 /**
      9  * 仿写List
     10  *
     11  * @author liuyangos8888
     12  */
     13 public class AllArrayList<E> extends AbstractList<E>
     14         implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
     15 
     16 
     17     /**
     18      * 默认的数组大小,数组默认大小是10
     19      */
     20     private static final int DEFAULT_CAPACITY = 10;
     21 
     22 
     23     /**
     24      * 默认空数组的初始化
     25      */
     26     private static final Object[] EMPTY_ELEMENT_DATA = {};
     27 
     28 
     29     /**
     30      * 默认共享数组空间,用于区分添加第一个元素空间扩容多少
     31      */
     32     private static final Object[] DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA = {};
     33 
     34 
     35     /**
     36      * 存储ArrayList元素的数组缓冲区。
     37      * ArrayList的容量是此数组缓冲区的长度.
     38      * 添加第一个元素时,任何具有elementData == DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA的
     39      * 空ArrayList都将扩展为DEFAULT_CAPACITY。
     40      * <p>
     41      * <p>
     42      * 缓冲区数组,任何被赋值的数组都会变为容量为默认10的数组
     43      */
     44     transient Object[] elementData;
     45 
     46 
     47     /**
     48      * 包含元素的数组的大小
     49      */
     50     private int size;
     51 
     52 
     53     /**
     54      * 要分配的最大数组大小。
     55      * 一些虚拟机在数组中保留一些标题字。
     56      * 尝试分配更大的阵列可能会导致*
     57      * OutOfMemoryError:请求的阵列大小超出VM限制
     58      */
     59     private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
     60 
     61 
     62     /**
     63      * 构造一个初始容量为10的空数组
     64      */
     65     public AllArrayList() {
     66 
     67         this.elementData = DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA;
     68     }
     69 
     70     /**
     71      * 添加元素
     72      * 将指定的元素追加到此数组的末尾
     73      *
     74      * @param e 要添加到此数组的元素
     75      * @return <tt>true</tt> 判断添加是否成功
     76      */
     77     @Override
     78     public boolean add(E e) {
     79 
     80         // 初始化容器大小
     81         int minCapacity = size + 1;
     82 
     83         // 容器是否满了,不满返回较大值
     84         if (elementData == DEFAULT_CAPACITY_EMPTY_ELEMENT_DATA) {
     85 
     86             // 比较默认数组大小和最小容量
     87             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
     88 
     89         }
     90 
     91         // 数组被修改次数
     92         modCount++;
     93 
     94         // 容器大小范围限制,防止溢出
     95         if (minCapacity - elementData.length > 0)
     96         // 设置数组大小
     97         {
     98             // 设置数组大小防止溢出
     99             int oldCapacity = elementData.length;
    100 
    101             // 如果容器大小是10,即oldCapacity=10,则公式为: 1010(二进制)+101(右移一位)就是15
    102             int newCapacity = oldCapacity + (oldCapacity >> 1);
    103 
    104             if (newCapacity - minCapacity < 0) {
    105 
    106                 newCapacity = minCapacity;
    107             }
    108 
    109             if (newCapacity - MAX_ARRAY_SIZE > 0) {
    110 
    111                 // 所需容量是负值
    112                 if (minCapacity < 0) {
    113                     throw new OutOfMemoryError();
    114                 }
    115 
    116                 // 所需容量是否大于最大容量
    117                 if (minCapacity > MAX_ARRAY_SIZE) {
    118                     newCapacity = Integer.MAX_VALUE;
    119                 } else {
    120                     newCapacity = MAX_ARRAY_SIZE;
    121                 }
    122 
    123             }
    124 
    125             // 老数组拷贝到新数组中
    126             elementData = Arrays.copyOf(elementData, newCapacity);
    127         }
    128 
    129 
    130         elementData[size++] = e;
    131         return true;
    132     }
    133 
    134     /**
    135      * 返回此列表中的元素数(必须有否则元素加不进去)
    136      *
    137      * @return 此列表中的元素数
    138      */
    139     @Override
    140     public int size() {
    141         return size;
    142     }
    143 
    144     /**
    145      * 必须写要不加不进去数组
    146      *
    147      * @param index 数组索引
    148      * @return 数组元素
    149      */
    150     @Override
    151     public E get(int index) {
    152         rangeCheck(index);
    153 
    154         return elementData(index);
    155     }
    156 
    157     /**
    158      * 核查索引
    159      *
    160      * @param index 此列表中的元素数
    161      */
    162     private void rangeCheck(int index) {
    163         if (index >= size) {
    164             throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    165         }
    166     }
    167 
    168     /**
    169      * 数组越界
    170      *
    171      * @param index 数组长度
    172      * @return 数组长度和尺度
    173      */
    174     private String outOfBoundsMsg(int index) {
    175         return "Index: " + index + ", Size: " + size;
    176     }
    177 
    178     /**
    179      * @param index 数组索引
    180      * @return 数组元素
    181      */
    182     private E elementData(int index) {
    183         return (E) elementData[index];
    184     }
    185   }
    View Code
  • 合并测试
     1 package com.synway.test.version001;
     2 
     3 
     4 import com.synway.test.collections.version001.addmethod.all.AllArrayList;
     5 import com.synway.test.collections.version001.addmethod.impl.MyArrayList;
     6 
     7 import java.util.ArrayList;
     8 import java.util.List;
     9 
    10 /**
    11  * 测试仿写的ArrayList
    12  *
    13  * @author liuyangos8888
    14  */
    15 public class AllListTest {
    16 
    17 
    18     public static void main(String[] args) {
    19 
    20         getAllArrayList();
    21     }
    22 
    23     private static void getAllArrayList() {
    24         List<String> stringList = new AllArrayList<>();
    25 
    26         stringList.add("A");
    27         stringList.add("B");
    28         stringList.add("C");
    29         stringList.add("D");
    30         stringList.add("E");
    31 
    32         System.out.println("stringList:" + stringList);
    33     }
    34 
    35 
    36 }
    View Code
  • 测试结果
  • 补充
    涉及频繁增删使用linkedlist,涉及安全使用Vector


    这就是初步的改写和合并查阅方案,希望对你有帮助,感谢支持,如果有有疑问请留言,我们共同讨论
posted @ 2020-01-13 02:41  北极的大企鹅  阅读(258)  评论(0编辑  收藏