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 }
这里就是去除了所有不属于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详细版集合类)的更多相关文章
- JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)
Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...
- JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)
一.实现get方法 1.一般思维实现思路 1).将对象的值放入一个中间变量中. 2).遍历索引值,将中间量的下一个元素赋值给中间量. 3).返回中间量中的元素值. 4).示意图 get(2),传入角标 ...
- JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)
一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...
- JDK源码阅读-------自学笔记(五)(浅析数组)
一.数组基础 1.定义和特点 数组也可以看做是对象,数组变量属于引用类型,数组中每个元素相当于该队形的成员变量,数组对象存储在堆中. 2.初始化数组 常用类初始化 // 整型初始化 int[] int ...
- 利用IDEA搭建JDK源码阅读环境
利用IDEA搭建JDK源码阅读环境 首先新建一个java基础项目 基础目录 source 源码 test 测试源码和入口 准备JDK源码 下图框起来的路径就是jdk的储存位置 打开jdk目录,找到sr ...
- JDK源码阅读-ByteBuffer
本文转载自JDK源码阅读-ByteBuffer 导语 Buffer是Java NIO中对于缓冲区的封装.在Java BIO中,所有的读写API,都是直接使用byte数组作为缓冲区的,简单直接.但是在J ...
- JDK源码阅读(三):ArraryList源码解析
今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...
- JDK源码阅读(一):Object源码分析
最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...
- JDK源码阅读(1)_简介+ java.io
1.简介 针对这一个版块,主要做一个java8的源码阅读笔记.会对一些在javaWeb中应用比较广泛的java包进行精读,附上注释.对于容易混淆的知识点给出相应的对比分析. 精读的源码顺序主要如下: ...
- ZooKeeper源码阅读——client(二)
原创技术文章,转载请注明:转自http://newliferen.github.io/ 如何连接ZooKeeper集群 要想了解ZooKeeper客户端实现原理,首先需要关注一下客户端的使用方式, ...
随机推荐
- Ryoku 的新年欢乐赛
目录 前言 洛谷 6033 Ryoku 的探索 题目 分析 代码 洛谷 6034 Ryoku 与最初之人笔记 题目 分析O(log^2n) 代码(赛时AC) 分析O(logn) 代码(赛后) 洛谷 6 ...
- #SPFA#洛谷 4042 [AHOI2014/JSOI2014] 骑士游戏
题目 分析 如果我想普通攻击1,那么必须干掉所有产生的其它怪兽,这不由得可以用一个不等式来表示, \(普攻+\sum need<法攻\) 但是所需要消灭的怪兽同样可以这样进行,所以它可能具有后效 ...
- #K-D Tree#洛谷 4357 [CQOI2016]K 远点对
题目 已知平面内 \(n\) 个点的坐标,求欧氏距离下的第 \(k\) 远点对. 分析 先将\(k\)乘2转换为第\(k\)远有序点对. 由于\(O(n^2)\)即枚举一个点再枚举另一个点会超出时限, ...
- Wasm软件生态系统安全分析
演讲嘉宾 | 王浩宇 回顾整理 | 廖 涛 排版校对 | 李萍萍 嘉宾简介 王浩宇,华中科技大学教授,博士生导师,华中科技大学OpenHarmony技术俱乐部主任.研究关注于新兴软件系统中的安全. ...
- C++ 开发者必读经典书籍推荐
如果你正在学习C++,那么一本好的教材或参考书可以事半功倍.以下是几本我个人推荐的C++书籍或视频 C++基础 看书 C++ Primer C++程序设计语言 Effective C++ More E ...
- Python 数组和列表:创建、访问、添加和删除数组元素
Python 没有内置支持数组,但可以使用 Python 列表来代替. 数组 本页将向您展示如何使用列表作为数组,但要在 Python 中使用数组,您需要导入一个库,比如 NumPy 库.数组用于在一 ...
- 报表 BI 选型的那些事
前言 报表工具是一个接近 20 年的产物了 但是,直到现在,在各种数据信息化的系统中,报表工具的作用,不仅没有褪色,反而是因为信息化需求的增大.数据的增多,以及报表工具本身迭代后越来越方便好用,使得它 ...
- redis 简单整理——慢查询[八]
前言 简单整理一下redis的慢查询. 正文 什么是慢查询呢? 一般存储系统就是系统在命令执行前后计算每条命令的执行时间,当超出预设阀值,就将这条命令的相关信息记录下来. 但是有人可能没有看到慢查询日 ...
- steam社区留言红小作文模板
steam社区留言红小作文模板 Dear steam: Im a steam user which most play csgo.i saw i had be banned in steam comm ...
- Java 断言 Assert 使用教程与最佳实践
本文收录于 Github.com/niumoo/JavaNotes,Java 系列文档,数据结构与算法! 本文收录于网站:https://www.wdbyte.com/,我的公众号:程序猿阿朗 作为一 ...