1- # Table of Contents
2-
1+ # 目录
32 * [ ArrayList] ( #arraylist )
43 * [ ArrayList概述] ( #arraylist概述 )
54 * [ ArrayList的继承关系] ( #arraylist的继承关系 )
@@ -79,7 +78,7 @@ non-private to simplify nested class access
7978
8079//总结一下就是只复制数组中有值的位置,其他未赋值的位置不进行序列化,可以节省空间。
8180
82-
81+ ````
8382 // private void writeObject(java.io.ObjectOutputStream s)
8483 // throws java.io.IOException{
8584 // // Write out element count, and any hidden stuff
@@ -99,7 +98,7 @@ non-private to simplify nested class access
9998 // }
10099 // }
101100
102-
101+ ````
103102### 增删改查
104103
105104 //增删改查
@@ -109,13 +108,13 @@ non-private to simplify nested class access
109108这个方法无非就是使用System.arraycopy()方法将C集合(先准换为数组)里面的数据复制到elementData数组中。这里就稍微介绍下System.arraycopy(),因为下面还将大量用到该方法
110109
111110。该方法的原型为:
112-
111+ ````
113112 public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)。
114-
113+ ````
115114它的根本目的就是进行数组元素的复制。即从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。
116115
117116将源数组src从srcPos位置开始复制到dest数组中,复制长度为length,数据从dest的destPos位置开始粘贴。
118-
117+ ````
119118 // public void add(int index, E element) {
120119 // rangeCheckForAdd(index);
121120 //
@@ -126,9 +125,9 @@ non-private to simplify nested class access
126125 // size++;
127126 // }
128127 //
129-
128+ ````
130129删除元素时,同样判断索引是否和法,删除的方式是把被删除元素右边的元素左移,方法同样是使用System.arraycopy进行拷贝。
131-
130+ ````
132131 // public E remove(int index) {
133132 // rangeCheck(index);
134133 //
@@ -143,9 +142,9 @@ non-private to simplify nested class access
143142 //
144143 // return oldValue;
145144 // }
146-
145+ ````
147146ArrayList提供一个清空数组的办法,方法是将所有元素置为null,这样就可以让GC自动回收掉没有被引用的元素了。
148-
147+ ````
149148 //
150149 // /**
151150 // * Removes all of the elements from this list. The list will
@@ -160,9 +159,9 @@ ArrayList提供一个清空数组的办法,方法是将所有元素置为null
160159 //
161160 // size = 0;
162161 // }
163-
162+ ````
164163修改元素时,只需要检查下标即可进行修改操作。
165-
164+ ````
166165 // public E set(int index, E element) {
167166 // rangeCheck(index);
168167 //
@@ -177,18 +176,18 @@ ArrayList提供一个清空数组的办法,方法是将所有元素置为null
177176 // return elementData(index);
178177 // }
179178 //
180-
179+ ````
181180上述方法都使用了rangeCheck方法,其实就是简单地检查下标而已。
182-
181+ ````
183182 // private void rangeCheck(int index) {
184183 // if (index >= size)
185184 // throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
186185 // }
187-
186+ ````
188187### modCount
189-
188+ ````
190189 // protected transient int modCount = 0;
191-
190+ ````
192191由以上代码可以看出,在一个迭代器初始的时候会赋予它调用这个迭代器的对象的mCount,如何在迭代器遍历的过程中,一旦发现这个对象的mcount和迭代器中存储的mcount不一样那就抛异常
193192
194193> 好的,下面是这个的完整解释
@@ -205,10 +204,10 @@ ArrayList提供一个清空数组的办法,方法是将所有元素置为null
205204
206205初始容量是10,下面是扩容方法。
207206首先先取
208-
209- // private static final int DEFAULT_CAPACITY = 10;
207+ ````
208+ //private static final int DEFAULT_CAPACITY = 10;
210209
211- 扩容发生在add元素时,传入当前元素容量加一
210+ // 扩容发生在add元素时,传入当前元素容量加一
212211 public boolean add(E e) {
213212 ensureCapacityInternal(size + 1); // Increments modCount!!
214213 elementData[size++] = e;
@@ -238,13 +237,13 @@ ArrayList提供一个清空数组的办法,方法是将所有元素置为null
238237 if (minCapacity - elementData.length > 0)
239238 grow(minCapacity);
240239 }
241-
240+ ````
242241真正执行扩容的方法grow
243242
244243扩容方式是让新容量等于旧容量的1.5被。
245244
246245当新容量大于最大数组容量时,执行大数扩容
247-
246+ ````
248247 // private void grow(int minCapacity) {
249248 // // overflow-conscious code
250249 // int oldCapacity = elementData.length;
@@ -256,38 +255,39 @@ ArrayList提供一个清空数组的办法,方法是将所有元素置为null
256255 // // minCapacity is usually close to size, so this is a win:
257256 // elementData = Arrays.copyOf(elementData, newCapacity);
258257 // }
259-
258+ ````
260259当新容量大于最大数组长度,有两种情况,一种是溢出,抛异常,一种是没溢出,返回整数的最大值。
261-
260+ ````
262261 private static int hugeCapacity(int minCapacity) {
263262 if (minCapacity < 0) // overflow
264263 throw new OutOfMemoryError();
265264 return (minCapacity > MAX_ARRAY_SIZE) ?
266265 Integer.MAX_VALUE :
267266 MAX_ARRAY_SIZE;
268267 }
269-
268+ ````
270269
271270在这里有一个疑问,为什么每次扩容处理会是1.5倍,而不是2.5、3、4倍呢?通过google查找,发现1.5倍的扩容是最好的倍数。因为一次性扩容太大(例如2.5倍)可能会浪费更多的内存(1.5倍最多浪费33%,而2.5被最多会浪费60%,3.5倍则会浪费71%……)。但是一次性扩容太小,需要多次对数组重新分配内存,对性能消耗比较严重。所以1.5倍刚刚好,既能满足性能需求,也不会造成很大的内存消耗。
272271
273272 处理这个ensureCapacity()这个扩容数组外,ArrayList还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过trimToSize()方法来实现。该方法可以最小化ArrayList实例的存储量。
274-
273+ ````
275274 public void trimToSize() {
276275 modCount++;
277276 int oldCapacity = elementData.length;
278277 if (size < oldCapacity) {
279278 elementData = Arrays.copyOf(elementData, size);
280279 }
281280 }
281+ ````
282282### 线程安全
283283
284284ArrayList是线程不安全的。在其迭代器iteator中,如果有多线程操作导致modcount改变,会执行fastfail。抛出异常。
285-
285+ ````
286286 final void checkForComodification() {
287287 if (modCount != expectedModCount)
288288 throw new ConcurrentModificationException();
289289 }
290-
290+ ````
291291## Vector
292292
293293### Vector简介
@@ -301,7 +301,7 @@ Vector实现RandmoAccess接口,即提供了随机访问功能,提供提供
301301Vector 实现了Cloneable接口,支持clone()方法,可以被克隆。
302302
303303vector底层数组不加transient,序列化时会全部复制
304-
304+ ````
305305 protected Object[] elementData;
306306
307307
@@ -318,9 +318,9 @@ vector底层数组不加transient,序列化时会全部复制
318318 // fields.put("elementData", data);
319319 // s.writeFields();
320320 // }
321-
321+ ````
322322Vector除了iterator外还提供Enumeration枚举方法,不过现在比较过时。
323-
323+ ````
324324 // public Enumeration<E> elements() {
325325 // return new Enumeration<E>() {
326326 // int count = 0;
@@ -340,13 +340,13 @@ Vector除了iterator外还提供Enumeration枚举方法,不过现在比较过
340340 // };
341341 // }
342342 //
343-
343+ ````
344344
345345### 增删改查
346346
347347vector的增删改查既提供了自己的实现,也继承了abstractList抽象类的部分方法。
348348下面的方法是vector自己实现的。
349-
349+ ````
350350 //
351351 // public synchronized E elementAt(int index) {
352352 // if (index >= elementCount) {
@@ -404,11 +404,11 @@ vector的增删改查既提供了自己的实现,也继承了abstractList抽
404404 // ensureCapacityHelper(elementCount + 1);
405405 // elementData[elementCount++] = obj;
406406 // }
407-
407+ ````
408408### 初始容量和扩容
409409扩容方式与ArrayList基本一样,但是扩容时不是1.5倍扩容,而是有一个扩容增量。
410410
411-
411+ ````
412412 // protected int elementCount;
413413
414414 // protected int capacityIncrement;
@@ -418,11 +418,11 @@ vector的增删改查既提供了自己的实现,也继承了abstractList抽
418418 // public Vector() {
419419 // this(10);
420420 // }
421-
421+ ````
422422capacityIncrement:向量的大小大于其容量时,容量自动增加的量。如果在创建Vector时,指定了capacityIncrement的大小;则,每次当Vector中动态数组容量增加时>,增加的大小都是capacityIncrement。如果容量的增量小于等于零,则每次需要增大容量时,向量的容量将增大一倍。
423423
424424
425-
425+ ````
426426 // public synchronized void ensureCapacity(int minCapacity) {
427427 // if (minCapacity > 0) {
428428 // modCount++;
@@ -447,15 +447,15 @@ capacityIncrement:向量的大小大于其容量时,容量自动增加的量
447447 // elementData = Arrays.copyOf(elementData, newCapacity);
448448 // }
449449
450-
450+ ````
451451下面是扩容过程示意图
452452
453453
454- ![ ] ( https://img-blog.csdn.net/20180818200637720?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNjM3ODkxNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70 )
454+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404145205.png )
455455
456- ![ ] ( https://img-blog.csdn.net/20180818200704724?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNjM3ODkxNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70 )
456+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404145237.png )
457457
458- ![ ] ( https://img-blog.csdn.net/20180818200735561?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zNjM3ODkxNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70 )
458+ ![ ] ( https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404145305.png )
459459
460460### 线程安全
461461
@@ -497,10 +497,12 @@ Stack通过五个操作对Vector进行扩展,允许将向量视为堆栈。这
497497> 返回对象在堆栈中的位置,以 1 为基数。
498498
499499Stack继承Vector,他对Vector进行了简单的扩展:
500-
500+ ````
501501public class Stack<E> extends Vector<E>
502- Stack的实现非常简单,仅有一个构造方法,五个实现方法(从Vector继承而来的方法不算与其中),同时其实现的源码非常简单
502+ ````
503503
504+ Stack的实现非常简单,仅有一个构造方法,五个实现方法(从Vector继承而来的方法不算与其中),同时其实现的源码非常简单
505+ ````
504506 /**
505507 * 构造函数
506508 */
@@ -563,7 +565,7 @@ public class Stack<E> extends Vector<E>
563565 }
564566 return -1;
565567 }
566-
568+ ````
567569Stack的源码很多都是基于Vector,所以这里不再累述
568570
569571## 三个集合类之间的区别
@@ -589,24 +591,24 @@ ArrayList的优缺点
589591ArrayList和Vector的区别
590592
591593> ArrayList是线程非安全的,这很明显,因为ArrayList中所有的方法都不是同步的,在并发下一定会出现线程安全问题。那么我们想要使用ArrayList并且让它线程安全怎么办?一个方法是用Collections.synchronizedList方法把你的ArrayList变成一个线程安全的List,比如:
592- >
593- > List<String> synchronizedList = Collections.synchronizedList(list);
594- > synchronizedList.add("aaa");
595- > synchronizedList.add("bbb");
596- > for (int i = 0; i < synchronizedList.size(); i++)
597- > {
598- > System.out.println(synchronizedList.get(i));
599- > }
600-
594+ ````
595+ List<String> synchronizedList = Collections.synchronizedList(list);
596+ synchronizedList.add("aaa");
597+ synchronizedList.add("bbb");
598+ for (int i = 0; i < synchronizedList.size(); i++)
599+ {
600+ System.out.println(synchronizedList.get(i));
601+ }
602+ ````
601603另一个方法就是Vector,它是ArrayList的线程安全版本,其实现90%和ArrayList都完全一样,区别在于:
602604
603605> 1、Vector是线程安全的,ArrayList是线程非安全的
604606>
605607> 2、Vector可以指定增长因子,如果该增长因子指定了,那么扩容的时候会每次新的数组大小会在原数组的大小基础上加上增长因子;如果不指定增长因子,那么就给原数组大小* 2,源代码是这样的:
606-
608+ ````
607609 int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
608610 capacityIncrement : oldCapacity);
609-
611+ ````
610612
611613## 参考文章
612614
@@ -637,7 +639,3 @@ https://www.jianshu.com/p/c4027084ac43
637639** 程序员3T技术学习资源:** 一些程序员学习技术的资源大礼包,关注公众号后,后台回复关键字 ** “资料”** 即可免费无套路获取。
638640
639641![ ] ( https://img-blog.csdnimg.cn/20190829222750556.jpg )
640-
641-
642-
643-
0 commit comments