diff --git a/docs/notes/Java 容器.md b/docs/notes/Java 容器.md index 803422d8..61fe2c1f 100644 --- a/docs/notes/Java 容器.md +++ b/docs/notes/Java 容器.md @@ -126,7 +126,7 @@ public class ArrayList extends AbstractList private static final int DEFAULT_CAPACITY = 10; ``` -

+

### 2. 扩容 @@ -170,7 +170,7 @@ private void grow(int minCapacity) { ### 3. 删除元素 -需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看出 ArrayList 删除元素的代价是非常高的。 +需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看到 ArrayList 删除元素的代价是非常高的。 ```java public E remove(int index) { @@ -185,34 +185,7 @@ public E remove(int index) { } ``` -### 4. Fail-Fast - -modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。 - -在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException。 - -```java -private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException{ - // Write out element count, and any hidden stuff - int expectedModCount = modCount; - s.defaultWriteObject(); - - // Write out size as capacity for behavioural compatibility with clone() - s.writeInt(size); - - // Write out all elements in the proper order. - for (int i=0; i list = new CopyOnWriteArrayList<>(); ## CopyOnWriteArrayList -### 读写分离 +### 1. 读写分离 写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。 @@ -398,7 +378,7 @@ private E get(Object[] a, int index) { } ``` -### 适用场景 +### 2. 适用场景 CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景。 @@ -430,13 +410,14 @@ transient Node first; transient Node last; ``` -

+

### 2. 与 ArrayList 的比较 -- ArrayList 基于动态数组实现,LinkedList 基于双向链表实现; -- ArrayList 支持随机访问,LinkedList 不支持; -- LinkedList 在任意位置添加删除元素更快。 +ArrayList 基于动态数组实现,LinkedList 基于双向链表实现。ArrayList 和 LinkedList 的区别可以归结为数组和链表的区别: + +- 数组支持随机访问,但插入删除的代价很高,需要移动大量元素; +- 链表不支持随机访问,但插入删除只需要改变指针。 ## HashMap @@ -444,16 +425,14 @@ transient Node last; ### 1. 存储结构 -内部包含了一个 Entry 类型的数组 table。 +内部包含了一个 Entry 类型的数组 table。Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。 + +

```java transient Entry[] table; ``` -Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。 - -

- ```java static class Entry implements Map.Entry { final K key; @@ -528,7 +507,7 @@ map.put("K3", "V3"); - 计算键值对所在的桶; - 在链表上顺序查找,时间复杂度显然和链表的长度成正比。 -

+

### 3. put 操作 diff --git a/notes/Java 容器.md b/notes/Java 容器.md index 803422d8..61fe2c1f 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -126,7 +126,7 @@ public class ArrayList extends AbstractList private static final int DEFAULT_CAPACITY = 10; ``` -

+

### 2. 扩容 @@ -170,7 +170,7 @@ private void grow(int minCapacity) { ### 3. 删除元素 -需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看出 ArrayList 删除元素的代价是非常高的。 +需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看到 ArrayList 删除元素的代价是非常高的。 ```java public E remove(int index) { @@ -185,34 +185,7 @@ public E remove(int index) { } ``` -### 4. Fail-Fast - -modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。 - -在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException。 - -```java -private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException{ - // Write out element count, and any hidden stuff - int expectedModCount = modCount; - s.defaultWriteObject(); - - // Write out size as capacity for behavioural compatibility with clone() - s.writeInt(size); - - // Write out all elements in the proper order. - for (int i=0; i list = new CopyOnWriteArrayList<>(); ## CopyOnWriteArrayList -### 读写分离 +### 1. 读写分离 写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。 @@ -398,7 +378,7 @@ private E get(Object[] a, int index) { } ``` -### 适用场景 +### 2. 适用场景 CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景。 @@ -430,13 +410,14 @@ transient Node first; transient Node last; ``` -

+

### 2. 与 ArrayList 的比较 -- ArrayList 基于动态数组实现,LinkedList 基于双向链表实现; -- ArrayList 支持随机访问,LinkedList 不支持; -- LinkedList 在任意位置添加删除元素更快。 +ArrayList 基于动态数组实现,LinkedList 基于双向链表实现。ArrayList 和 LinkedList 的区别可以归结为数组和链表的区别: + +- 数组支持随机访问,但插入删除的代价很高,需要移动大量元素; +- 链表不支持随机访问,但插入删除只需要改变指针。 ## HashMap @@ -444,16 +425,14 @@ transient Node last; ### 1. 存储结构 -内部包含了一个 Entry 类型的数组 table。 +内部包含了一个 Entry 类型的数组 table。Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。 + +

```java transient Entry[] table; ``` -Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。 - -

- ```java static class Entry implements Map.Entry { final K key; @@ -528,7 +507,7 @@ map.put("K3", "V3"); - 计算键值对所在的桶; - 在链表上顺序查找,时间复杂度显然和链表的长度成正比。 -

+

### 3. put 操作 diff --git a/notes/pics/image-20191208232221265.png b/notes/pics/image-20191208232221265.png new file mode 100644 index 00000000..c1759aba Binary files /dev/null and b/notes/pics/image-20191208232221265.png differ diff --git a/notes/pics/image-20191208233940066.png b/notes/pics/image-20191208233940066.png new file mode 100644 index 00000000..3a6145c1 Binary files /dev/null and b/notes/pics/image-20191208233940066.png differ diff --git a/notes/pics/image-20191208234825587.png b/notes/pics/image-20191208234825587.png new file mode 100644 index 00000000..6f596da0 Binary files /dev/null and b/notes/pics/image-20191208234825587.png differ diff --git a/notes/pics/image-20191208234948205.png b/notes/pics/image-20191208234948205.png new file mode 100644 index 00000000..7466b3e0 Binary files /dev/null and b/notes/pics/image-20191208234948205.png differ diff --git a/notes/pics/image-20191208235258643.png b/notes/pics/image-20191208235258643.png new file mode 100644 index 00000000..6c5471cc Binary files /dev/null and b/notes/pics/image-20191208235258643.png differ