一、前景提要
本人经历了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 }
- 测试结果
这就是初步的改写和合并查阅方案,希望对你有帮助,感谢支持,如果有有疑问请留言,我们共同讨论
来源:https://www.cnblogs.com/liuyangfirst/p/12159423.html