Skip to content

Commit c914387

Browse files
committed
集合类modify
1 parent 6d3c2f5 commit c914387

10 files changed

+1717
-1889
lines changed

docs/java/collection/Java集合类总结.md

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Table of Contents
2-
1+
# 目录
32
* [Colletion,iterator,comparable](#colletion,iterator,comparable)
43
* [List](#list)
54
* [Map](#map)
@@ -13,23 +12,8 @@
1312
* [Java技术江湖](#java技术江湖)
1413
* [个人公众号:黄小斜](#个人公众号:黄小斜)
1514

16-
17-
---
18-
title: Java集合框架学习总结
19-
date: 2018-07-08 22:03:44
20-
tags:
21-
- Java集合
22-
categories:
23-
- 后端
24-
- 技术总结
25-
---
26-
27-
28-
2915
这篇总结是基于之前博客内容的一个整理和回顾。
3016

31-
32-
3317
这里先简单地总结一下,更多详细内容请参考我的专栏:深入浅出Java核心技术
3418

3519
https://blog.csdn.net/column/details/21930.html
@@ -40,8 +24,6 @@ https://blog.csdn.net/column/details/21930.html
4024
以下总结不保证全对,如有错误,还望能够指出。谢谢
4125
<!-- more -->
4226

43-
44-
4527
## Colletion,iterator,comparable
4628

4729

@@ -55,12 +37,8 @@ List接口下的实现类有ArrayList,linkedlist,vector等等,一般就是
5537
ArrayList的扩容方式是1.5倍扩容,这样扩容避免2倍扩容可能浪费空间,是一种折中的方案。
5638
另外他不是线程安全,vector则是线程安全的,它是两倍扩容的。
5739

58-
5940
linkedlist没啥好说的,多用于实现链表。
6041

61-
62-
63-
6442
## Map
6543

6644

docs/java/collection/Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理.md

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
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+
````
147146
ArrayList提供一个清空数组的办法,方法是将所有元素置为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

284284
ArrayList是线程不安全的。在其迭代器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接口,即提供了随机访问功能,提供提供
301301
Vector 实现了Cloneable接口,支持clone()方法,可以被克隆。
302302

303303
vector底层数组不加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+
````
322322
Vector除了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

347347
vector的增删改查既提供了自己的实现,也继承了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+
````
422422
capacityIncrement:向量的大小大于其容量时,容量自动增加的量。如果在创建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
499499
Stack继承Vector,他对Vector进行了简单的扩展:
500-
500+
````
501501
public 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+
````
567569
Stack的源码很多都是基于Vector,所以这里不再累述
568570

569571
## 三个集合类之间的区别
@@ -589,24 +591,24 @@ ArrayList的优缺点
589591
ArrayList和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

Comments
 (0)