JDK源码阅读-------自学笔记(一)(详细版集合类)

一、前景提要

本人经历了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 extends AbstractList
  9         implements List, 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      * 

35 *

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 true 判断添加是否成功 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 *

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 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 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 extends AbstractList
     14         implements List, 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      * 

    41 *

    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 true 判断添加是否成功 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 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
  • 测试结果


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

你可能感兴趣的:(JDK源码阅读-------自学笔记(一)(详细版集合类))