JDK源码阅读-------自学笔记(二十二)(java.util.ArrayList自定义晋级,ArrayList实战详解)
简介(Introduction)
上篇文章主要介绍了ArrayList自行模仿建立的方式,那么,其实这个类不是一次性就那么完美的,现在做一个一步步变成那样完整的ArrayList的版本升级测试.
期间会阐述企业级在使用类的时候的常用做法.
版本中将去掉接口和继承,让你看到一个极简模式的ArrayList.
其实源码就是对于Java这门语言的概念的应用,你的理解可能只是书上,教程上最简单的理解,但是,并没有进行深入的理解,而源码要面向各种环境,各种可能性的应用而进行的设计.
随着Java开发年头的增加,你对基础概念的理解也将逐渐增加,就像三重境界
看山是山,看水是水;
看山不是山,看水不是水;
看山还是山,看水还是水
而Java的理解也是
一切皆为对象;
一切皆为对象;
一切皆为对象快速上手(Getting Started)
建立一个基础版的ArrayList,理解Object[]的特性,对于Object型数组的应用加深理解
建立源码1 /** 自定义ArrayList极简版 /
2 public class MySelfArrayList {
3
4 /**
5 * 存放元素的数组
6 */
7 private Object[] elementData;
8
9 /**
10 * 存放数组的索引
11 */
12 private int size;
13
14
15 /**
16 * 默认初始化Object数据的大小,参数是默认容量
17 */
18 private static final int DEFAULT_CAPACITY = 10;
19
20 /**
21 * 封装toString方法返回结果值(自定义,未在源码中)
22 */
23 StringBuilder stringBuilder;
24
25
26 /**
27 * <p>无参构造函数<p/>
28 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
29 * <p>或者是读取文件的方式在构造函数中存放<p/>
30 * <p>或者是常量的初始化<p/>
31 */
32 public MySelfArrayList() {
33
34 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
35 elementData = new Object[DEFAULT_CAPACITY];
36
37 // 封装toString方法返回结果值
38 stringBuilder = new StringBuilder();
39
40 }
41
42 /**
43 * 有参构造函数
44 *
45 * @param capacity 传入容量,即你想要创建一个多大的数组
46 */
47 public MySelfArrayList(int capacity) {
48 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
49 elementData = new Object[DEFAULT_CAPACITY];
50
51 // 封装toString方法返回结果值
52 stringBuilder = new StringBuilder();
53 }
54
55
56 /**
57 * 数组添加元素方法
58 *
59 * @param object 任意类型的数据添加到数组中
60 */
61 public void add(Object object) {
62
63 // 每次调用这个方法的时候,就像数组中添加索引值的元素
64 elementData[size++] = object;
65 }
66
67
68 /**
69 * 重写toString方法,打印数组元素
70 *
71 * @return 数组元素样式
72 */
73 @Override
74 public String toString() {
75
76 stringBuilder.append("MySelfArrayList : {");
77 stringBuilder.append("elementData=");
78
79 for (int i = 0; i < size; i++) {
80 stringBuilder.append(elementData[i] + ",");
81 }
82
83 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
84
85 stringBuilder.append("}");
86
87 return stringBuilder.toString();
88 }
89 }测试简单实体类
1 public static void main(String[] args) {
2
3 MySelfArrayList mySelfArrayList = new MySelfArrayList();
4
5 mySelfArrayList.add("A");
6 mySelfArrayList.add("B");
7
8
9 System.out.println(mySelfArrayList.toString());
10 }
入门篇(Basics)
环境准备(Prerequisite)
JDK环境:JDK1.8
工具:IDEA 2019
需要理解数组的概念,详见:https://www.cnblogs.com/liuyangfirst/p/12364850.html
理解StringBuilder使用方法,详见:https://www.cnblogs.com/liuyangfirst/p/12829294.html
理解toString()使用方法:https://www.cnblogs.com/liuyangfirst/p/12255687.html
进阶篇(Advanced)
1、添加自定义泛型
建立源码
1 /** 自定义ArrayList,添加泛型 /
2 public class MySelfArrayList<ME> {
3
4 /**
5 * 存放元素的数组
6 */
7 private Object[] elementData;
8
9 /**
10 * 存放数组的索引
11 */
12 private int size;
13
14
15 /**
16 * 默认初始化Object数据的大小,参数是默认容量
17 */
18 private static final int DEFAULT_CAPACITY = 10;
19
20 /**
21 * 封装toString方法返回结果值(自定义,未在源码中)
22 */
23 StringBuilder stringBuilder;
24
25
26 /**
27 * <p>无参构造函数<p/>
28 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
29 * <p>或者是读取文件的方式在构造函数中存放<p/>
30 * <p>或者是常量的初始化<p/>
31 */
32 public MySelfArrayList() {
33
34 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
35 elementData = new Object[DEFAULT_CAPACITY];
36
37 // 封装toString方法返回结果值
38 stringBuilder = new StringBuilder();
39
40 }
41
42 /**
43 * 有参构造函数
44 *
45 * @param capacity 传入容量,即你想要创建一个多大的数组
46 */
47 public MySelfArrayList(int capacity) {
48 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
49 elementData = new Object[DEFAULT_CAPACITY];
50
51 // 封装toString方法返回结果值
52 stringBuilder = new StringBuilder();
53 }
54
55
56 /**
57 * 数组添加元素方法
58 *
59 * @param object 任意类型的数据添加到数组中
60 */
61 public void add(Object object) {
62
63 // 每次调用这个方法的时候,就像数组中添加索引值的元素
64 elementData[size++] = object;
65 }
66
67
68 /**
69 * 重写toString方法,打印数组元素
70 *
71 * @return 数组元素样式
72 */
73 @Override
74 public String toString() {
75
76 stringBuilder.append("MySelfArrayList : {");
77 stringBuilder.append("elementData=");
78
79 for (int i = 0; i < size; i++) {
80 stringBuilder.append(elementData[i] + ",");
81 }
82
83 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
84
85 stringBuilder.append("}");
86
87 return stringBuilder.toString();
88 }
89 }
测试
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 mySelfArrayList.add("A");
6 mySelfArrayList.add("B");
7
8
9 System.out.println(mySelfArrayList.toString());
10
11
12 }
2、添加扩容机制
扩容机制:通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容
两个核心问题:
什么时候扩容?
当默认数组或自定义数组的最大值无法承载添加的元素数量的时候,进行扩容.比如:默认是10,添加第11个元素,就会报错,所以此时就要修改代码,以便适应更多情况.
怎么扩容?
(1)定义一个更大的数组,比如建立一个二倍大小的数组,为了提高效率,可以采用左移的方式,左移一位<<1.源码中是原长度+原长度的一半,即如果是原长度10,扩容后就是15.
(2)使用拷贝机制,将原来的数组拷贝到新数组.
拷贝机制详见:https://www.cnblogs.com/liuyangfirst/p/12364850.html
建立源码
1 /** 自定义ArrayList,添加扩容机制 /
2 public class MySelfArrayList<ME> {
3
4 /**
5 * 存放元素的数组
6 */
7 private Object[] elementData;
8
9 /**
10 * 存放数组的索引
11 */
12 private int size;
13
14
15 /**
16 * 默认初始化Object数据的大小,参数是默认容量
17 */
18 private static final int DEFAULT_CAPACITY = 10;
19
20 /**
21 * 封装toString方法返回结果值(自定义,未在源码中)
22 */
23 StringBuilder stringBuilder;
24
25
26 /**
27 * <p>无参构造函数<p/>
28 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
29 * <p>或者是读取文件的方式在构造函数中存放<p/>
30 * <p>或者是常量的初始化<p/>
31 */
32 public MySelfArrayList() {
33
34 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
35 elementData = new Object[DEFAULT_CAPACITY];
36
37 // 封装toString方法返回结果值
38 stringBuilder = new StringBuilder();
39
40 }
41
42 /**
43 * 有参构造函数
44 *
45 * @param capacity 传入容量,即你想要创建一个多大的数组
46 */
47 public MySelfArrayList(int capacity) {
48 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
49 elementData = new Object[DEFAULT_CAPACITY];
50
51 // 封装toString方法返回结果值
52 stringBuilder = new StringBuilder();
53 }
54
55
56 /**
57 * 数组添加元素方法
58 *
59 * @param object 任意类型的数据添加到数组中
60 */
61 public void add(Object object) {
62
63 // 当传入的元素个数跟默认或自定义的长度相等的时候采用扩容机制
64 if (size == elementData.length) {
65
66 //定义一个更大的数组
67 Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
68
69 // 拷贝旧的数组到新的数组中
70 System.arraycopy(elementData, 0, newArray, 0, elementData.length);
71
72 // 将新数组结果赋值给创建的数组
73 elementData = newArray;
74 }
75
76
77 // 每次调用这个方法的时候,就像数组中添加索引值的元素
78 elementData[size++] = object;
79 }
80
81
82 /**
83 * 重写toString方法,打印数组元素
84 *
85 * @return 数组元素样式
86 */
87 @Override
88 public String toString() {
89
90 stringBuilder.append("MySelfArrayList : {");
91 stringBuilder.append("elementData=");
92
93 for (int i = 0; i < size; i++) {
94 stringBuilder.append(elementData[i] + ",");
95 }
96
97 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
98
99 stringBuilder.append("}");
100
101 return stringBuilder.toString();
102 }
103 }
测试
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 for (int i = 0; i < 20; i++) {
6 mySelfArrayList.add("B");
7 }
8
9
10 System.out.println(mySelfArrayList.toString());
11
12
13 }
3、添加Object的get和set方法
1 /** 自定义ArrayList,添加Object的get和set方法 /
2 public class MySelfArrayList<ME> {
3
4 /**
5 * 存放元素的数组
6 */
7 private Object[] elementData;
8
9 /**
10 * 存放数组的索引
11 */
12 private int size;
13
14
15 /**
16 * 默认初始化Object数据的大小,参数是默认容量
17 */
18 private static final int DEFAULT_CAPACITY = 10;
19
20 /**
21 * 封装toString方法返回结果值(自定义,未在源码中)
22 */
23 StringBuilder stringBuilder;
24
25
26 /**
27 * <p>无参构造函数<p/>
28 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
29 * <p>或者是读取文件的方式在构造函数中存放<p/>
30 * <p>或者是常量的初始化<p/>
31 */
32 public MySelfArrayList() {
33
34 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
35 elementData = new Object[DEFAULT_CAPACITY];
36
37 // 封装toString方法返回结果值
38 stringBuilder = new StringBuilder();
39
40 }
41
42 /**
43 * 有参构造函数
44 *
45 * @param capacity 传入容量,即你想要创建一个多大的数组
46 */
47 public MySelfArrayList(int capacity) {
48 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
49 elementData = new Object[DEFAULT_CAPACITY];
50
51 // 封装toString方法返回结果值
52 stringBuilder = new StringBuilder();
53 }
54
55
56 /**
57 * 数组添加元素方法
58 *
59 * @param object 任意类型的数据添加到数组中
60 */
61 public void add(Object object) {
62
63 // 当传入的元素个数跟默认或自定义的长度相等的时候采用扩容机制
64 if (size == elementData.length) {
65
66 //定义一个更大的数组
67 Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
68
69 // 拷贝旧的数组到新的数组中
70 System.arraycopy(elementData, 0, newArray, 0, elementData.length);
71
72 // 将新数组结果赋值给创建的数组
73 elementData = newArray;
74 }
75
76
77 // 每次调用这个方法的时候,就像数组中添加索引值的元素
78 elementData[size++] = object;
79 }
80
81
82 /**
83 * 获取索引位置的值
84 *
85 * @param index 数组的索引
86 * @return 对应位置的索引值
87 */
88 public Object getElementData(int index) {
89 return elementData[index];
90 }
91
92 /**
93 * 设置元素
94 *
95 * @param object 设置的元素
96 * @param index 索引位置
97 */
98 public void setElementData(Object object, int index) {
99 elementData[index] = object;
100 }
101
102 /**
103 * 重写toString方法,打印数组元素
104 *
105 * @return 数组元素样式
106 */
107 @Override
108 public String toString() {
109
110 // 处理返回结果产生多个的状况
111 if (null != stringBuilder) {
112 stringBuilder = new StringBuilder();
113 }
114
115 getStringBuilderResult();
116
117 return stringBuilder.toString();
118 }
119
120 /**
121 * 获取拼接结果集
122 */
123 private void getStringBuilderResult() {
124 stringBuilder.append("MySelfArrayList : {");
125 stringBuilder.append("elementData=");
126
127 for (int i = 0; i < size; i++) {
128 stringBuilder.append(elementData[i] + ",");
129 }
130
131 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
132
133 stringBuilder.append("}");
134 }
135 }
4、升级Object的get和set方法为泛型
1 /*
2 * 获取索引位置的值
3 @param index 数组的索引
4 @return 对应位置的索引值
5 */
6 public ME getElementData(int index) { return (ME)elementData[index]; }
7
8 /**
9 * 设置元素
10 *
11 * @param object 设置的元素
12 * @param index 索引位置
13 */
14 public void setElementData(ME object, int index) {
15 elementData[index] = object;
16 }
测试
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 mySelfArrayList.add("5");
6 mySelfArrayList.add("6");
7 mySelfArrayList.add("7");
8 System.out.println("原数组:" + mySelfArrayList.toString());
9
10 mySelfArrayList.setElementData("6", 2);
11
12
13 System.out.println("使用set()设置后数组:" + mySelfArrayList.toString());
14
15 System.out.println("使用get()获取元素:" + mySelfArrayList.getElementData(1));
16
17
18 }
5、添加判断索引的大小是否超过数组的大小判断
1 /** 自定义ArrayList,添加判断索引的大小是否超过数组的大小判断 / public class MySelfArrayList<ME> {
2
3 /**
4 * 存放元素的数组
5 */
6 private Object[] elementData;
7
8 /**
9 * 存放数组的索引
10 */
11 private int size;
12
13
14 /**
15 * 默认初始化Object数据的大小,参数是默认容量
16 */
17 private static final int DEFAULT_CAPACITY = 10;
18
19 /**
20 * 封装toString方法返回结果值(自定义,未在源码中)
21 */
22 StringBuilder stringBuilder;
23
24
25 /**
26 * <p>无参构造函数<p/>
27 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
28 * <p>或者是读取文件的方式在构造函数中存放<p/>
29 * <p>或者是常量的初始化<p/>
30 */
31 public MySelfArrayList() {
32
33 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
34 elementData = new Object[DEFAULT_CAPACITY];
35
36 // 封装toString方法返回结果值
37 stringBuilder = new StringBuilder();
38
39 }
40
41 /**
42 * 有参构造函数
43 *
44 * @param capacity 传入容量,即你想要创建一个多大的数组
45 */
46 public MySelfArrayList(int capacity) {
47
48 // 封装toString方法返回结果值
49 stringBuilder = new StringBuilder();
50
51 if (capacity > 0) {
52 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
53 elementData = new Object[capacity];
54 } else if (capacity == 0) {
55 // 当你new这个对象的时候,就会建立一个默认值大小的数组
56 elementData = new Object[DEFAULT_CAPACITY];
57 } else {
58 throw new IllegalArgumentException("传入索引不合法: " +
59 capacity);
60 }
61 }
62
63
64 /**
65 * 数组添加元素方法
66 *
67 * @param object 任意类型的数据添加到数组中
68 */
69 public void add(Object object) {
70
71 // 当传入的元素个数跟默认或自定义的长度相等的时候采用扩容机制
72 if (size == elementData.length) {
73
74 //定义一个更大的数组
75 Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
76
77 // 拷贝旧的数组到新的数组中
78 System.arraycopy(elementData, 0, newArray, 0, elementData.length);
79
80 // 将新数组结果赋值给创建的数组
81 elementData = newArray;
82 }
83
84
85 // 每次调用这个方法的时候,就像数组中添加索引值的元素
86 elementData[size++] = object;
87 }
88
89
90 /**
91 * 获取索引位置的值
92 *
93 * @param index 数组的索引
94 * @return 对应位置的索引值
95 */
96 public ME getElementData(int index) {
97 checkIndexRange(index);
98 return (ME) elementData[index];
99 }
100
101 /**
102 * 设置元素
103 *
104 * @param object 设置的元素
105 * @param index 索引位置
106 */
107 public void setElementData(ME object, int index) {
108 checkIndexRange(index);
109 elementData[index] = object;
110 }
111
112 /**
113 * 传入的值是否在数组范围内
114 *
115 * @param index 传入的索引值
116 */
117 public void checkIndexRange(int index) {
118
119 // 传入的长度不能是负数和大于数组长度的数
120 if (index < 0 || index > size - 1) {
121 throw new IllegalArgumentException("传入索引不合法: " +
122 index);
123 }
124 }
125
126 /**
127 * 重写toString方法,打印数组元素
128 *
129 * @return 数组元素样式
130 */
131 @Override
132 public String toString() {
133
134 // 处理返回结果产生多个的状况
135 if (null != stringBuilder) {
136 stringBuilder = new StringBuilder();
137 }
138
139 getStringBuilderResult();
140
141 return stringBuilder.toString();
142 }
143
144 /**
145 * 获取拼接结果集
146 */
147 private void getStringBuilderResult() {
148 stringBuilder.append("MySelfArrayList : {");
149 stringBuilder.append("elementData=");
150
151 for (int i = 0; i < size; i++) {
152 stringBuilder.append(elementData[i] + ",");
153 }
154
155 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
156
157 stringBuilder.append("}");
158 }
159 }
测试
1.参数越界
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList2 = new MySelfArrayList<>(-9);
4 }
2.set()越界
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 mySelfArrayList.add("5");
6 mySelfArrayList.add("6");
7 mySelfArrayList.add("7");
8 System.out.println("原数组:" + mySelfArrayList.toString());
9
10 mySelfArrayList.setElementData("6", -5);
11
12 }
3.get()越界
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 mySelfArrayList.add("5");
6 mySelfArrayList.add("6");
7 mySelfArrayList.add("7");
8 System.out.println("原数组:" + mySelfArrayList.toString());
9
10 mySelfArrayList.getElementData( -5);
11
12 }
6、添加remove方法
核心思想
也是拷贝,从被删除的元素之后的一个位置把元素拷贝进数组
核心代码
1 /*
2 * 指定位置移除元素
3 *@param index 传入的索引值
4 *
5 /
6 public void remove(int index) { checkIndexRange(index);
7
8 // 移动索引之后元素的长度
9 int numMovedLength = size - index - 1;
10
11 // 长度大于零,说明最后一个元素之后还有元素
12 if (numMovedLength > 0) {
13 System.arraycopy(elementData, index + 1, elementData, index,
14 numMovedLength);
15 }
16
17 // clear to let GC do its work
18 elementData[--size] = null;
19
20 }
21
22
23 /**
24 * 移除元素
25 *
26 * @param element 需要移除的元素
27 */
28 public void remove(ME element) {
29
30 // 遍历元素组,找到与输入的元素相同的那个
31 for (int i = 0; i < size; i++) {
32
33 // 所有容器中比较操作,都是用equals,而不是'=='
34 if (element.equals(getElementData(i))) {
35
36 // 获得相同的元素的索引,通过索引进行移除操作
37 remove(i);
38 }
39 }
40 }
完整代码
1 /** 自定义ArrayList,添加移除操作 /
2 public class MySelfArrayList<ME> {
3
4 /**
5 * 存放元素的数组
6 */
7 private Object[] elementData;
8
9 /**
10 * 存放数组的索引
11 */
12 private int size;
13
14
15 /**
16 * 默认初始化Object数据的大小,参数是默认容量
17 */
18 private static final int DEFAULT_CAPACITY = 10;
19
20 /**
21 * 封装toString方法返回结果值(自定义,未在源码中)
22 */
23 StringBuilder stringBuilder;
24
25
26 /**
27 * <p>无参构造函数<p/>
28 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
29 * <p>或者是读取文件的方式在构造函数中存放<p/>
30 * <p>或者是常量的初始化<p/>
31 */
32 public MySelfArrayList() {
33
34 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
35 elementData = new Object[DEFAULT_CAPACITY];
36
37 // 封装toString方法返回结果值
38 stringBuilder = new StringBuilder();
39
40 }
41
42 /**
43 * 有参构造函数
44 *
45 * @param capacity 传入容量,即你想要创建一个多大的数组
46 */
47 public MySelfArrayList(int capacity) {
48
49 // 封装toString方法返回结果值
50 stringBuilder = new StringBuilder();
51
52 if (capacity > 0) {
53 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
54 elementData = new Object[capacity];
55 } else if (capacity == 0) {
56 // 当你new这个对象的时候,就会建立一个默认值大小的数组
57 elementData = new Object[DEFAULT_CAPACITY];
58 } else {
59 throw new IllegalArgumentException("传入索引不合法: " +
60 capacity);
61 }
62 }
63
64
65 /**
66 * 数组添加元素方法
67 *
68 * @param object 任意类型的数据添加到数组中
69 */
70 public void add(Object object) {
71
72 // 当传入的元素个数跟默认或自定义的长度相等的时候采用扩容机制
73 if (size == elementData.length) {
74
75 //定义一个更大的数组
76 Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
77
78 // 拷贝旧的数组到新的数组中
79 System.arraycopy(elementData, 0, newArray, 0, elementData.length);
80
81 // 将新数组结果赋值给创建的数组
82 elementData = newArray;
83 }
84
85
86 // 每次调用这个方法的时候,就像数组中添加索引值的元素
87 elementData[size++] = object;
88 }
89
90
91 /**
92 * 获取索引位置的值
93 *
94 * @param index 数组的索引
95 * @return 对应位置的索引值
96 */
97 public ME getElementData(int index) {
98 checkIndexRange(index);
99 return (ME) elementData[index];
100 }
101
102 /**
103 * 设置元素
104 *
105 * @param object 设置的元素
106 * @param index 索引位置
107 */
108 public void setElementData(ME object, int index) {
109 checkIndexRange(index);
110 elementData[index] = object;
111 }
112
113
114 /**
115 * 指定位置移除元素
116 *
117 * @param index 传入的索引值
118 */
119 public void remove(int index) {
120 checkIndexRange(index);
121
122 // 移动索引之后元素的长度
123 int numMovedLength = size - index - 1;
124
125 // 长度大于零,说明最后一个元素之后还有元素
126 if (numMovedLength > 0) {
127 System.arraycopy(elementData, index + 1, elementData, index,
128 numMovedLength);
129 }
130
131 // clear to let GC do its work
132 elementData[--size] = null;
133
134 }
135
136
137 /**
138 * 移除元素
139 *
140 * @param element 需要移除的元素
141 */
142 public void remove(ME element) {
143
144 // 遍历元素组,找到与输入的元素相同的那个
145 for (int i = 0; i < size; i++) {
146
147 // 所有容器中比较操作,都是用equals,而不是'=='
148 if (element.equals(getElementData(i))) {
149
150 // 获得相同的元素的索引,通过索引进行移除操作
151 remove(i);
152 }
153 }
154 }
155
156
157 /**
158 * 传入的值是否在数组范围内
159 *
160 * @param index 传入的索引值
161 */
162 public void checkIndexRange(int index) {
163
164 // 传入的长度不能是负数和大于数组长度的数
165 if (index < 0 || index > size - 1) {
166 throw new IllegalArgumentException("传入索引不合法: " +
167 index);
168 }
169 }
170
171 /**
172 * 重写toString方法,打印数组元素
173 *
174 * @return 数组元素样式
175 */
176 @Override
177 public String toString() {
178
179 // 处理返回结果产生多个的状况
180 if (null != stringBuilder) {
181 stringBuilder = new StringBuilder();
182 }
183
184 getStringBuilderResult();
185
186 return stringBuilder.toString();
187 }
188
189 /**
190 * 获取拼接结果集
191 */
192 private void getStringBuilderResult() {
193 stringBuilder.append("MySelfArrayList : {");
194 stringBuilder.append("elementData=");
195
196 for (int i = 0; i < size; i++) {
197 stringBuilder.append(elementData[i] + ",");
198 }
199
200 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
201
202 stringBuilder.append("}");
203 }
204 }
测试
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 mySelfArrayList.add("5");
6 mySelfArrayList.add("6");
7 mySelfArrayList.add("7");
8 System.out.println("原数组:" + mySelfArrayList.toString());
9
10 mySelfArrayList.remove(0);
11
12 mySelfArrayList.remove("6");
13
14 System.out.println("移除后:" + mySelfArrayList.toString());
15
16 }
7、添加其他方法
编辑源码
1 /** 自定义ArrayList,添加其他操作 /
2 public class MySelfArrayList<ME> {
3
4 /**
5 * 存放元素的数组
6 */
7 private Object[] elementData;
8
9 /**
10 * 存放数组的索引
11 */
12 private int size;
13
14
15 /**
16 * 默认初始化Object数据的大小,参数是默认容量
17 */
18 private static final int DEFAULT_CAPACITY = 10;
19
20 /**
21 * 封装toString方法返回结果值(自定义,未在源码中)
22 */
23 StringBuilder stringBuilder;
24
25
26 /**
27 * <p>无参构造函数<p/>
28 * <p>企业级开发一般会在构造函数对对象中存在的对象初始化<p/>
29 * <p>或者是读取文件的方式在构造函数中存放<p/>
30 * <p>或者是常量的初始化<p/>
31 */
32 public MySelfArrayList() {
33
34 // 当你new 这个对象的时候,就会建立一个大小为10的Object数组
35 elementData = new Object[DEFAULT_CAPACITY];
36
37 // 封装toString方法返回结果值
38 stringBuilder = new StringBuilder();
39
40 }
41
42 /**
43 * 有参构造函数
44 *
45 * @param capacity 传入容量,即你想要创建一个多大的数组
46 */
47 public MySelfArrayList(int capacity) {
48
49 // 封装toString方法返回结果值
50 stringBuilder = new StringBuilder();
51
52 if (capacity > 0) {
53 // 当你new这个对象的时候,就会建立一个自定义大小的Object数组
54 elementData = new Object[capacity];
55 } else if (capacity == 0) {
56 // 当你new这个对象的时候,就会建立一个默认值大小的数组
57 elementData = new Object[DEFAULT_CAPACITY];
58 } else {
59 throw new IllegalArgumentException("传入索引不合法: " +
60 capacity);
61 }
62 }
63
64
65 /**
66 * 数组添加元素方法
67 *
68 * @param object 任意类型的数据添加到数组中
69 */
70 public void add(Object object) {
71
72 // 当传入的元素个数跟默认或自定义的长度相等的时候采用扩容机制
73 if (size == elementData.length) {
74
75 //定义一个更大的数组
76 Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
77
78 // 拷贝旧的数组到新的数组中
79 System.arraycopy(elementData, 0, newArray, 0, elementData.length);
80
81 // 将新数组结果赋值给创建的数组
82 elementData = newArray;
83 }
84
85
86 // 每次调用这个方法的时候,就像数组中添加索引值的元素
87 elementData[size++] = object;
88 }
89
90
91 /**
92 * 获取索引位置的值
93 *
94 * @param index 数组的索引
95 * @return 对应位置的索引值
96 */
97 public ME getElementData(int index) {
98 checkIndexRange(index);
99 return (ME) elementData[index];
100 }
101
102 /**
103 * 设置元素
104 *
105 * @param object 设置的元素
106 * @param index 索引位置
107 */
108 public void setElementData(ME object, int index) {
109 checkIndexRange(index);
110 elementData[index] = object;
111 }
112
113
114 /**
115 * 指定位置移除元素
116 *
117 * @param index 传入的索引值
118 */
119 public void remove(int index) {
120 checkIndexRange(index);
121
122 // 移动索引之后元素的长度
123 int numMovedLength = size - index - 1;
124
125 // 长度大于零,说明最后一个元素之后还有元素
126 if (numMovedLength > 0) {
127 System.arraycopy(elementData, index + 1, elementData, index,
128 numMovedLength);
129 }
130
131 // clear to let GC do its work
132 elementData[--size] = null;
133
134 }
135
136
137 /**
138 * 移除元素
139 *
140 * @param element 需要移除的元素
141 */
142 public void remove(ME element) {
143
144 // 遍历元素组,找到与输入的元素相同的那个
145 for (int i = 0; i < size; i++) {
146
147 // 所有容器中比较操作,都是用equals,而不是'=='
148 if (element.equals(getElementData(i))) {
149
150 // 获得相同的元素的索引,通过索引进行移除操作
151 remove(i);
152 }
153 }
154 }
155
156
157 /**
158 * 数组大小
159 *
160 * @return 数组大小的值
161 */
162 public int size() {
163 return size;
164 }
165
166 /**
167 * 判断数组是否为空
168 *
169 * @return 是 true 否 false
170 */
171 public boolean isEmpty() {
172 return size == 0 ? true : false;
173 }
174
175 /**
176 * 传入的值是否在数组范围内
177 *
178 * @param index 传入的索引值
179 */
180 public void checkIndexRange(int index) {
181
182 // 传入的长度不能是负数和大于数组长度的数
183 if (index < 0 || index > size - 1) {
184 throw new IllegalArgumentException("传入索引不合法: " +
185 index);
186 }
187 }
188
189 /**
190 * 重写toString方法,打印数组元素
191 *
192 * @return 数组元素样式
193 */
194 @Override
195 public String toString() {
196
197 // 处理返回结果产生多个的状况
198 if (null != stringBuilder) {
199 stringBuilder = new StringBuilder();
200 }
201
202 getStringBuilderResult();
203
204 return stringBuilder.toString();
205 }
206
207 /**
208 * 获取拼接结果集
209 */
210 private void getStringBuilderResult() {
211 stringBuilder.append("MySelfArrayList : {");
212 stringBuilder.append("elementData=");
213
214 for (int i = 0; i < size; i++) {
215 stringBuilder.append(elementData[i] + ",");
216 }
217
218 stringBuilder.setCharAt(stringBuilder.length() - 1, ']');
219
220 stringBuilder.append("}");
221 }
222 }
测试
1 public static void main(String[] args) {
2
3 MySelfArrayList<String> mySelfArrayList = new MySelfArrayList<>();
4
5 mySelfArrayList.add("5");
6 mySelfArrayList.add("6");
7 mySelfArrayList.add("7");
8
9 System.out.println("数组大小:" + mySelfArrayList.size());
10 System.out.println("数组是否为空:" + mySelfArrayList.isEmpty());
11
12
13 }
JDK源码阅读-------自学笔记(二十二)(java.util.ArrayList自定义晋级,ArrayList实战详解)的更多相关文章
- JDK源码阅读-------自学笔记(一)(java.lang.Object重写toString源码)
一.前景提要 Object类中定义有public String toString()方法,其返回值是 String 类型. 二.默认返回组成 类名+@+16进制的hashcode,当使用打印方法打印的 ...
- JDK源码阅读-------自学笔记(二十五)(java.util.Vector 自定义讲解)
Vector 向量 Vector简述 1).Vector底层是用数组实现的List 2).虽然线程安全,但是效率低,所以并不是安全就是好的 3).底层大量方法添加synchronized同步标记,sy ...
- JDK源码阅读-------自学笔记(五)(浅析数组)
一.数组基础 1.定义和特点 数组也可以看做是对象,数组变量属于引用类型,数组中每个元素相当于该队形的成员变量,数组对象存储在堆中. 2.初始化数组 常用类初始化 // 整型初始化 int[] int ...
- JDK源码阅读-------自学笔记(二十四)(java.util.LinkedList 再探 自定义讲解)
一.实现get方法 1.一般思维实现思路 1).将对象的值放入一个中间变量中. 2).遍历索引值,将中间量的下一个元素赋值给中间量. 3).返回中间量中的元素值. 4).示意图 get(2),传入角标 ...
- JDK源码阅读(1)_简介+ java.io
1.简介 针对这一个版块,主要做一个java8的源码阅读笔记.会对一些在javaWeb中应用比较广泛的java包进行精读,附上注释.对于容易混淆的知识点给出相应的对比分析. 精读的源码顺序主要如下: ...
- element-ui MessageBox组件源码分析整理笔记(十二)
MessageBox组件源码,有添加部分注释 main.vue <template> <transition name="msgbox-fade"> < ...
- JDK源码阅读(三):ArraryList源码解析
今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...
- JDK源码阅读(一):Object源码分析
最近经过某大佬的建议准备阅读一下JDK的源码来提升一下自己 所以开始写JDK源码分析的文章 阅读JDK版本为1.8 目录 Object结构图 构造器 equals 方法 getClass 方法 has ...
- 利用IDEA搭建JDK源码阅读环境
利用IDEA搭建JDK源码阅读环境 首先新建一个java基础项目 基础目录 source 源码 test 测试源码和入口 准备JDK源码 下图框起来的路径就是jdk的储存位置 打开jdk目录,找到sr ...
- JDK源码阅读-FileOutputStream
本文转载自JDK源码阅读-FileOutputStream 导语 FileOutputStream用户打开文件并获取输出流. 打开文件 public FileOutputStream(File fil ...
随机推荐
- #结论#CF1776G Another Wine Tasting Event
题目 给定一个长度为 \(2n-1\) 的字符串,问一组使得 \(n\) 个长度不小于 \(n\) 的区间中字母W的个数相等的字母W的个数 分析 首先结论就是 \(\max_{i=1}^n\{cW[i ...
- USACO 4.2
目录 洛谷 2740 草地排水 代码(网络最大流) 洛谷 2751 工序安排 分析 代码 洛谷 1894 完美的牛栏 代码(二分图最大匹配) 草地排水洛谷传送门,草地排水USACO传送门 工序安排洛谷 ...
- #线段树#洛谷 4428 [BJOI2018]二进制
题目 有一个长为 \(n\) 的二进制串,支持单个位置取反,对于这个二进制串的一个子区间, 求出其有多少位置不同的连续子串,满足在重新排列后(可包含前导0)是一个 3 的倍数. 分析 考虑对于单个位置 ...
- SQline安装
SQLite 安装 SQLite 的一个重要的特性是零配置的,这意味着不需要复杂的安装或管理.本章将讲解 Windows.Linux 和 Mac OS X 上的安装设置. 在 Windows 上安装 ...
- C#的AOP(最经典实现)
(适用于.NET/.NET Core/.NET Framework) [目录]0.前言1.第一个AOP程序2.Aspect横切面编程3.一个横切面程序拦截多个主程序4.多个横切面程序拦截一个主程序5. ...
- scala 生成指定日期范围的list
可以通过scala中的流处理,生成指定范围内的日期list import java.time.LocalDate def dateStream(fromDt:LocalDate):Stream[Loc ...
- 重新整理.net core 计1400篇[五] (.net core 添加mvc 中间件 )
前言 前面提过.net core web实际上是一个服务和若干个中间件组成. 现在我们有一个服务了,那么可想而知我们要完成mvc这种功能,我们需要的就是一个中间件. 好的,接下来,我们只要添加中间件即 ...
- MySQL组合索引
MySQL组引合索优化SQL 我的场景 200w左右的数据,后面会更多 使用定时任务爬取数据插入到自己的数据库.要保证数据的唯一性,所以我用了组合唯一索引. 表结构 最初的组合索引 SQL执行和exp ...
- Web自动化实战:去哪儿网购票流程测试
克隆源码 项目Github地址:https://github.com/gy-7/Web-automation-practice/tree/main/project1_qunar_booking_tic ...
- 【SQL】IN和EXISTS谁的效率更高
[SQL]IN和EXISTS谁的效率更高 总结: 索引设置好的情况下 子查询数据量大的,用exists 子查询数据量小的,用in 原文连接:https://zhuanlan.zhihu.com/p/4 ...