一、前景提要

本人经历了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 }

这里就是去除了所有不属于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 }

结果:

(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 }

结果:

(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 }
  • 合并测试

     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 }
  • 测试结果
  • 补充
    涉及频繁增删使用linkedlist,涉及安全使用Vector

    这就是初步的改写和合并查阅方案,希望对你有帮助,感谢支持,如果有有疑问请留言,我们共同讨论

JDK源码阅读-------自学笔记(二十一)(java.util.ArrayList详细版集合类)的更多相关文章

  1. JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)

    Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...

  2. JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)

    一.实现get方法 1.一般思维实现思路 1).将对象的值放入一个中间变量中. 2).遍历索引值,将中间量的下一个元素赋值给中间量. 3).返回中间量中的元素值. 4).示意图 get(2),传入角标 ...

  3. JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)

    一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...

  4. JDK源码阅读-------自学笔记(五)(浅析数组)

    一.数组基础 1.定义和特点 数组也可以看做是对象,数组变量属于引用类型,数组中每个元素相当于该队形的成员变量,数组对象存储在堆中. 2.初始化数组 常用类初始化 // 整型初始化 int[] int ...

  5. 利用IDEA搭建JDK源码阅读环境

    利用IDEA搭建JDK源码阅读环境 首先新建一个java基础项目 基础目录 source 源码 test 测试源码和入口 准备JDK源码 下图框起来的路径就是jdk的储存位置 打开jdk目录,找到sr ...

  6. JDK源码阅读-ByteBuffer

    本文转载自JDK源码阅读-ByteBuffer 导语 Buffer是Java NIO中对于缓冲区的封装.在Java BIO中,所有的读写API,都是直接使用byte数组作为缓冲区的,简单直接.但是在J ...

  7. JDK源码阅读(三):ArraryList源码解析

    今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...

  8. JDK源码阅读(一):Object源码分析

    最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...

  9. JDK源码阅读(1)_简介+ java.io

    1.简介 针对这一个版块,主要做一个java8的源码阅读笔记.会对一些在javaWeb中应用比较广泛的java包进行精读,附上注释.对于容易混淆的知识点给出相应的对比分析. 精读的源码顺序主要如下: ...

  10. ZooKeeper源码阅读——client(二)

    原创技术文章,转载请注明:转自http://newliferen.github.io/ 如何连接ZooKeeper集群   要想了解ZooKeeper客户端实现原理,首先需要关注一下客户端的使用方式, ...

随机推荐

  1. Ryoku 的新年欢乐赛

    目录 前言 洛谷 6033 Ryoku 的探索 题目 分析 代码 洛谷 6034 Ryoku 与最初之人笔记 题目 分析O(log^2n) 代码(赛时AC) 分析O(logn) 代码(赛后) 洛谷 6 ...

  2. #SPFA#洛谷 4042 [AHOI2014/JSOI2014] 骑士游戏

    题目 分析 如果我想普通攻击1,那么必须干掉所有产生的其它怪兽,这不由得可以用一个不等式来表示, \(普攻+\sum need<法攻\) 但是所需要消灭的怪兽同样可以这样进行,所以它可能具有后效 ...

  3. #K-D Tree#洛谷 4357 [CQOI2016]K 远点对

    题目 已知平面内 \(n\) 个点的坐标,求欧氏距离下的第 \(k\) 远点对. 分析 先将\(k\)乘2转换为第\(k\)远有序点对. 由于\(O(n^2)\)即枚举一个点再枚举另一个点会超出时限, ...

  4. Wasm软件生态系统安全分析

    演讲嘉宾 | 王浩宇 回顾整理 | 廖   涛 排版校对 | 李萍萍 嘉宾简介 王浩宇,华中科技大学教授,博士生导师,华中科技大学OpenHarmony技术俱乐部主任.研究关注于新兴软件系统中的安全. ...

  5. C++ 开发者必读经典书籍推荐

    如果你正在学习C++,那么一本好的教材或参考书可以事半功倍.以下是几本我个人推荐的C++书籍或视频 C++基础 看书 C++ Primer C++程序设计语言 Effective C++ More E ...

  6. Python 数组和列表:创建、访问、添加和删除数组元素

    Python 没有内置支持数组,但可以使用 Python 列表来代替. 数组 本页将向您展示如何使用列表作为数组,但要在 Python 中使用数组,您需要导入一个库,比如 NumPy 库.数组用于在一 ...

  7. 报表 BI 选型的那些事

    前言 报表工具是一个接近 20 年的产物了 但是,直到现在,在各种数据信息化的系统中,报表工具的作用,不仅没有褪色,反而是因为信息化需求的增大.数据的增多,以及报表工具本身迭代后越来越方便好用,使得它 ...

  8. redis 简单整理——慢查询[八]

    前言 简单整理一下redis的慢查询. 正文 什么是慢查询呢? 一般存储系统就是系统在命令执行前后计算每条命令的执行时间,当超出预设阀值,就将这条命令的相关信息记录下来. 但是有人可能没有看到慢查询日 ...

  9. steam社区留言红小作文模板

    steam社区留言红小作文模板 Dear steam: Im a steam user which most play csgo.i saw i had be banned in steam comm ...

  10. Java 断言 Assert 使用教程与最佳实践

    本文收录于 Github.com/niumoo/JavaNotes,Java 系列文档,数据结构与算法! 本文收录于网站:https://www.wdbyte.com/,我的公众号:程序猿阿朗 作为一 ...