diff --git a/35/target/ArrayListDemo.class b/35/target/ArrayListDemo.class
new file mode 100644
index 0000000..98f8536
Binary files /dev/null and b/35/target/ArrayListDemo.class differ
diff --git a/35/target/HashMapDemo$1.class b/35/target/HashMapDemo$1.class
new file mode 100644
index 0000000..0ead949
Binary files /dev/null and b/35/target/HashMapDemo$1.class differ
diff --git a/35/target/HashMapDemo.class b/35/target/HashMapDemo.class
new file mode 100644
index 0000000..f97b55e
Binary files /dev/null and b/35/target/HashMapDemo.class differ
diff --git a/35/target/LinkedListDemo.class b/35/target/LinkedListDemo.class
new file mode 100644
index 0000000..e2aa6f6
Binary files /dev/null and b/35/target/LinkedListDemo.class differ
diff --git a/week_01/05/01-ArrayList.md b/week_01/05/01-ArrayList.md
new file mode 100644
index 0000000..c8cca2a
--- /dev/null
+++ b/week_01/05/01-ArrayList.md
@@ -0,0 +1,398 @@
+# ArrayList 源码分析
+
+## TOP 带着问题看源码
+
+1. List list = new ArrayList(20) 扩容了几次
+2. ArrayList 怎么实现数组动态扩容,扩容时机,扩容倍数
+3. ArrayList 怎么实现remove的
+4. 为什么remove具体元素性能差
+5. ArrayList 是怎么序列化的
+
+## 1. 继承和实现关系
+
+
+
+
+
+- *RandomAccess 接口*
+
+ 标记该类具有快速随机访问能力。当一个集合拥有该能力时候,采用for循环遍历会很快;若没有则采用Iterator迭代器最快。参考ArrayList的indexOf(Object o)方法和AbstractList的indexOf(Object o)方法区别。
+
+- *Serializable 接口*
+
+ 标记该类是可序列化的。
+
+- *Cloneable 接口*
+
+ 标记该类对象能够被Object.clone()
+
+ 根据重写的clone方法实现主要分为如下两种克隆方式
+
+ 1. 浅克隆
+
+ 只copy对象本身和对象中的基本变量,不copy包含引用的对象
+
+ 2. 深克隆
+
+ 不仅copy对象本身,还copy对象包含的引用对象
+
+- *AbstractList 抽象类*
+
+ 提供一些基础方法: IndexOf、clear、addAll、iterator等
+
+## 2. 成员变量分析
+
+```java
+// 默认容量
+private static final int DEFAULT_CAPACITY = 10;
+// 空数组实例(为0时候)
+private static final Object[] EMPTY_ELEMENTDATA = {};
+// 默认大小时候的空数组实例
+private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+// 存储数组
+transient Object[] elementData;
+// 数组大小
+private int size;
+// 数组最大容量,减8是因为可能一些VM会在数组保留一些header,防止OOM
+private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+```
+
+## 3. 构造方法分析
+
+### 3.1 无参构造方法
+
+默认赋值一个空数组实例
+
+```java
+public ArrayList() {
+ this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+}
+```
+
+### 3.2 带初始化容量的构造方法
+
+可以看到是由参数的大小来创建对应大小的 elementData 数组,回到 **TOP 1** 问题,可以看出来不会发生扩容,也就是0次
+
+```java
+public ArrayList(int initialCapacity) {
+ if (initialCapacity > 0) {
+ this.elementData = new Object[initialCapacity];
+ } else if (initialCapacity == 0) {
+ this.elementData = EMPTY_ELEMENTDATA;
+ } else {
+ throw new IllegalArgumentException("Illegal Capacity: "+
+ initialCapacity);
+ }
+}
+```
+
+### 3.3 带集合内容的构造方法
+
+把传过来的集合转化为数组赋值给 elementData 数组
+
+```java
+public ArrayList(Collection extends E> c) {
+ elementData = c.toArray();
+ if ((size = elementData.length) != 0) {
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ if (elementData.getClass() != Object[].class)
+ elementData = Arrays.copyOf(elementData, size, Object[].class);
+ } else {
+ // replace with empty array.
+ this.elementData = EMPTY_ELEMENTDATA;
+ }
+}
+```
+
+## 4. 核心方法分析
+
+### 4.1 获取元素
+
+先 check ,再按照 index 取。check也是为了保证工程中不会出现奇奇怪怪的结果
+
+```java
+public E get(int index) {
+ rangeCheck(index);
+
+ return elementData(index);
+}
+```
+
+使用 final 修饰的数组来接收存储数组,对其遍历。 modCount 变量和 final 修饰的 expectedModCount 进行对比来判断是否存在并发读写情况
+
+```java
+public void forEach(Consumer super E> action) {
+ Objects.requireNonNull(action);
+ final int expectedModCount = modCount;
+ @SuppressWarnings("unchecked")
+ final E[] elementData = (E[]) this.elementData;
+ final int size = this.size;
+ for (int i=0; modCount == expectedModCount && i < size; i++) {
+ action.accept(elementData[i]);
+ }
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+}
+```
+
+### 4.2 新增元素
+
+#### 4.2.1 add(E e)
+
+把一个元素新增到elementData,主要涉及如下几点
+
+1. modCount++ 声明我新增元素了,在并发情况下起到容量是否发生变化作用
+2. 如果容量不足,则扩容数组大小(参考下面grow方法)
+
+```java
+public boolean add(E e) {
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ elementData[size++] = e;
+ return true;
+}
+```
+
+####4.2.2 add(int index, E element)
+
+按照index位置来插入元素,和上面方法同理。
+
+```java
+public void add(int index, E element) {
+ rangeCheckForAdd(index);
+
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ elementData[index] = element;
+ size++;
+}
+```
+
+####4.2.3 grow(int minCapacity)
+
+第4行可以看到,使用位运算扩容了 1.5 倍大小空间,至于为啥是1.5倍,我猜是经验值。
+
+回到 **TOP 2** 问题,可以明白了扩容机制是通过数组 copy方式,时机就是容量不够的时候,倍数是1.5倍
+
+```java
+private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+```
+
+### 4.3 更新元素
+
+直接数组下标覆盖,返回旧值,至于为什么返回的是旧值,可能一方面是根据下标查询不是很影响性能索性给查询出来,另一方面下标和新值请求者都清楚也没必要返回。
+
+```java
+public E set(int index, E element) {
+ rangeCheck(index);
+
+ E oldValue = elementData(index);
+ elementData[index] = element;
+ return oldValue;
+}
+```
+
+### 4.4 删除元素
+
+#### 4.4.1 remove(int index)
+
+计算要删除的下标后一位到数组末尾的长度,然后通过copy这段长度覆盖到原数组的位置,最后把最后一位置null,实现删除。
+
+回到 **TOP 3** 问题,可以明白删除机制也是通过数组copy覆盖的思想来实现的
+
+```java
+public E remove(int index) {
+ rangeCheck(index);
+
+ modCount++;
+ E oldValue = elementData(index);
+ // 计算长度
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ // param1: 源数组
+ // param2: 源数组要复制的起始位置
+ // param3: 目标数组
+ // param4: 目标数组放置的起始位置
+ // param5: 复制的长度
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+
+ return oldValue;
+}
+```
+
+####4.4.2 remove(Object o)
+
+首先分为两个场景,第一个是要删除的元素是null,第二个是要删除的是非null的。
+
+主要是遍历要找的元素,找到该元素对应的index,然后使用 fastRemove(index) 去快速删除
+
+回到 **TOP 4** 问题,可以明白计算某个元素下标的时间复杂度是 O(n) 的,所以性能没有直接根据下标删除好
+
+```java
+public boolean remove(Object o) {
+ if (o == null) {
+ for (int index = 0; index < size; index++)
+ if (elementData[index] == null) {
+ fastRemove(index);
+ return true;
+ }
+ } else {
+ for (int index = 0; index < size; index++)
+ if (o.equals(elementData[index])) {
+ fastRemove(index);
+ return true;
+ }
+ }
+ return false;
+}
+```
+
+#### 4.4.3 fastRemove(int index)
+
+因为调用该方法都是内部计算index后调用的,所以不需要再校验index是否越界,也不需要返回oldValue。
+
+```java
+private void fastRemove(int index) {
+ modCount++;
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+}
+```
+
+#### 4.4.4 clear()
+
+遍历赋值null,size重置为0
+
+## 5. 序列化
+
+首先我们在最开始就有介绍 ArrayList 类实现的有 Serializable 接口,但是我们在成员变量那一节看到的存储数组 elementData 是有 `transient` 修饰的,也就是elementData不会参与默认序列化,那实现这个 Serializable 接口还有意义么?
+
+其实仔细观察类里的方法你会发现有两个与序列化的流有关系的方法:`writeObject` 、`readObject`
+
+在序列化过程中如果有这两个方法,会默认调用这两个方法进行用户自定义的序列化和反序列化,如果没有才走默认序列化。
+
+那么我们知道作者的序列化是自定义了,那为什么这样做呢,为什么不直接使用默认序列化呢?
+
+我们可以想下,每次扩容1.5倍,那这个数组实际会有一些空间扩容后还未被填充,如果使用默认序列化则会将null也给序列化进去。
+
+接下来我们来看一下自定义序列化方法具体的实现:
+
+###5.1 writeObject
+
+写入数组大小,遍历写入数组元素,检查并发冲突
+
+```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 0) {
+ // be like clone(), allocate array based upon size not capacity
+ int capacity = calculateCapacity(elementData, size);
+ SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
+ ensureCapacityInternal(size);
+
+ Object[] a = elementData;
+ // Read in all elements in the proper order.
+ for (int i=0; i writeObject0 -> writeOrdinaryObject -> writeSerialData`
+
+代码如下所示,可以看到会先判断是否有 writeObject 方法,如果有的话,会通过反射的方式调用序列化对象的writeObject方法,如果没有则使用默认序列化方式
+
+```java
+private void writeSerialData(Object obj, ObjectStreamClass desc)
+ throws IOException
+{
+ ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
+ for (int i = 0; i < slots.length; i++) {
+ ObjectStreamClass slotDesc = slots[i].desc;
+ if (slotDesc.hasWriteObjectMethod()) {
+ PutFieldImpl oldPut = curPut;
+ curPut = null;
+ SerialCallbackContext oldContext = curContext;
+
+ if (extendedDebugInfo) {
+ debugInfoStack.push(
+ "custom writeObject data (class \"" +
+ slotDesc.getName() + "\")");
+ }
+ try {
+ curContext = new SerialCallbackContext(obj, slotDesc);
+ bout.setBlockDataMode(true);
+ slotDesc.invokeWriteObject(obj, this);
+ bout.setBlockDataMode(false);
+ bout.writeByte(TC_ENDBLOCKDATA);
+ } finally {
+ curContext.setUsed();
+ curContext = oldContext;
+ if (extendedDebugInfo) {
+ debugInfoStack.pop();
+ }
+ }
+
+ curPut = oldPut;
+ } else {
+ defaultWriteFields(obj, slotDesc);
+ }
+ }
+}
+```
\ No newline at end of file
diff --git a/week_01/05/02-LinkedList.md b/week_01/05/02-LinkedList.md
new file mode 100644
index 0000000..35eac97
--- /dev/null
+++ b/week_01/05/02-LinkedList.md
@@ -0,0 +1,227 @@
+# LinkedList 源码分析
+
+## TOP 带着问题看源码
+
+1. LinkedList 采用的数据结构是什么
+
+## 1. 继承和实现关系
+
+
+
+- *AbstractSequentialList 实现类*
+
+ 提供一些围绕着iterator的基础方法
+
+- *List 接口*
+
+ 提供 list 功能
+
+- *Deque 接口*
+
+ 提供双端操作功能,以此可以猜出 LinkedList 数据结构是一个双向链表
+
+- *Cloneable 接口*
+
+ 标记该类对象能够被Object.clone()
+
+- *Serializable 接口*
+
+ 标记该类是可序列化的
+
+## 2. 成员变量分析
+
+```java
+// 容量
+transient int size = 0;
+// 首节点
+transient Node first;
+// 尾节点
+transient Node last;
+```
+
+接下来看节点 Node的成员变量
+
+```java
+// 节点的值
+E item;
+// next 指针
+Node next;
+// prev 指针
+Node prev;
+```
+
+回到 **TOP 1** 问题,根据实现可以明白其数据结构是一个双向链表
+
+## 3. 构造方法分析
+
+一个是默认无参,一个是带集合内容的。
+
+把传来的集合新增入当前list
+
+```java
+public LinkedList(Collection extends E> c) {
+ this();
+ addAll(c);
+}
+```
+
+接下来看节点 Node的构造方法,根据入参默认维护两个指针。
+
+```java
+Node(Node prev, E element, Node next) {
+ this.item = element;
+ this.next = next;
+ this.prev = prev;
+}
+```
+
+## 4. 核心方法分析
+
+### 4.1 获取元素
+
+先check,然后通过 node(index)方法取
+
+```java
+public E get(int index) {
+ checkElementIndex(index);
+ return node(index).item;
+}
+```
+
+node 方法通过判断索引 index 的范围(若是大于一半集合容量,则从尾结点向前遍历,若小于则从头结点向后遍历)来尽量高效的取到对应的节点
+
+### 4.2 新增元素
+
+#### 4.2.1 add(E e)
+
+尾插
+
+```java
+public boolean add(E e) {
+ linkLast(e);
+ return true;
+}
+```
+
+构建一个next指针是null,prev指针是尾结点的新节点newNode,如果尾结点不为空则将尾结点的next结点指向newNode,否则将头结点指向newNode
+
+```java
+void linkLast(E e) {
+ final Node l = last;
+ final Node newNode = new Node<>(l, e, null);
+ last = newNode;
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+#### 4.2.2 add(int index, E element)
+
+先check,如果插入的还是尾部,则调用 linkLast 方法,否则先获取到索引 index 对应的节点然后调用 linkBefore 方法
+
+```java
+public void add(int index, E element) {
+ checkPositionIndex(index);
+
+ if (index == size)
+ linkLast(element);
+ else
+ linkBefore(element, node(index));
+}
+```
+
+构建一个 prev 指针是索引 index - 1 对应节点,next节点是索引 index 对应节点的新节点newNode **(图中绿色部分)**。然后把index节点的prev指向newNode **(图中蓝色部分)**,如果要插入的是第一个位置,则把 first 指针指向newNode,否则维护剩余的指针关系(index - 1 节点的next指向newNode)**(图中红色部分)**
+
+
+
+
+
+### 4.3 更新元素
+
+根据下标位置获取节点,然后把节点的值进行覆盖
+
+```java
+public E set(int index, E element) {
+ checkElementIndex(index);
+ Node x = node(index);
+ E oldVal = x.item;
+ x.item = element;
+ return oldVal;
+}
+```
+
+### 4.4 删除元素
+
+#### 4.4.1 remove(int index)
+
+先check,然后先获取index对应节点最后调用unlink方法
+
+```java
+public E remove(int index) {
+ checkElementIndex(index);
+ return unlink(node(index));
+}
+```
+
+同按照下标新增那块逻辑差不多,去除一个节点(prev next item置null),重新维护指针关系
+
+```java
+E unlink(Node x) {
+ // assert x != null;
+ final E element = x.item;
+ final Node next = x.next;
+ final Node prev = x.prev;
+
+ if (prev == null) {
+ first = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+
+ if (next == null) {
+ last = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+
+ x.item = null;
+ size--;
+ modCount++;
+ return element;
+}
+```
+
+#### 4.4.2 remove(Object o)
+
+遍历要找的元素的index,然后调用unlink方法
+
+```java
+public boolean remove(Object o) {
+ if (o == null) {
+ for (Node x = first; x != null; x = x.next) {
+ if (x.item == null) {
+ unlink(x);
+ return true;
+ }
+ }
+ } else {
+ for (Node x = first; x != null; x = x.next) {
+ if (o.equals(x.item)) {
+ unlink(x);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+```
+
+#### 4.4.3 clear()
+
+遍历赋值null,size重置为0
\ No newline at end of file
diff --git a/week_01/05/03-HashMap.md b/week_01/05/03-HashMap.md
new file mode 100644
index 0000000..1f79caa
--- /dev/null
+++ b/week_01/05/03-HashMap.md
@@ -0,0 +1,421 @@
+# HashMap 源码分析
+
+## TOP 带着问题看源码
+
+1. HashMap 的数据结构是什么
+2. Hash冲突解决办法是什么,什么时候会转为红黑树
+3. 容量为什么为2的N次幂
+4. HashMap 是怎么扩容的
+5. HashMap 为什么使用红黑树
+
+## 1. 继承和实现关系
+
+
+
+- *AbstractMap 实现类*
+
+ 提供一些围绕着iterator的基础方法
+
+- *Cloneable 接口*
+
+ 标记该类对象能够被Object.clone()
+
+- *Serializable 接口*
+
+ 标记该类是可序列化的。
+
+## 2. 成员变量分析
+
+```java
+// 默认初始容量
+static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+// 最大容量
+static final int MAXIMUM_CAPACITY = 1 << 30;
+// 负载因子
+static final float DEFAULT_LOAD_FACTOR = 0.75f;
+// 大于8转为树
+static final int TREEIFY_THRESHOLD = 8;
+// 小于6转为链表
+static final int UNTREEIFY_THRESHOLD = 6;
+// 当内部数组size小于64并且单位置冲突超过8,优先扩容,而不是树化
+static final int MIN_TREEIFY_CAPACITY = 64;
+```
+
+## 3. 构造方法分析
+
+### 3.1 无参构造方法
+
+使用默认负载因子做全局负载因子
+
+```java
+public HashMap() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+}
+```
+
+### 3.2 带初始化容量的构造方法
+
+指定容量和默认负载因子,走下面带负载因子的构造方法
+
+```java
+public HashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+}
+```
+
+### 3.3 带初始化容量和负载因子的构造方法
+
+check参数,容量转为参数的最小2次幂。
+
+为什么要转为2的N次幂呢,主要是为了后面做取模运算可以使用性能更好地位运算来代替%
+
+回到 **TOP 3** 问题,可以明白了为什么这样设计。
+
+```java
+public HashMap(int initialCapacity, float loadFactor) {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal initial capacity: " +
+ initialCapacity);
+ if (initialCapacity > MAXIMUM_CAPACITY)
+ initialCapacity = MAXIMUM_CAPACITY;
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new IllegalArgumentException("Illegal load factor: " +
+ loadFactor);
+ this.loadFactor = loadFactor;
+ this.threshold = tableSizeFor(initialCapacity);
+}
+```
+
+## 4. 核心方法分析
+
+### 4.1 获取元素
+
+先计算key的hash值,然后调用getNode方法获取到节点的值
+
+```java
+public V get(Object key) {
+ Node e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+}
+```
+
+我们先来看hash方法,可以看到是通过高半区与低半区进行异或,为什么要这样做呢?
+
+主要是把高位的特征也给加入到扰动计算中,降低低位的冲突。那降低低位冲突目的是啥呢?
+
+其实可以从取下标位置(n-1) & hash来分析,n为2的N次幂,在n - 1在二进制中低位肯定全是1,那和hash做与运算相当于结果是hash低位的截取操作。也就是hash的冲突情况完全取决于hash自身低位的冲突情况。
+
+```java
+static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+这段代码的主要逻辑就是先计算下标,然后对比hash值和value值来获取元素(①)。注意的是如果节点是tree,会使用递归来遍历查找,时间复杂度则会转为O(nlogn)(②)。如果是链表则会遍历来获取,这段长度比较短并不会太影响性能(③)。
+
+```java
+final Node getNode(int hash, Object key) {
+ Node[] tab; Node first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ // ①
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ if ((e = first.next) != null) {
+ // ②
+ if (first instanceof TreeNode)
+ return ((TreeNode)first).getTreeNode(hash, key);
+ do {
+ // ③
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
+}
+```
+
+### 4.2 新增&更新元素
+
+#### 4.2.1 put(K key, V value)
+
+计算 key 的 hash,onlyIfAbsent 设置为 false (默认覆盖旧的 key ),evict 设置为 true (代表会逐出元素,在LinkedHashMap 实现 LRU 时候的重写方法 removeEldestEntry 里会用到。在序列化也会涉及到,序列化时候会设置为 false)
+
+```java
+public V put(K key, V value) {
+ return putVal(hash(key), key, value, false, true);
+}
+```
+
+#### 4.2.2 putIfAbsent(K key, V value)
+
+对比默认的 put 方法,只是把 onlyIfAbsent 设置为true,表示有则不覆盖
+
+```java
+public V putIfAbsent(K key, V value) {
+ return putVal(hash(key), key, value, true, true);
+}
+```
+
+####4.2.3 resize()
+
+在分析 putVal 方法之前,我们先分析扩容方法 resize
+
+核心逻辑主要分为以下五个部分
+
+① 没超过最大值,且数组元素超过了64的阈值则扩容为原来的2倍
+
+② 无冲突情况数组桶重新hash
+
+③ 节点是红黑树,走红黑树拆分逻辑,和下面链表差不多,会增加阈值判断,若扩容后节点数小于6则会转为链表
+
+④ 节点是链表,④-① 和 ④-② 是判断hash值新增bit位是0还是1的情况,来分散链表
+
+⑤ 对④做最后的铺垫,根据不同情况放置不同位置
+
+```java
+final Node[] resize() {
+ Node[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ // ①
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node[] newTab = (Node[])new Node[newCap];
+ table = newTab;
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ // ②
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ // ③
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ // ④
+ do {
+ next = e.next;
+ // ④-①
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ // ④-②
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ // ⑤
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+}
+```
+
+回到 **TOP 4** 问题,可以明白了hashMap扩容的机制和场景
+
+####4.2.3 putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict)
+
+核心逻辑主要分为如下6个步骤
+
+① 先检查存储数组是否为空(例如使用默认构造方法没有设置初始值),为空了则调用上面的扩容方法resize
+
+② 然后计算hash值对应的位置是否为空,如果为空则构建一个next节点是null的空节点放到该位置
+
+③ 如果位置不为空,hash值相同,且key相同则更新元素
+
+④ 如果节点是 treeNode,则调用 Tree 版本的putTreeVal,逻辑都差不多,就是遍历左右子树,查到了就返回查不到就构建一个
+
+⑤ 如果节点是链表,首先遍历到链表最后一位加入构建的节点,然后 check 阈值是否要转为红黑树,最后若存在相同的key就覆盖
+
+⑥ 超过最大容量则扩容处理
+
+```java
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node[] tab; Node p; int n, i;
+ // ①
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ // ②
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ Node e; K k;
+ // ③
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ // ④
+ else if (p instanceof TreeNode)
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ // ⑤
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ }
+ ++modCount;
+ // ⑥
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
+ return null;
+}
+```
+
+回到 **TOP 2** 问题,可以明白了解决冲突的方式是采用了拉链法,当链表长度大于8则会转为红黑树
+
+### 4.3 删除元素
+
+计算hash值,然后调用 removeNode 方法
+
+```java
+public V remove(Object key) {
+ Node e;
+ return (e = removeNode(hash(key), key, null, false, true)) == null ?
+ null : e.value;
+}
+```
+
+核心逻辑主要分为三个步骤
+
+① 定位元素的位置
+
+② 找到键相同的元素
+
+③ 删除相关节点
+
+```java
+final Node removeNode(int hash, Object key, Object value,
+ boolean matchValue, boolean movable) {
+ Node[] tab; Node p; int n, index;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ // ①
+ (p = tab[index = (n - 1) & hash]) != null) {
+ Node node = null, e; K k; V v;
+ // ②
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ node = p;
+ else if ((e = p.next) != null) {
+ if (p instanceof TreeNode)
+ node = ((TreeNode)p).getTreeNode(hash, key);
+ else {
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key ||
+ (key != null && key.equals(k)))) {
+ node = e;
+ break;
+ }
+ p = e;
+ } while ((e = e.next) != null);
+ }
+ }
+ // ③
+ if (node != null && (!matchValue || (v = node.value) == value ||
+ (value != null && value.equals(v)))) {
+ if (node instanceof TreeNode)
+ ((TreeNode)node).removeTreeNode(this, tab, movable);
+ else if (node == p)
+ tab[index] = node.next;
+ else
+ p.next = node.next;
+ ++modCount;
+ --size;
+ afterNodeRemoval(node);
+ return node;
+ }
+ }
+ return null;
+}
+```
+
+## 5. 总结
+
+### 5.1 数据结构的设计
+
+总体是一个散列表的设计,底层使用数组,这里为了方便位运算,会将size重置为最接近你所设置的2^n,这样取模就可以用位运算代替了。
+
+### 5.2 冲突的处理
+
+hash 冲突采用的是拉链法,王争老师的《数据结构与算法之美》专栏里有讲解,对于数据较少的话使用开放寻址法处理冲突较为合适,例如ThreadLocal,显然不适合HashMap。
+
+回到 **TOP 1** 问题,可以明白了 HashMap 底层使用的数组+链表(红黑树) 来实现的。
+
+### 5.3 为什么使用红黑树
+
+当链足够长,HashMap设置的阈值是8 超过8就会转成红黑树,原因是链表的时间复杂度在数据多的情况下会表现很差。至于为什么使用的是红黑树而不是相同时间复杂度实现更为简单的跳表呢? 实际上使用跳表也不是不可以,但是HashMap主要的场景还是散列表,每个冲突都用跳表结构属实有些浪费空间。
+
+解释了 **TOP 5问题**
+
+### 5.4 扩容
+
+在扩容期间,为了避免单链过长,扩容时候会对链进行分开处理,所以就又有了冲突的长度小于6会把树节点重新转化为链表。
\ No newline at end of file
diff --git a/week_01/07/ArrayList-007.md b/week_01/07/ArrayList-007.md
new file mode 100644
index 0000000..c512c70
--- /dev/null
+++ b/week_01/07/ArrayList-007.md
@@ -0,0 +1,148 @@
+ArrayList源码解析
+前言:源码都是基于JDK1.8。ArrayList是我们开发中比较常用的一个集合类,底层是基于数组实现的,现在就来看一看里面是怎么实现的
+1.首先看一下定义的成员变量
+ int DEFAULT_CAPACITY = 10; //默认初始化数组的大小
+ Object[] EMPTY_ELEMENTDATA = {}; //空数组对象
+ Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //默认大小空数组对象,跟上面变量的区别在于,新建一个集合对象时,没有指定大小,就用这个对象
+ transient Object[] elementData; //集合CRUD时操作的数组,不可序列化
+ int size; //集合的大小
+ int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //最大的数组大小,为啥要-8,我也没搞懂
+2.成员变量之后来看一下几个构造方法
+ //带容量参数的构造方法
+ public ArrayList(int initialCapacity) {
+ if (initialCapacity > 0) { //容量大于0,直接新建一个Object数组
+ this.elementData = new Object[initialCapacity];
+ } else if (initialCapacity == 0) { //容量等于0,直接用之前声明的空数组对象
+ this.elementData = EMPTY_ELEMENTDATA;
+ } else { //如果小于0,参数是不合法的,抛出异常
+ throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
+ }
+ }
+ //不带参数的构造方法
+ public ArrayList() {
+ this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //默认大小空数组对象
+ }
+ //带集合参数的构造方法
+ public ArrayList(Collection extends E> c) {
+ elementData = c.toArray(); //先把集合转换为数组,下面会分析toArray()这个方法
+ if ((size = elementData.length) != 0) { //把集合的大小赋值给size,如果传入集合的长度不为0,再进去判断数组的Class对象是不是Object[].class
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ if (elementData.getClass() != Object[].class) //如果不是Object[].class,那就得复制整个数组里面的元素
+ elementData = Arrays.copyOf(elementData, size, Object[].class);
+ } else { //如果集合大小等于0(不可能小于0,因为小于0都会抛异常),赋值为空数组对象
+ // replace with empty array.
+ this.elementData = EMPTY_ELEMENTDATA;
+ }
+ }
+3.构造方法说完之后,再来看几个重要的方法,其它方法就不一一分析了
+ //首先看一下add方法,有两个add方法,一个是向数组添加元素,一个是向数组指定位置添加元素
+ public boolean add(E e) {
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ elementData[size++] = e;
+ return true;
+ }
+ //这个方法是确保数组的容量,让新加的元素能加到数组中去
+ private void ensureCapacityInternal(int minCapacity) {
+ ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
+ }
+ private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0) //如果最小容量大于数组的大小,扩容数组
+ grow(minCapacity);
+ }
+ //扩容方法
+ private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1); //先把数组扩容1.5倍
+ if (newCapacity - minCapacity < 0) //如果扩容后的大小还小于minCapacity,那就直接把大小改成minCapacity
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0) //如果扩容后大小大于最大数组大小,看minCapacity大小是否大于最大数组大小,如果大于返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity); //把数组复制一份返回
+ }
+ private static int hugeCapacity(int minCapacity) {
+ if (minCapacity < 0) // overflow
+ throw new OutOfMemoryError();
+ return (minCapacity > MAX_ARRAY_SIZE) ? //这个地方返回Integer.MAX_VALUE,和上面最大数组大小,会不会有问题?
+ Integer.MAX_VALUE :
+ MAX_ARRAY_SIZE;
+ }
+ //计算容量大小
+ private static int calculateCapacity(Object[] elementData, int minCapacity) {
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //如果数组为空,取默认大小和最小容量大小中的最大值
+ return Math.max(DEFAULT_CAPACITY, minCapacity);
+ } //否则返回最小容量大小
+ return minCapacity;
+ }
+
+ //在指定位置插入数据
+ public void add(int index, E element) {
+ rangeCheckForAdd(index); //判断添加元素的位置是否越界
+ ensureCapacityInternal(size + 1); // Increments modCount!! //跟上面的方法一样,判断是否要扩容,如果要扩容,扩容后返回新数组
+ System.arraycopy(elementData, index, elementData, index + 1, size - index); //在指定位置插入元素后,把指定位置后的元素全部向后移一位
+ elementData[index] = element;
+ size++;
+ }
+
+ //批量增加的方法
+ public boolean addAll(int index, Collection extends E> c) {
+ rangeCheckForAdd(index); //判断添加元素的位置是否越界
+ Object[] a = c.toArray(); //把要添加的集合转换为Object[]
+ int numNew = a.length;
+ //判断是否需要扩容,如果需要的话,大小为size + numNew
+ ensureCapacityInternal(size + numNew); // Increments modCount
+ int numMoved = size - index;
+ if (numMoved > 0) //判断原数组里面的元素是否需要移动,如果需要,移动到index + numNew,为什么是这个长度呢?因为index + numNew的长度要放批量新加的集合
+ System.arraycopy(elementData, index, elementData, index + numNew, numMoved);
+ //把新加的集合添加到数组中
+ System.arraycopy(a, 0, elementData, index, numNew);
+ size += numNew; //把size变成添加集合后的大小
+ return numNew != 0;
+ }
+
+ //get方法
+ public E get(int index) {
+ rangeCheck(index); //判断元素下标是否大于size
+ return elementData(index); //从数组中取元素返回
+ }
+
+ //删除方法,这里有两个,一个是根据下标删除,并返回删除的元素,时间复杂度为O(1)。还有一个是根据元素删除,返回是否删除成功,需要循环数组中的元素,最好时间复杂度为O(1),最坏时间复杂度为O(n),平均时间复杂度为O(n)
+ public E remove(int index) {
+ rangeCheck(index); //判断元素下标是否大于size
+ modCount++;
+ E oldValue = elementData(index); //取出要删除的元素,最后返回
+ int numMoved = size - index - 1; //得到要移动元素的长度
+ if (numMoved > 0) //如果元素大于0,移动数组
+ System.arraycopy(elementData, index+1, elementData, index, numMoved);
+ elementData[--size] = null; // clear to let GC do its work //把最后一个元素置为空
+ return oldValue;
+ }
+
+ //截取集合
+ public List subList(int fromIndex, int toIndex) {
+ subListRangeCheck(fromIndex, toIndex, size); //检查边界,是否合法
+ return new SubList(this, 0, fromIndex, toIndex); //每次截取集合,会返回一个新的SubList,这个新的SubList又实现了集合中的大部分方法。这里就不贴代码了,太长了
+ }
+
+ //1.8新增的循环集合方法
+ @Override
+ public void forEach(Consumer super E> action) {
+ Objects.requireNonNull(action); //判断传的函数式对象是否为空
+ final int expectedModCount = modCount; //操作次数
+ @SuppressWarnings("unchecked")
+ final E[] elementData = (E[]) this.elementData; //底层数组
+ final int size = this.size; //数组大小
+ for (int i=0; modCount == expectedModCount && i < size; i++) { //这里多一个判断是因为在集合循环的时候,不能去增、删、改里面的元素
+ action.accept(elementData[i]);
+ }
+ if (modCount != expectedModCount) { //如果有增、删、改操作的话,就抛异常
+ throw new ConcurrentModificationException();
+ }
+ }
+
+4.总结:ArrayList就分析到这了,如有错误请指正,或者有建议也欢迎提出来一起讨论
+
+
diff --git a/week_01/07/HashMap-007.md b/week_01/07/HashMap-007.md
new file mode 100644
index 0000000..966a88e
--- /dev/null
+++ b/week_01/07/HashMap-007.md
@@ -0,0 +1,218 @@
+HashMap源码分析
+1.关于HashMap有几个前提先说清楚,要不然后面看代码的时候也是懵的(以JDK1.8为例来说明)
+ (1).HashMap继承(extends)自 AbstractMap抽象(abstract)类 实现(implements)了Map,Cloneable, Serializable三个接口
+ (2).DEFAULT_INITIAL_CAPACITY = 1 << 4;初始容量为1*2*2*2*2=16(左移1位就是乘以2,左移4位就是乘以2^4)。MAXIMUM_CAPACITY = 1 << 30;最大容量为(2^30)
+ (3).DEFAULT_LOAD_FACTOR = 0.75f;初始加载因子,容量*加载因子=阀值,如果添加的元素大于这个阀值,就两倍扩容
+ (4).TREEIFY_THRESHOLD = 8;这个变量我的理解为,如果一个Key对应的Hash表中的元素超过8个,就转换为树(这里转换为红黑树),在后面还有一个值决定是不是转换为树
+ (5).UNTREEIFY_THRESHOLD = 6;如果一个Key对应的Hash表中的元素小于6个,如果元素结构是红黑树的话,就转换为单链表结构
+ (6).MIN_TREEIFY_CAPACITY = 64;这个变量在treeifyBin这个方法中,只有HashMap中的元素大于64才会去真正的转换为红黑树
+ (7).Node为HashMap中没有树型化时的类型,TreeNode为树型化后的类型(1.8之前没有转红黑树的操作,直接是用的Entry的单链表)
+
+2.把有关前提说完后,开始来介绍里面两个有代表性的方法put(K,V);get(K)。为了能更清楚的说明,我就直接贴代码,然后写注释
+ (1)put(K,V)方法:
+
+ public V put(K key, V value) {
+ //调用putVal方法
+ return putVal(hash(key), key, value, false, true);
+ }
+
+ 来看看putVal方法
+ hash把key求hash值;key和value分别是键值对,onlyIfAbsent如果里的值已存在,则不去覆盖原来的值,evict我也没太看懂这是干嘛的,影响不大
+
+ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
+ Node[] tab; Node p; int n, i;
+ if ((tab = table) == null || (n = tab.length) == 0)
+ //如果第一次调用put方法,table为空,调用resize()方法,一个初始化和扩容的方法,后面再分析具体的实现
+ n = (tab = resize()).length;
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ //在指定tab数组的位置没有值的话,把新加的值添加到数组指定位置(相当于存Key的位置没有值)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ //在指定tab数组的位置已经存在值了,那么把新加的值存到已存在的Key对应的值最后
+ Node e; K k;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ //如果新添加的值和已存在的Key相同,让e = p;
+ e = p;
+ else if (p instanceof TreeNode)
+ //如果添加的元素是红黑树,则调用TreeNode里面相关的put方法。这里我就不去详情说了,太复杂了,我也没看懂
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ //Key对应的节点数量
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ //如果是最后一个,把新添加的元素加到最后一个元素后面
+ p.next = newNode(hash, key, value, null);
+ //如果节点元素大于TREEIFY_THRESHOLD-1这个值,调用转红黑树的方法
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ //如果新添加的值和节点的元素中相同,直接跳出不处理
+ break;
+ p = e;
+ }
+ }
+ if (e != null) { // existing mapping for key
+ //如果存在相同的元素,onlyIfAbsent这个值为true,就不去覆盖原值
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ }
+ //修改次数加1
+ ++modCount;
+ //size>阀值就会触发扩容,增加两倍
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
+ return null;
+ }
+
+ (2)resize()方法:
+ final Node[] resize() {
+ Node[] oldTab = table;
+ //得到原来tab的容量
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ //原来的阀值
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ //如果原容量不为0
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ //原容量大于最大容量,把阀值设置为int能表示的最大值
+ //不再继续扩容,把原tab返回
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ //如果新容量扩容2倍后,小于最大容量,且原容量大于初始化容量
+ //阀值扩容2倍
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ //如果阀值和容量都为0,都取默认值
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ //如果阀值没有设置,则为新容量*加载因子
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ //把新的阀值赋值给阀值变量
+ threshold = newThr;
+ //新建一个newCap长度的Node[]数组
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node[] newTab = (Node[])new Node[newCap];
+ //把新tab赋值给table
+ table = newTab;
+ //如果原tab有值,扩容之后把原来的元素都放到新的tab数组中去
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ //返回新扩容的数组
+ return newTab;
+ }
+ (3)treeifyBin()这个方法再提一下,注意第二行(n = tab.length) < MIN_TREEIFY_CAPACITY这个条件,只有tab的元素大于等于64的时候,才真正的转红黑树
+ final void treeifyBin(Node[] tab, int hash) {
+ int n, index; Node e;
+ if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+ resize();
+ else if ((e = tab[index = (n - 1) & hash]) != null) {
+ TreeNode hd = null, tl = null;
+ do {
+ TreeNode p = replacementTreeNode(e, null);
+ if (tl == null)
+ hd = p;
+ else {
+ p.prev = tl;
+ tl.next = p;
+ }
+ tl = p;
+ } while ((e = e.next) != null);
+ if ((tab[index] = hd) != null)
+ hd.treeify(tab);
+ }
+ }
+
+ (4)get(key)方法:
+ public V get(Object key) {
+ Node e;
+ //调用了getNode(hash,key)方法
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+ }
+ (5)getNode(hash,key)方法:
+ final Node getNode(int hash, Object key) {
+ Node[] tab; Node first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ //数组不为空,长度不为0,在数组中能找到key对应的元素,则继续
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ //如果找到的元素刚好是key对应的这个元素,直接返回
+ return first;
+ if ((e = first.next) != null) {
+ //如果第一个元素后面还有元素
+ if (first instanceof TreeNode)
+ //如果第一个元素是红黑树,则去对应的TreeNode方法中去找
+ return ((TreeNode)first).getTreeNode(hash, key);
+ //如果不是红黑树,则循环这个单链表
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
+ }
+
+3.HashMap的源码就分析到这,如果有不准确的地方,欢迎指出,如果有问题的话,也欢迎随时一起探讨
diff --git a/week_01/08/ArrayList-008.md b/week_01/08/ArrayList-008.md
new file mode 100644
index 0000000..90a5697
--- /dev/null
+++ b/week_01/08/ArrayList-008.md
@@ -0,0 +1,284 @@
+# 读源码--ArrayList
+
+1. ## 继承结构
+
+ 1. ### 继承类
+
+ - #### AbstractList
+
+ 2. ### 实现接口
+
+ - List
+ - RandomAcces--可随机访问
+ - Cloneable--可拷贝
+ - java.io.Serializable--可序列化
+
+2. ## 属性和方法
+
+ 1. ### 属性
+
+ - #### 默认空间(static):DEFAULT_CAPACITY--10
+
+ - #### 初始化数组(static)
+
+ - ##### EMPTY_ELEMENTDATA:
+
+ - ##### DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+
+ - #### 瞬态对象:elementData
+
+ 2. ### 常用方法
+
+ 1. #### 构造器
+
+ - ArrayList(int size)
+ - ArrayList(Collections c):任意集合转ArrayList。底层实现为数组
+
+ 2. #### trimToSize():去除空值生成新的集合
+
+ 3. #### int size()
+
+ 4. #### isEmpty()
+
+ 5. #### contains(Object o)
+
+ 6. #### indexOf(Object o)
+
+ - 若为null,则返回第一个null所在的索引
+ - 无则返回-1
+
+ 7. #### lastIndexOf(Object o)
+
+ 8. #### toArray():转数组
+
+ 9. #### clear():将数组所有元素置空,便于GC回收
+
+3. ## 扩容(调试)
+
+ 1. #### 添加元素add
+
+ 2. #### 最小容量minCapacity(添加元素后的数组长度)与数组容量element.length
+
+ - ##### 初始化时为均为0
+
+ - ##### 首次添加单个元素后为element.length变为10
+
+ ```
+ private static int calculateCapacity(Object[] elementData, int minCapacity) {
+ // 首次增加元素容量扩展为默认容量10
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ return Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+ return minCapacity;
+ }
+ ```
+
+ ```
+ private void ensureCapacityInternal(int minCapacity) {
+ ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
+ }
+ ```
+
+ ```
+ // 精确扩容
+ private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;
+
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0)
+ grow(minCapacity);
+ }
+ ```
+
+ - ##### 后面超过element.length后依次1.5增长
+
+ ```
+ // 真正执行扩容的方法grow
+ private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+ }
+ ```
+
+
+
+ - ###### 注:非1.5倍扩容的情况
+
+ - ##### add()首次添加单个元素扩容至10
+
+ - ##### addAll()批量增加后的数组长度大于扩容1.5倍容量时,直接扩容至数组长度
+
+ ```
+ public static void main(String[] args) {
+ ArrayList list = new ArrayList();
+ ArrayList list1 = new ArrayList();
+ int count = 10;
+ int count1 = 8;
+ for (int i = 0; i < count1; i++) {
+ list1.add(i);
+ }
+ for (int i = 0; i < count; i++) {
+ list.add(i);
+ System.out.println(i+":"+list);
+ }
+ // 当增加的数组长度大于1.5倍容量的扩容情况
+ list.addAll(list1);
+ }
+ ```
+
+4. ## 增删改查
+
+ 1. ### 通用方法
+
+ - #### System.arraycopy()方法
+
+ ```
+ public static native void arraycopy(Object src, int srcPos,
+ Object dest, int destPos,
+ int length);
+ ```
+
+ 2. ### 增
+
+ - ##### 末尾增加:boolean add(E e)
+
+ - ##### 指定位置增加
+
+ ```
+ public void add(int index, E element) {
+ rangeCheckForAdd(index);
+
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ // 后续元素依次往后移,添加缓慢,删除同理
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ elementData[index] = element;
+ size++;
+ }
+ ```
+
+ - ##### 批量添加
+
+ ```
+ public boolean addAll(Collection extends E> c) {
+ Object[] a = c.toArray();
+ int numNew = a.length;
+ // 此时的容量(size + numNew)可能超过扩容后1.5倍,则扩容后的容量为(size + numNew)
+ ensureCapacityInternal(size + numNew); // Increments modCount
+ System.arraycopy(a, 0, elementData, size, numNew);
+ size += numNew;
+ return numNew != 0;
+ }
+ ```
+
+ ```
+ private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+
+ // 需求容量minCapacity大于扩容后的容量newCapacity
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+ }
+ ```
+
+ - ##### 指定位置批量添加
+
+ 3. ### 删除
+
+ - ##### 指定下标
+
+ ```
+ public E remove(int index) {
+ rangeCheck(index);
+
+ modCount++;
+ E oldValue = elementData(index);
+ // 下标为index的元素,实际是数组的第index+1个元素
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+
+ return oldValue;
+ }
+ ```
+
+ - ##### 指定对象
+
+ ```
+ public boolean remove(Object o) {
+ if (o == null) {
+ for (int index = 0; index < size; index++)
+ if (elementData[index] == null) {
+ fastRemove(index);
+ return true;
+ }
+ } else {
+ for (int index = 0; index < size; index++)
+ if (o.equals(elementData[index])) {
+ fastRemove(index);
+ return true;
+ }
+ }
+ return false;
+ }
+ ```
+
+ - ##### 指定下标范围批量移除
+
+ 4. ### 改
+
+ 1. #### set(int index, E element):替换指定位置的元素
+
+ 5. ### 查
+
+ 1. #### get(int index)
+
+ 2. #### 迭代方法
+
+ - ##### ListIterator listIterator(int index):类似String的subString
+
+ - ##### ListIterator listIterator()
+
+ 3. #### List subList(int fromIndex, int toIndex):
+
+ - ##### 返回的是ArrayList的内部类--SubList
+
+ - ##### 该SubList无法转换为ArrayList,只是ArrayList的一个视图
+
+ - ###### 对父子类做的非结构性修改,都会影响到彼此
+
+ - ###### 对子List做结构性修改,操作会反映到父List上
+
+ - ###### 对父List做结构性修改,会抛出异常ConcurrentModificationException
+
+ - 若需要对subList进行修改,有不想动原list,那么可以创建subList的一个拷贝
+
+ ```
+ subList = Lists.newArrayList(subList);
+ list.stream().skip(strart).limit(end).collect(Collectors.toList());
+ ```
+
+
+
+ 4.
+
+ -
+
+
+
+
\ No newline at end of file
diff --git a/week_01/08/ArrayList-008.xmind b/week_01/08/ArrayList-008.xmind
new file mode 100644
index 0000000..8d522a9
Binary files /dev/null and b/week_01/08/ArrayList-008.xmind differ
diff --git a/week_01/08/HashMap-008.md b/week_01/08/HashMap-008.md
new file mode 100644
index 0000000..3370da4
--- /dev/null
+++ b/week_01/08/HashMap-008.md
@@ -0,0 +1,341 @@
+# 读源码--HashMap
+
+1. ## 继承体系
+
+ 1. ### 继承抽象类AbstractHashMap
+
+ 2. ### 实现接口List,Cloneable,Serializable
+
+2. ## 常规属性与方法
+
+ 1. ### 重要静态属性
+
+ ```
+ // 默认初始化容量为16
+ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+ // 最大容量2的30次方
+ static final int MAXIMUM_CAPACITY = 1 << 30;
+ // 默认负载因子0.75
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ // 转红黑树阈值8
+ static final int TREEIFY_THRESHOLD = 8;
+ // 转链表阈值6
+ static final int UNTREEIFY_THRESHOLD = 6;
+ // 转树的最小容量64,哈希表的容量小于64,会先进行扩容;不能小于4*TREEIFY_THRESHOLD
+ static final int MIN_TREEIFY_CAPACITY = 64;
+ ```
+
+ 2. ### 内部类Node(final?)
+
+ 3. ### 方法
+
+ 1. #### hash方法:将key进行hash重算,让key分别更均匀
+
+ ```
+ static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+ }
+ ```
+
+
+
+ 2. #### comparableClassFor(Object o):判断该对象是否实现Comparable接口
+
+ 3. #### compareComparables(Class> kc, Object k, Object x):k实现Comparable接口,若x为kc类,比较k与x
+
+ 4. #### tableSizeFor(int cap):得到大于或等于给定cap的最小二次幂,如cap为15,16,返回的都是16.(位运算技巧牛逼)
+
+ 4. ### 成员属性
+
+ ```
+ //
+ transient Node[] table;
+ transient Set> entrySet;
+ transient int size;
+ transient int modCount;
+ int threshold; //下次扩容的阈值
+ final float loadFactor;//负载因子
+ ```
+
+
+
+ 5. ### 构造器
+
+ 1. #### HashMap(int initialCapacity, float loadFactor)
+
+ 2. #### HashMap(int initialCapacity)
+
+ 3. #### HashMap()
+
+ 4. #### public HashMap(Map extends K, ? extends V> m)
+
+ 6. ### 常规方法
+
+ 1. #### size()
+
+ 2. #### isEmpty()
+
+ 3. #### containKey(Object key)
+
+ 4. #### containKey(Object value)
+
+ 5. #### clear():清空map
+
+3. ## 底层方法
+
+ 1. ### putMapEntries方法:
+
+ ```
+ final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {
+ int s = m.size();
+ if (s > 0) {
+ // 若桶为空
+ if (table == null) { // pre-size
+ float ft = ((float)s / loadFactor) + 1.0F;
+ int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+ (int)ft : MAXIMUM_CAPACITY);
+ // 若计算出的容量大于当前扩容阈值,则重新计算阈值
+ if (t > threshold)
+ threshold = tableSizeFor(t);
+ }
+ // 当map大小大于当前阈值时,扩容
+ else if (s > threshold)
+ resize();
+ // 赋值
+ for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
+ K key = e.getKey();
+ V value = e.getValue();
+ putVal(hash(key), key, value, false, evict);
+ }
+ }
+ }
+ ```
+
+
+
+ 2. ### putVal方法:赋值
+
+ ```
+ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node[] tab; Node p; int n, i;
+ // 判断tab是否为null
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ // 若对应索引下tab值为空,则直接插入
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ // 若存在,说明存在相同hash值。1:key值相同,说明存在该key了 2:key值不同,hash冲突
+ else {
+ Node e; K k;
+ // key值相同
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ // key值不同
+ // p为树节点
+ else if (p instanceof TreeNode)
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ // p为链表
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ // 插入到链表尾部
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+ // 超过转树的阈值,转为树节点,-1是因为binCount从0开始
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ }
+ ++modCount;
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
+ return null;
+ }
+ ```
+
+ 
+
+ 3. ### reSize():扩容
+
+ ```
+ final Node[] resize() {
+ Node[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ //
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node[] newTab = (Node[])new Node[newCap];
+ table = newTab;
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+ }
+ ```
+
+ 4. ### treeifyBin():转树(树结构不懂,待学习)
+
+ ```
+ final void treeifyBin(Node[] tab, int hash) {
+ int n, index; Node e;
+ // 桶大小小于最小转树容量,先扩容
+ if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+ resize();
+ else if ((e = tab[index = (n - 1) & hash]) != null) {
+ TreeNode hd = null, tl = null;
+ do {
+ TreeNode p = replacementTreeNode(e, null);
+ if (tl == null)
+ hd = p;
+ else {
+ p.prev = tl;
+ tl.next = p;
+ }
+ tl = p;
+ } while ((e = e.next) != null);
+ if ((tab[index] = hd) != null)
+ hd.treeify(tab);
+ }
+ }
+ ```
+
+
+
+4. ## 增删改查
+
+ 1. ### 查:
+
+ 1. #### get(Object key)
+
+ 2. #### getNode(int hash, Object key)
+
+ ```
+ final Node getNode(int hash, Object key) {
+ Node[] tab; Node first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ // 当存在hash冲突时,进行链表查询
+ if ((e = first.next) != null) {
+ // 若为树,则进行树节点查询(链表长度超过8会转为红黑树)
+ if (first instanceof TreeNode)
+ return ((TreeNode)first).getTreeNode(hash, key);
+ // 若不是树,则返回对应的链表查询值
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
+ }
+ ```
+
+
+
+ 2. ### 增
+
+ 1. #### put(K key,V value):增或改
+
+ 2. #### putAll(Map extends K, ? extends V> m):增加map
+
+ 3. ### 删
+
+ 1. #### remove(Object key)
+
+ 2. #### remove(K key,V value):
+
+ 4. ### 改
+
+ 1. #### put(K key,V value):
+
+ 2. #### replace(K key, V oldValue, V newValue)
+
+ 5. #### 遍历
+
+ 1. #### Set keySet() :将map中所有键放入Set中,通过遍历Set达到遍历map键的目的
+
+ 2. #### Collections values():获取map中的所有值
+
+ 3. #### Set> entrySet():遍历map键和值
\ No newline at end of file
diff --git a/week_01/08/HashMap-008.xmind b/week_01/08/HashMap-008.xmind
new file mode 100644
index 0000000..d31c054
Binary files /dev/null and b/week_01/08/HashMap-008.xmind differ
diff --git "a/week_01/08/HashMap-put\346\211\247\350\241\214\346\265\201\347\250\213\347\244\272\346\204\217\345\233\276.jpg" "b/week_01/08/HashMap-put\346\211\247\350\241\214\346\265\201\347\250\213\347\244\272\346\204\217\345\233\276.jpg"
new file mode 100644
index 0000000..e5e4a2e
Binary files /dev/null and "b/week_01/08/HashMap-put\346\211\247\350\241\214\346\265\201\347\250\213\347\244\272\346\204\217\345\233\276.jpg" differ
diff --git a/week_01/08/LinkedList-008.md b/week_01/08/LinkedList-008.md
new file mode 100644
index 0000000..e007585
--- /dev/null
+++ b/week_01/08/LinkedList-008.md
@@ -0,0 +1,239 @@
+# 读源码--LinkedList
+
+1. ## 继承体系
+
+ 1. ### 示意图
+
+ ###
+
+ 2. ### 继承分析
+
+ 1. #### 继承AbstractSequentialList
+
+ 2. #### 实现
+
+ - RandomAcces--可随机访问
+ - Cloneable--可拷贝
+ - java.io.Serializable--可序列化
+
+2. ## 属性和方法
+
+ 1. ### 属性
+
+ ```
+ transient int size = 0; // 大小
+ transient Node first; // 首节点
+ transient Node last; // 末节点
+ ```
+
+ 2. ### 构造器
+
+ ```
+ public LinkedList() {
+ }
+
+ public LinkedList(Collection extends E> c) {
+ this();
+ addAll(c);
+ }
+ ```
+
+ 3. ### 节点方法(非环链表首节点的prev为null,尾节点的next为null)
+
+ 1. 设置元素为首节点
+
+ ```
+ private void linkFirst(E e) {
+ final Node f = first;
+ final Node newNode = new Node<>(null, e, f);
+ first = newNode;
+ // 判断原链表是否为空
+ // 为空则把last也置为新节点
+ // 否则把原首节点的prev置为新节点
+ if (f == null)
+ last = newNode;
+ else
+ f.prev = newNode;
+ size++;
+ modCount++;
+ }
+ ```
+
+ 2. 设置元素为末节点
+
+ ```
+ void linkLast(E e) {
+ final Node l = last;
+ final Node newNode = new Node<>(l, e, null);
+ last = newNode;
+ // 判断原链表是否为空
+ // 为空则把first也置为新节点
+ // 否则把原首节点的prev置为新节点
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+ }
+ ```
+
+ 3. 插入
+
+ ```
+ void linkBefore(E e, Node succ) {
+ // assert succ != null;
+ // 获取首节点的上个节点
+ final Node pred = succ.prev;
+ final Node newNode = new Node<>(pred, e, succ);
+ succ.prev = newNode;
+ //若上个节点为空,则新节点为首节点
+ if (pred == null)
+ first = newNode;
+ else
+ pred.next = newNode;
+ size++;
+ modCount++;
+ }
+ ```
+
+ 4. 删除首节点
+
+ private E unlinkFirst(Node f) {
+ // assert f == first && f != null;
+ final E element = f.item;
+ final Node next = f.next;
+ f.item = null;
+ f.next = null; // help GC
+ first = next;
+ //只有一个节点的情况下
+ if (next == null) // 下个节点为空
+ last = null;
+ else
+ next.prev = null;
+ size--;
+ modCount++;
+ return element;
+ }
+
+ 5. 删除末节点
+
+ ```
+ private E unlinkLast(Node l) {
+ // assert l == last && l != null;
+ final E element = l.item;
+ final Node prev = l.prev;
+ l.item = null;
+ l.prev = null; // help GC
+ last = prev;
+ //只有一个节点的情况下
+ if (prev == null)
+ first = null;
+ else
+ prev.next = null;
+ size--;
+ modCount++;
+ return element;
+ }
+ ```
+
+ 6. 删除任意节点
+
+ ```
+ E unlink(Node x) {
+ // assert x != null;
+ final E element = x.item;
+ final Node next = x.next;
+ final Node prev = x.prev;
+
+ if (prev == null) {
+ first = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+
+ if (next == null) {
+ last = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+
+ x.item = null;
+ size--;
+ modCount++;
+ return element;
+ }
+ ```
+
+ 7. 查询
+
+ - getFirst()
+
+ - getLast()
+
+ - Node node(int index)
+
+ ```
+ Node node(int index) {
+ // assert isElementIndex(index);
+
+ if (index < (size >> 1)) {
+ Node x = first;
+ for (int i = 0; i < index; i++)
+ x = x.next;
+ return x;
+ } else {
+ Node x = last;
+ for (int i = size - 1; i > index; i--)
+ x = x.prev;
+ return x;
+ }
+ }
+ ```
+
+
+
+ 4. ### 常用方法
+
+ 1. contains(Object o)
+ 2. size()
+ 3. clear()
+
+3. ## 增删改查
+
+ 1. ### 增
+
+ - void addFirst(E e):首端添加
+ - void addLast(E e):末端添加
+ - boolean add():同addLast(),区别是该方法有返回值
+ - boolean (Collection extends E> c):添加集合
+ - boolean (int index, Collection extends E> c):指定位置添加集合
+ - void add(int index, E element) :指定位置添加元素
+
+ 2. ### 删
+
+ - boolean remove(Object o) :删除linkedList中值为o的节点
+ - E remove(int index)
+
+ 3. ### 改
+
+ - E set(int index, E element)
+
+ 4. ### 查
+
+ - E get(int index) :查询下标为index的节点的值
+ - boolean isElementIndex(int index):
+ - boolean isPositionIndex(int index):
+ - int indexOf(Object o)
+ - int lastIndexOf(Object o)
+
+ 5. ### 迭代器
+
+ 1. ListIterator listIterator(int index)
+
+
+
+###
+
diff --git a/week_01/08/LinkedList-008.xmind b/week_01/08/LinkedList-008.xmind
new file mode 100644
index 0000000..d9dde51
Binary files /dev/null and b/week_01/08/LinkedList-008.xmind differ
diff --git "a/week_01/08/LinkedList\347\273\247\346\211\277\344\275\223\347\263\273.jpg" "b/week_01/08/LinkedList\347\273\247\346\211\277\344\275\223\347\263\273.jpg"
new file mode 100644
index 0000000..5cc9ddb
Binary files /dev/null and "b/week_01/08/LinkedList\347\273\247\346\211\277\344\275\223\347\263\273.jpg" differ
diff --git a/week_01/08/README.md b/week_01/08/README.md
index 5bb53d0..b122c48 100644
--- a/week_01/08/README.md
+++ b/week_01/08/README.md
@@ -6,8 +6,8 @@
### 一、学习周期(2个月)
-| 时间 | 内容 |
-| :------------------------------- | ------- |
+| 时间 | 内容 |
+| :------------------------------- | ------- |
| 第一周 (2019/12/09-2019/12.15) | jdk ||
| 第二周 (2019/12/16-2019/12.22) | jdk ||
| 第三周 (2019/12/23-2019/12.29) | jdk ||
@@ -32,10 +32,11 @@
#### 3、review5名其他的学习笔记或学习总结
在项目的`Pull requests`可以看到其他人的Pull requests记录,并进行review。
-
+
###三、源码提交流程
+
- 先将[xxx]仓库 `fork` 到自己的 GitHub 账号下。
- 将 `fork` 后的仓库 `clone` 到本地,然后在本地新建、修改自己的源码学习笔记,**注意:** 仅允许在和自己编号对应的目录下新建或修改自己的源码学习笔记。完成后,将相关修改部分 `push` 到自己的 GitHub 远程仓库。
- 当完成本周作业,提交 `Pull Request`申请给[xxx]仓库,Pull 作业时,必须备注自己的编号和提交第几周的作业,如`007-week 02`,是指编号为`007`的成员提交的`第二周`的源码学习笔记。
@@ -44,4 +45,8 @@
-ps:任何学习上的问题可以发布issure求助,其他同学有时间就帮忙看看哈。
\ No newline at end of file
+ps:任何学习上的问题可以发布issure求助,其他同学有时间就帮忙看看哈。
+
+
+
+#
\ No newline at end of file
diff --git a/week_01/09/ArrayList-009.md b/week_01/09/ArrayList-009.md
new file mode 100644
index 0000000..206ac3c
--- /dev/null
+++ b/week_01/09/ArrayList-009.md
@@ -0,0 +1,215 @@
+##关于ArrayList小笔记
+
+
+###1、认识ArrayList
+
+1)ArrayList就是动态数组
+
+ ```java
+public class ArrayList extends AbstractList
+ implements List, RandomAccess, Cloneable, java.io.Serializable
+ ```
+
+2)实现了List,提供了基础的添加、删除等操作;
+
+3)实现Cloneable;
+
+4)实现java.io.Serializable,可被序列化;
+
+5)实现RandomAccess,可随机访问。
+
+###2、属性概览
+
+```java
+/**
+ * 默认初始化大小.
+ */
+ private static final int DEFAULT_CAPACITY = 10;
+ /**
+ * 空数组 当创建实例数量为空时使用.
+ */
+ private static final Object[] EMPTY_ELEMENTDATA = {};
+ /**
+ * 存储元素的数组.
+ */
+ transient Object[] elementData;// non-private to simplify nested class access
+ /**
+ * 数组大小.
+ */
+ private int size;
+
+```
+
+###3、构造器
+```java
+/**
+ * 创建指定大小数组
+ * @param 数组大小
+ */
+public ArrayList(int initialCapacity) {
+ super();
+ // 如果传入大小小于0,抛出异常
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal Capacity: "+
+ initialCapacity);
+ // 创建指定容量大小新数组
+ this.elementData = new Object[initialCapacity];
+}
+
+/**
+ * 默认构造器为空数组,使用时按默认大小
+ */
+ public ArrayList() {
+ super();
+ this.elementData = EMPTY_ELEMENTDATA;
+}
+
+/**
+ * 将传入的集合转成数组
+ */
+ public ArrayList(Collection extends E> c) {
+ elementData = c.toArray();
+ size = elementData.length;
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ //see 6260652为bug编号 https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
+ if (elementData.getClass() != Object[].class)
+ elementData = Arrays.copyOf(elementData, size, Object[].class);
+}
+
+```
+ps:针对最后一类创建数组方式为什么返回不一定是一个对象,测试如下:
+
+```java
+//第一种方式
+List test1 = new ArrayList( );
+test1.add("123");
+System.out.println(test1.toArray());
+//返回结果:[Ljava.lang.Object;@a74868d
+
+//第二种方式
+List test2 = Arrays.asList("123");
+System.out.println(test2.toArray());
+//返回结果:[Ljava.lang.String;@12c8a2c0
+```
+
+### 4、add
+1)add(E e)
+
+```java
+/**
+ * 添加数组,从末尾开始添加
+ */
+public boolean add(E e) {
+ //检查是否需要扩容
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ //将元素添加到数组最后一位
+ elementData[size++] = e;
+ return true;
+}
+
+private void ensureCapacityInternal(int minCapacity) {
+ //如果是空数组,则初始化默认大小10
+ if (elementData == EMPTY_ELEMENTDATA) {
+ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+ ensureExplicitCapacity(minCapacity);
+}
+
+private void ensureExplicitCapacity(int minCapacity) {
+ //用于统计list被修改的次数
+ modCount++;
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0)
+ //扩容
+ grow(minCapacity);
+}
+
+private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ //扩容1.5倍
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ //如果新容量小于需要最小容量,则以需要容量为准
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ //如果新容量大于最大容量,则以最大容量为准
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ //以新容量拷贝一个新数组出来
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+```
+2)add(int index, E element)
+
+``` java
+/**
+ * 添加元素到指定位置
+ */
+public void add(int index, E element) {
+ //检查是否越界
+ rangeCheckForAdd(index);
+ //检查是否需要扩容
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ //将数组index之后的元素往后移动一位
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ //再将index设置为需要添加的元素
+ elementData[index] = element;
+ //数组长度+1
+ size++;
+}
+```
+
+###5、get
+
+``` java
+/**
+ * 获得指定位置该元素
+ */
+public E get(int index) {
+ ///检查是否越界
+ rangeCheck(index);
+ //返回元素
+ return elementData(index);
+}
+
+
+private void rangeCheck(int index) {
+ if (index < 0 || index >= this.size)
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+}
+
+private void checkForComodification() {
+ //保证操作为同一list(考虑并发情况下)
+ if (ArrayList.this.modCount != this.modCount)
+ throw new ConcurrentModificationException();
+}
+
+```
+
+###6、remove
+
+``` java
+/**
+ * 删除指定位置元素
+ */
+public E remove(int index) {
+ //检查是否越界
+ rangeCheck(index);
+
+ modCount++;
+ //获取index位置元素
+ E oldValue = elementData(index);
+ //如果index不是最后一位,则把index之后的元素往前挪一位
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ //// 将最后一个元素删除,帮助GC
+ elementData[--size] = null; // clear to let GC do its work
+ //返回删除旧值
+ return oldValue;
+}
+
+```
diff --git a/week_01/09/HashMap-009.md b/week_01/09/HashMap-009.md
new file mode 100644
index 0000000..d7c70fe
--- /dev/null
+++ b/week_01/09/HashMap-009.md
@@ -0,0 +1,445 @@
+##关于HashMap
+
+###1、认识HashMap
+1)采用key/value存储结构,每个key对应唯一的value,查询和修改的速度都很快
+
+```java
+public class HashMap extends AbstractMap
+ implements Map, Cloneable, Serializable {
+```
+2)实现Cloneable
+
+3)实现Serializable ,可序列化
+
+4)继承AbstractMap,实现Map接口,可实现Map功能。
+
+
+###2、属性
+
+```java
+/**
+ * 默认初始容量16
+ */
+static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+
+/**
+ * 最大的容量为2的30次方
+ */
+static final int MAXIMUM_CAPACITY = 1 << 30;
+
+/**
+ * 默认装载因子
+ */
+static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+/**
+ * 当一个桶中的元素个数大于等于8时进行树化
+ */
+static final int TREEIFY_THRESHOLD = 8;
+
+/**
+ * 当一个桶中的元素个数小于等于6时把树转化为链表
+ */
+static final int UNTREEIFY_THRESHOLD = 6;
+
+/**
+ * 当桶的个数达到64的时候才进行树化
+ */
+static final int MIN_TREEIFY_CAPACITY = 64;
+
+/**
+ * 数组,桶(bucket)
+ */
+transient Node[] table;
+
+/**
+ * 作为entrySet()的缓存
+ */
+transient Set> entrySet;
+
+/**
+ * 元素的数量
+ */
+transient int size;
+
+/**
+ * 修改次数,用于在迭代的时候执行快速失败策略
+ */
+transient int modCount;
+
+/**
+ * 当桶的使用数量达到多少时进行扩容,threshold = capacity * loadFactor
+ */
+int threshold;
+
+/**
+ * 装载因子
+ */
+final float loadFactor;
+
+```
+###3、Node类
+
+```java
+/**
+ * 典型的单链表节点,其中,hash用来存储key计算得来的hash值。
+ */
+static class Node implements Map.Entry {
+ final int hash;
+ final K key;
+ V value;
+ Node next;
+```
+
+###4、TreeNode类
+
+```java
+/**
+ * 典型的树型节点,其中,prev是链表中的节点,用于在删除元素的时候可以快速找到它的前置节点。
+ */
+static final class TreeNode extends LinkedHashMap.Entry {
+ TreeNode parent; // red-black tree links
+ TreeNode left;
+ TreeNode right;
+ TreeNode prev; // needed to unlink next upon deletion
+ boolean red;
+
+```
+###5、构造方法
+
+1) HashMap(int initialCapacity, float loadFactor)
+
+```java
+
+/**
+ * 创建一个hashmap
+ * @param initialCapacity 初始化容量大小
+ * @param loadFactor 默认装载因子
+ * @throws IllegalArgumentException if the initial capacity is negative
+ * or the load factor is nonpositive
+ */
+public HashMap(int initialCapacity, float loadFactor) {
+ //检测传入初始化容量是否合法
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal initial capacity: " +
+ initialCapacity);
+
+ if (initialCapacity > MAXIMUM_CAPACITY)
+ initialCapacity = MAXIMUM_CAPACITY;
+ //检测装载因子
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new IllegalArgumentException("Illegal load factor: " +
+ loadFactor);
+ this.loadFactor = loadFactor;
+ // 计算扩容门槛
+ this.threshold = tableSizeFor(initialCapacity);
+}
+
+/**
+ * Returns a power of two size for the given target capacity.
+ */
+static final int tableSizeFor(int cap) {
+ int n = cap - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+}
+
+
+```
+2) HashMap(int initialCapacity)
+
+```java
+/**
+ * 创建一个hashmap
+ * 传入初始化容量大小,装载因子为默认值
+ */
+public HashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+}
+```
+
+3)HashMap()
+
+``` java
+/**
+ * 创建一个默认值hashmap
+ */
+public HashMap() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+}
+```
+
+###6、put方法
+
+``` java
+/**
+ * hashmap 添加新元素
+ */
+public V put(K key, V value) {
+ //调用hash(key)计算出key的hash值
+ return putVal(hash(key), key, value, false, true);
+}
+
+static final int hash(Object key) {
+ int h;
+ //解释(h = key.hashCode()) ^ (h >>> 16):
+ //调用key的hashCode(),且让高16位与整个hash异或,这样做是为了使计算出的hash更分散
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node[] tab; Node p; int n, i;
+ //如果桶中元素为空
+ if ((tab = table) == null || (n = tab.length) == 0)
+ //调用resize初始化
+ n = (tab = resize()).length;
+ //(n - 1) & hash 计算元素在哪个桶中
+ //如果这个桶中还没有元素,则把这个元素放在桶中的第一个位置
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else { //如果桶中存在元素
+ Node e; K k;
+ // 如果桶中第一个元素的key与待插入元素的key相同,保存到e中用于后续修改value值
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ else if (p instanceof TreeNode)
+ // 如果第一个元素是树节点,则调用树节点的putTreeVal插入元素
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+
+ // 如果待插入的key在链表中找到了,则退出循环
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+
+ // 如果找到了对应key的元素
+ if (e != null) { // existing mapping for key
+ // 记录下旧值
+ V oldValue = e.value;
+ // 判断是否需要替换旧值
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ //返回旧值
+ return oldValue;
+ }
+ }
+
+ ++modCount;
+
+ // 元素数量加1,判断是否需要扩容。
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
+ //如果没有找到,则返回null
+ return null;
+}
+```
+####ps resize方法
+
+``` java
+/**
+ * 扩容方法
+ */
+final Node[] resize() {
+ Node[] oldTab = table;
+ //旧数组
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ // 如果旧容量达到了最大容量,则不再进行扩容
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+
+ // 如果旧容量的两倍小于最大容量并且旧容量大于默认初始容量(16),则容量扩大为两部,扩容门槛也扩大为两倍
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0)
+ // 如果旧容量为0且旧扩容门槛大于0,则把新容量赋值为旧门槛
+ newCap = oldThr;
+ else {
+ // 如果旧容量旧扩容门槛都是0,说明还未初始化过,则初始化容量为默认容量,扩容门槛为默认容量*默认装载因子
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ // 如果新扩容门槛为0,则计算为容量*装载因子,但不能超过最大容量
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ //赋值扩容门槛为新门槛
+ threshold = newThr;
+ // 新建一个新容量的数组
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node[] newTab = (Node[])new Node[newCap];
+ // 把桶赋值为新数组
+ table = newTab;
+ // 如果旧数组不为空,则搬移元素
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+}
+
+```
+
+###7、get方法
+
+``` java
+/**
+ * 获取某一键值
+ */
+public V get(Object key) {
+ Node e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+}
+
+final Node getNode(int hash, Object key) {
+ Node[] tab; Node first, e; int n; K k;
+ // 如果桶的数量大于0并且待查找的key所在的桶的第一个元素不为空
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ // 检查第一个元素是不是要查的元素,如果是直接返回
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ if ((e = first.next) != null) {
+ // 如果第一个元素是树节点,则按树的方式查找
+ if (first instanceof TreeNode)
+ return ((TreeNode)first).getTreeNode(hash, key);
+ // 否则就遍历整个链表查找该元素
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
+}
+```
+###8、remove方法
+
+```java
+/**
+ * 删除某一键值
+ */
+public V remove(Object key) {
+ Node e;
+ return (e = removeNode(hash(key), key, null, false, true)) == null ?
+ null : e.value;
+}
+
+final Node removeNode(int hash, Object key, Object value,
+ boolean matchValue, boolean movable) {
+ Node[] tab; Node p; int n, index;
+ // 如果桶的数量大于0且待删除的元素所在的桶的第一个元素不为空
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (p = tab[index = (n - 1) & hash]) != null) {
+ Node node = null, e; K k; V v;
+ // 如果第一个元素正好就是要找的元素,赋值给node变量后续删除使用
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ node = p;
+ else if ((e = p.next) != null) {
+ // 如果第一个元素是树节点,则以树的方式查找节点
+ if (p instanceof TreeNode)
+ node = ((TreeNode)p).getTreeNode(hash, key);
+ else {
+ // 否则遍历整个链表查找元素
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key ||
+ (key != null && key.equals(k)))) {
+ node = e;
+ break;
+ }
+ p = e;
+ } while ((e = e.next) != null);
+ }
+ }
+
+ // 如果找到了元素,则看参数是否需要匹配value值,如果不需要匹配直接删除,如果需要匹配则看value值是否与传入的value相等
+ if (node != null && (!matchValue || (v = node.value) == value ||
+ (value != null && value.equals(v)))) {
+ // 如果是树节点,调用树的删除方法(以node调用的,是删除自己)
+ if (node instanceof TreeNode)
+ ((TreeNode)node).removeTreeNode(this, tab, movable);
+ // 如果待删除的元素是第一个元素,则把第二个元素移到第一的位置
+ else if (node == p)
+ tab[index] = node.next;
+ //删除node节点
+ else
+ p.next = node.next;
+ ++modCount;
+ --size;
+ //删除节点后处理
+ afterNodeRemoval(node);
+ return node;
+ }
+ }
+ return null;
+}
+```
+
\ No newline at end of file
diff --git a/week_01/1/README.md b/week_01/1/README.md
deleted file mode 100644
index 5bb53d0..0000000
--- a/week_01/1/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-## 源码刻意学习小组
-
-[TOC]
-
-
-
-### 一、学习周期(2个月)
-
-| 时间 | 内容 |
-| :------------------------------- | ------- |
-| 第一周 (2019/12/09-2019/12.15) | jdk ||
-| 第二周 (2019/12/16-2019/12.22) | jdk ||
-| 第三周 (2019/12/23-2019/12.29) | jdk ||
-| 第四周 (2019/12/30-2020/01/05) | jdk ||
-| 第五周 (2020/01/06-2020/01/12) | Spring ||
-| 第六周 (2020/01/13-2020/01/19) | Spring ||
-| 第七周 (2020/01/20-2020/01/26) | MyBatis ||
-| 第八周 (2020/01/27-2020/02/02) | MyBatis ||
-
-
-
-### 二、作业
-
-#### 1、源码学习笔记(必做)
-
- 至少提交2个类的源码分析笔记
-
-#### 2、本周学习总结(可选)
-
- 学习总结直接在GitHub的issue上发布即可。
-
-#### 3、review5名其他的学习笔记或学习总结
-
- 在项目的`Pull requests`可以看到其他人的Pull requests记录,并进行review。
-
-
-
-###三、源码提交流程
-- 先将[xxx]仓库 `fork` 到自己的 GitHub 账号下。
-- 将 `fork` 后的仓库 `clone` 到本地,然后在本地新建、修改自己的源码学习笔记,**注意:** 仅允许在和自己编号对应的目录下新建或修改自己的源码学习笔记。完成后,将相关修改部分 `push` 到自己的 GitHub 远程仓库。
-- 当完成本周作业,提交 `Pull Request`申请给[xxx]仓库,Pull 作业时,必须备注自己的编号和提交第几周的作业,如`007-week 02`,是指编号为`007`的成员提交的`第二周`的源码学习笔记。
-- 源码学习笔记的命名规则:**`内容标题-编号`**,比如学号为 `007` 的成员完成`ArrayList`类后,请将源码学习笔记名保存为 `ArrayList-007 `。(内容标题自定义)
-- 务必按照Pull Request的备注形式和作业文件的命名进行提交,这样方便统计。
-
-
-
-ps:任何学习上的问题可以发布issure求助,其他同学有时间就帮忙看看哈。
\ No newline at end of file
diff --git a/week_01/11/ArrayList.md b/week_01/11/ArrayList.md
new file mode 100644
index 0000000..89489a1
--- /dev/null
+++ b/week_01/11/ArrayList.md
@@ -0,0 +1,59 @@
+ava.util.ArrayList ǷdzҪһ࣬ڴй㷺ʹãEʾͣArrayListһࡣ ArrayList൱C++ vectorڴ洢鲻ͬһȹ̶ArrayListijǶ̬ģƣԴ洢Ķֻܴ洢ܴ洢ԭint
+
+import java.util.ArrayList; public class TestArrayList { public static void main(String[] args) { // Create a list to store cities ArrayList cityList = new ArrayList();
+
+ // Add some cities in the list
+ cityList.add("London");
+ // cityList now contains [London]
+
+ cityList.add("Denver");
+ // cityList now contains [London, Denver]
+
+ cityList.add("Paris");
+ // cityList now contains [London, Denver, Paris]
+
+ cityList.add("Miami");
+ // cityList now contains [London, Denver, Paris, Miami]
+
+ cityList.add("Seoul");
+ // Contains [London, Denver, Paris, Miami, Seoul]
+
+ cityList.add("Tokyo");
+ // Contains [London, Denver, Paris, Miami, Seoul, Tokyo]
+
+ System.out.println("List size? " + cityList.size()); // 6
+ System.out.println("Is Miami in the list? " + cityList.contains("Miami")); // true
+ System.out.println("The location of Denver in the list? " + cityList.indexOf("Denver")); // 1 listУ-1
+ System.out.println("Is the list empty? " + cityList.isEmpty()); // Print false
+
+ // Insert a new city at index 2
+ cityList.add(2, "Xian");
+ // Contains [London, Denver, Xian, Paris, Miami, Seoul, Tokyo]
+
+ // Remove a city from the list
+ cityList.remove("Miami");
+ // Contains [London, Denver, Xian, Paris, Seoul, Tokyo]
+
+ // Remove a city at index 1
+ cityList.remove(1);
+ // Contains [London, Xian, Paris, Seoul, Tokyo]
+
+ // Display the contents in the list
+ System.out.println(cityList.toString());
+
+ // Display the contents in the list in reverse order
+ for (int i = cityList.size() - 1; i >= 0; i--)
+ System.out.print(cityList.get(i) + " ");
+ System.out.println();
+
+ // Create a list to store two circles
+ ArrayList list = new ArrayList();
+
+ // Add two circles
+ list.add(new CircleFromSimpleGeometricObject(2));
+ list.add(new CircleFromSimpleGeometricObject(3));
+
+ // Display the area of the first circle in the list
+ System.out.println("The area of the circle? " + list.get(0).getArea());
+}
+}
\ No newline at end of file
diff --git a/week_01/11/HashMap.md b/week_01/11/HashMap.md
new file mode 100644
index 0000000..1e99d18
--- /dev/null
+++ b/week_01/11/HashMap.md
@@ -0,0 +1,48 @@
+HashMapJDK1.8֮ǰʵַʽ +,JDK1.8HashMap˵ײŻ,Ϊ ++ʵ,ҪĿ߲Чʡ
+
+1.̳йϵ
+
+public class HashMap extends AbstractMap implements Map, Cloneable, Serializable
+
+2.&췽 //ֵ ڵ8ʱתΪ洢 static final int TREEIFY_THRESHOLD = 8; //ڵС6ʱתΪ洢 static final int UNTREEIFY_THRESHOLD = 6; //СΪ 64 static final int MIN_TREEIFY_CAPACITY = 64; //HashMapʼС static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 //HashMap static final int MAXIMUM_CAPACITY = 1 << 30; //ĬϴС static final float DEFAULT_LOAD_FACTOR = 0.75f; //NodeMap.Entryӿڵʵ //ڴ˴洢ݵNode2 //ÿһNodeʶһ transient Node[] table; //HashMapС,HashMapļֵԵĶ transient int size; //HashMapıĴ transient int modCount; //һHashMapݵĴС int threshold; //洢ӵij final float loadFactor;
+
+//ĬϵĹ캯 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } //ָС public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } //ָСӴС public HashMap(int initialCapacity, float loadFactor) { //ָСС0,׳IllegalArgumentException쳣 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); //жָСǷHashMap if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; //ָĸӲС0ΪNullж׳IllegalArgumentException쳣 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
+
+ this.loadFactor = loadFactor;
+ // áHashMapֵHashMapд洢ݵﵽthresholdʱҪHashMapӱ
+ this.threshold = tableSizeFor(initialCapacity);
+}
+//һMap,MapԪMap.EntryȫӽHashMapʵ
+public HashMap(Map extends K, ? extends V> m) {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ //˹췽ҪʵMap.putAll()
+ putMapEntries(m, false);
+}
+3.Nodeʵ //ʵMap.Entryӿ static class Node implements Map.Entry { final int hash; final K key; V value; Node next; //캯 Node(int hash, K key, V value, Node next) { this.hash = hash; this.key = key; this.value = value; this.next = next; }
+
+ public final K getKey() { return key; }
+ public final V getValue() { return value; }
+ public final String toString() { return key + "=" + value; }
+
+ public final int hashCode() {
+ return Objects.hashCode(key) ^ Objects.hashCode(value);
+ }
+
+ public final V setValue(V newValue) {
+ V oldValue = value;
+ value = newValue;
+ return oldValue;
+ }
+ //equalsԶԱ
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o instanceof Map.Entry) {
+ Map.Entry,?> e = (Map.Entry,?>)o;
+ if (Objects.equals(key, e.getKey()) &&
+ Objects.equals(value, e.getValue()))
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/week_01/12/ArrayList-012.md b/week_01/12/ArrayList-012.md
new file mode 100644
index 0000000..6e0ee0c
--- /dev/null
+++ b/week_01/12/ArrayList-012.md
@@ -0,0 +1,214 @@
+#### 问题
+elementData为什么加transient?
+自动扩容是如何进行的?
+modCount作用是什么?
+
+#### 简介
+线性表之一,基于数组,支持动态扩容
+#### 继承体系
+
+#### 源码解析
+
+##### 属性
+```
+// 默认容量
+private static final int DEFAULT_CAPACITY = 10;
+// 空数组,如果传入的容量为0时使用
+private static final Object[] EMPTY_ELEMENTDATA = {};
+// 空数组,传传入容量时使用,添加第一个元素的时候会重新初始为默认容量大小
+private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+// 存储元素的数组
+transient Object[] elementData;
+// 集合中元素的个数
+private int size;
+```
+##### 构造方法
+```
+public ArrayList(int initialCapacity);
+// 初始化为DEFAULT空数组,添加第一个元素时扩容为默认大小,10
+public ArrayList();
+// 使用拷贝把传入集合的元素拷贝到elementData数组中
+public ArrayList(Collection extends E> c);
+```
+##### 主要方法
+###### boolean add(E e)
+```
+public boolean add(E e) {
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ elementData[size++] = e;
+ return true;
+}
+
+// 增加第一个元素时设定默认容量10
+private void ensureCapacityInternal(int minCapacity) {
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+ ensureExplicitCapacity(minCapacity);
+}
+
+// 容量不够时扩容
+private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;
+ if (minCapacity - elementData.length > 0)
+ grow(minCapacity);
+}
+
+private void grow(int minCapacity) {
+ int oldCapacity = elementData.length;
+ // 1.5倍扩容
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ // 最大容量,2的31次方-1
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+```
+###### void add(int index, E element)
+```
+public void add(int index, E element) {
+ // 角标越界检查
+ rangeCheckForAdd(index);
+ ensureCapacityInternal(size + 1);
+ // index+1处复制起始位置为index,长度为size-index的数据
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ elementData[index] = element;
+ size++;
+}
+```
+###### E remove(int index)
+```
+public E remove(int index) {
+ rangeCheck(index);
+
+ modCount++;
+ E oldValue = elementData(index);
+
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ // index处复制起始位置为index+1,长度为size - index - 1的数据
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ // 尾元素置空,size-1
+ elementData[--size] = null; // clear to let GC do its work
+
+ return oldValue;
+}
+```
+###### boolean remove(Object o)
+```
+public boolean remove(Object o) {
+ if (o == null) {
+ for (int index = 0; index < size; index++)
+ if (elementData[index] == null) {
+ fastRemove(index);
+ return true;
+ }
+ } else {
+ for (int index = 0; index < size; index++)
+ if (o.equals(elementData[index])) {
+ fastRemove(index);
+ return true;
+ }
+ }
+ return false;
+}
+
+private void fastRemove(int index) {
+ // 无需检查越界
+ modCount++;
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+}
+```
+###### voic clear()
+```
+public void clear() {
+ modCount++;
+
+ // clear to let GC do its work
+ for (int i = 0; i < size; i++)
+ elementData[i] = null;
+
+ size = 0;
+}
+```
+###### E set(int index, E element)
+```
+public E set(int index, E element) {
+ rangeCheck(index);
+
+ E oldValue = elementData(index);
+ elementData[index] = element;
+ return oldValue;
+}
+```
+###### boolean addAll(Collection extends E> c)
+尾部添加集合
+###### boolean addAll(int index, Collection extends E> c)
+###### void removeRange(int fromIndex, int toIndex)
+###### boolean removeAll(Collection> c)
+```
+// 移除集合中包含参数集合中的数据
+public boolean removeAll(Collection> c) {
+ Objects.requireNonNull(c);
+ return batchRemove(c, false);
+}
+```
+###### boolean retainAll(Collection> c)
+```
+// 保留集合中包含参数集合中的数据
+public boolean retainAll(Collection> c) {
+ Objects.requireNonNull(c);
+ return batchRemove(c, true);
+}
+```
+```
+private boolean batchRemove(Collection> c, boolean complement) {
+ final Object[] elementData = this.elementData;
+ int r = 0, w = 0;
+ boolean modified = false;
+ try {
+ for (; r < size; r++)
+ if (c.contains(elementData[r]) == complement)
+ elementData[w++] = elementData[r];
+ } finally {
+ // Preserve behavioral compatibility with AbstractCollection,
+ // even if c.contains() throws.
+ if (r != size) {
+ System.arraycopy(elementData, r,
+ elementData, w,
+ size - r);
+ w += size - r;
+ }
+ if (w != size) {
+ // clear to let GC do its work
+ for (int i = w; i < size; i++)
+ elementData[i] = null;
+ modCount += size - w;
+ size = w;
+ modified = true;
+ }
+ }
+ return modified;
+}
+```
+###### void writeObject(java.io.ObjectOutputStream s)
+###### void readObject(java.io.ObjectInputStream s)
+###### ListIterator listIterator(int index)
+###### List subList(int fromIndex, int toIndex)
+###### void sort(Comparator super E> c)
+
+#### 总结
+默认容量为10,以1.5倍容量扩容
+Collection.toArray()转换后不一定全是Object[]
+
+#### 延伸
+bug网址:
+https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
\ No newline at end of file
diff --git a/week_01/15/HashMap-015.md b/week_01/15/HashMap-015.md
new file mode 100644
index 0000000..8c85042
--- /dev/null
+++ b/week_01/15/HashMap-015.md
@@ -0,0 +1,485 @@
+### [Java 8] HashMap
+
+`HashMap` 是哈希表的基本实现,不是线程安全的。`HashMap` 底层主要存储是一个数组 `table`,数组中每个元素称为一个桶。将 `key` 通过哈希函数得 `key.hashCode()` 到哈希值 `hash`,再将 `hash` 按照桶的个数(即数组长度)取模得到该 `key` 所映射的桶(即数组的索引)。
+
+因此从 `key` 到桶的映射过程可能会碰撞,即不同的 `key` 可能会映射到同一个桶,因此桶内需要能存多个键值对。桶默认使用链表存储多个键值对。
+
+如果碰撞过多会严重影响 `HashMap` 的性能,本来算个 `hash` 在取个模再比较个 `key` 三部曲就完事的工作,在碰撞时第三步要遍历链表挨个比较键值找到要查找的 `key`,这样 *O(1)* 的时间复杂度退化为 *O(K)*,其中K为桶内键值对数。
+
+因此为了减小碰撞带来的性能退化,有两种策略分别针对不同的场景:
+1. 假设哈希函数分布还是不错的,但因为桶数量太少了,广泛分布的 `hash` 被压缩到少量的桶中不碰撞才怪。既然这样,就扩容桶的数量,原先映射到一个桶就可能分散到不同的桶。比如桶的数量为4,`hash` 为3和7的 `key` 都会映射到索引为3的桶,但把桶扩容到8后,两者就分别映射到索引为3和7的桶;当然等键值对数增长到桶的数量再扩容有点晚了,肯定已经发生一些碰撞了,试想多牛逼的哈希函数配上多么契合的场景才能保证一个桶只落一个键值对呢。因此需要一个阈值,键值对数超过阈值就扩容。
+
+2. 假设哈希函数实现得比较烂,一堆不同的 `key` 通过哈希函数计算后都是差不多的 `hash`,桶再多又有毛用,全TMD映射到少量的桶中,桶里链表又特别长。既然这样,那就提高键值对多的桶内查找 `key` 的效率,用红黑树替换链表,将 *O(K)* 补救到 *O(logK)*
+
+
+#### 常量及实例变量
+``` java
+// 默认桶的数量,必须是2的整数次幂
+static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+
+// 桶的最大数量
+static final int MAXIMUM_CAPACITY = 1 << 30;
+
+// 默认负载因子
+static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+// 桶内链表转化为红黑树的键值对数量阈值
+static final int TREEIFY_THRESHOLD = 8;
+
+// 桶内红黑树转化为链表的键值对数量阈值
+static final int UNTREEIFY_THRESHOLD = 6;
+
+// 当桶的数量没有达到这个阈值时,桶内链表不会转化为红黑树
+static final int MIN_TREEIFY_CAPACITY = 64;
+
+// 桶数组
+transient Node[] table;
+
+// 键值对总数
+transient int size;
+
+// HashMap修改次数(确切地说是结构变更次数,不包括修改已存在key的value),其实相当于当前集合快照版本,用于迭代器遍历时检查集合是否被修改
+transient int modCount;
+
+// 桶数组扩容阈值
+int threshold;
+
+// 负载因子
+final float loadFactor;
+```
+负载因子 `loadFacotr` 是桶数组相对扩容阈值,是一个相对于桶数量的比例(可以大于1),因此绝对阈值 `threshold` 就是桶的数量 `table.length` 乘以负载因子 `loadFactor` 。当键值对总数 `size` 达到 `threshold` 时,触发 `resize` 方法进行桶数组 `table` 扩容。 因此 `loadFactor` 可以理解为 `HashMap` 时间和空间的权衡
+
+`loadFactor` 是 `HashMap` 初始化时可以指定的,如果未指定则默认为 `DEFAULT_LOAD_FACTOR`
+
+桶的数量即 `table` 的大小也是 `HashMap` 初始化时可以指定的,如果未指定则默认为 `DEFAULT_INITIAL_CAPACITY`
+
+`table` 也不是无限扩容的,最多支持 `MAXIMUM_CAPACITY` 个桶
+
+`table` 的长度必须是2的整数次幂,这是为了取模运算更高效,即hash对2的整数次幂n取模可以用骚操作位运算 `hash & (n - 1)` 。这是也是为什么默认值 `DEFAULT_INITIAL_CAPACITY` 及最大值 `MAXIMUM_CAPACITY` 也要求是2的整数次幂
+
+`table` 的元素是 `HashMap.Node` 类型,`HashMap.Node` 是默认的链表节点,`HashMap.TreeNode` 是红黑树节点,继承了 `HashMap.Node` 。既然链表有头树有根,`table` 中就只引用一个头/根节点即可。
+
+当某个桶内键值对数量超过 `TREEIFY_THRESHOLD` 时将触发 `treeifyBin` 方法将这个桶的链表转化为红黑树,当然这有个大前提,就是当前桶数量不少于 `MIN_TREEIFY_CAPACITY` ,因为桶很少的时候冲突的可能性就是非常高,这时就因为某个链太长就转为红黑树太鲁莽了,怎么也得先多弄几个桶看看是桶太少还是哈希函数太烂
+
+由于 `HashMap.TreeNode` 空间几乎是 `HashMap.Node` 的2倍,因此在性能提升不大的情况下链表没必要转化为红黑树,另外对于良好实现分布均匀的哈希函数,冲突的概率很小,对应于这种情况就应该只有极低的概率链表转化红黑树。对于服从常数为0.5的泊松分布的哈希函数,8个 `key` 落到同一个桶中的概率只有0.00000006,因此将 `TREEIFY_THRESHOLD` 设为8可以满足上面的论断
+
+如果某个桶已经转化为红黑树,`resize` 后原先桶里的键值对可能落到不同的桶中,即触发 `split`方法,`split` 后树节点可能很少了,浪费多一倍的空间没什么必要了,可以转化回链表,即触发 `untreeify` 。如果没有这个操作,多次 `resize` 和 `split` 后链表可能有很多的桶只有很少的节点,但却使用红黑树结构。`split` 后一个桶内红黑树节点降到多少转化回链表受阈值 `UNTREEIFY_THRESHOLD` 控制
+
+
+#### 哈希
+``` java
+static final int hash(Object key) {
+ int h;
+ // HashMap允许key为null,对应的hash为0
+ // 由于hash之后要 &(table.length-1)确定桶索引,只有比table.length唯一的1的位低的位保留下来,高位信息都被过滤掉了
+ // 对于哈希函数分布不均的情况这里挣扎了下,将hash低16位和高16位做异或,这样高位信息也会反应在低位中,降低某些哈希函数实现只在高位变化从而碰撞的概率,当然这里也只是用开销很小的位操作,因为分布平均的哈希函数不需要挣扎,而实现比较烂的哈希函数有转化红黑树兜底
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+
+#### 构造函数及相关辅助方法
+``` java
+public HashMap(int initialCapacity, float loadFactor) {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal initial capacity: " +
+ initialCapacity);
+ // 桶数量不超过MAXIMUM_CAPACITY
+ if (initialCapacity > MAXIMUM_CAPACITY)
+ initialCapacity = MAXIMUM_CAPACITY;
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new IllegalArgumentException("Illegal load factor: " +
+ loadFactor);
+ this.loadFactor = loadFactor;
+ // 调用tableSizeFor得到是桶数量,这里却赋值给了threshold,这是一个构造时的临时处理,因为table是延迟初始化的,并且没有专门的字段存储桶容量,因此先扔给threshold存着,具体等第一次resize初始化table时再将真正的扩容阈值赋给threshold
+ this.threshold = tableSizeFor(initialCapacity);
+}
+
+public HashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+}
+
+public HashMap() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+}
+
+public HashMap(Map extends K, ? extends V> m) {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ putMapEntries(m, false);
+}
+
+// 计算大于等于cap的最小的2的整数幂
+static final int tableSizeFor(int cap) {
+ // -1是专门针对cap正好就是2的整数幂这种情况
+ int n = cap - 1;
+ // 将n最高1位右边的位都设置为1
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ // 再+1正好就是2的整数幂
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+}
+
+final void putMapEntries(Map extends K, ? extends V> m, boolean evict) {
+ // 获取传进来的键值对数量
+ int s = m.size();
+ if (s > 0) {
+ // 判断table是否初始化
+ if (table == null) { // pre-size
+ // 用键值对数量除以负载因子倒推桶容量,加上1预防浮点数计算误差,这里还不是真正的桶容量,因为还未向上取最小2的整数幂
+ float ft = ((float)s / loadFactor) + 1.0F;
+ // 桶容量不超过MAXIMUM_CAPACITY
+ int t = ((ft < (float)MAXIMUM_CAPACITY) ?
+ (int)ft : MAXIMUM_CAPACITY);
+ // table没初始化时threshold暂存桶容量,或者threshold为0表示使用默认桶数量,无论哪种情况,这里将通过键值对反推的桶容量取向上最小的2的整数幂赋给threshold,在第一次resize时用来初始化table
+ if (t > threshold)
+ threshold = tableSizeFor(t);
+ }
+ else if (s > threshold)
+ // table已经初始化了,但发现键值对数量超过扩容阈值了,那就赶紧先resize,不要等到putVal再resize
+ resize();
+ // 遍历将每个键值对增加到该HashMap中
+ for (Map.Entry extends K, ? extends V> e : m.entrySet()) {
+ K key = e.getKey();
+ V value = e.getValue();
+ putVal(hash(key), key, value, false, evict);
+ }
+ }
+}
+```
+
+
+#### 桶容量
+``` java
+// 这里恰好和构造函数呼应,体现了table延迟初始化前后桶容量是如何保存的
+final int capacity() {
+ // 1. table若已初始化,当然table.length就是桶容量
+ // 2. 若table未初始化,且threshold大于0,对应HashMap前两个构造函数,参数指定了桶容量,暂存在threshold中
+ // 3. 若table未初始化,且threshold等于0,对应HashMap第三个构造函数(无参),没有指定桶容量则使用默认桶容量
+ return (table != null) ? table.length :
+ (threshold > 0) ? threshold :
+ DEFAULT_INITIAL_CAPACITY;
+}
+```
+
+
+#### 扩容
+``` java
+final Node[] resize() {
+ Node[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ // oldCap大于0,说明table已经初始化
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ // 桶容量已经达到MAXIMUM_CAPACITY了,再也扩不动了
+ // 将threshold设置为Integer.MAX_VALUE,再也不触发resize
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ // 桶容量扩容一倍
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ // 只有扩容后容量未达到MAXIMUM_CAPACITY并且扩容前容量不低于DEFAULT_INITIAL_CAPACITY时才将threshold增大一倍
+ // 第一个条件类似上面的分支,扩容后如果桶容量达到MAXIMUM_CAPACITY,那么threshold就应该设置为Integer.MAX_VALUE而不是傻傻地增大一倍,这个操作在下面newThr==0的分支中处理
+ // 第二个条件是因为当桶容量很小的时候,threshold移位操作带来的小数点上的误差影响非常大,应该由扩容后的桶容量乘以负载因子重新计算,这同样交给下面newThr==0的分支中计算
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ // table未初始化但threshold大于0,说明指定容量构造后第一次resize,threshold暂存的就是table初始化的容量,这里正式移交给桶容量变量,threshold本身则由下面newThr==0的分支中计算
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ // table未初始化且threshold等于0,说明无参构造后第一次resize,桶容量使用默认容量,threshold直接由默认容量乘以负载因子计算
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ // 计算扩容阈值
+ float ft = (float)newCap * loadFactor;
+ // 如果桶容量达到MAXIMUM_CAPACITY或扩容阈值达到MAXIMUM_CAPACITY,直接将threshold设置为Integer.MAX_VALUE,否则将计算好的阈值赋给threshold
+ // 之所以还要判断计算的阈值是否达到MAXIMUM_CAPACITY是因为loadFactor是可能大于1的
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ // 用扩容容量构造新table
+ Node[] newTab = (Node[])new Node[newCap];
+ table = newTab;
+ // 如果table不是第一次初始化,则需要将旧table的键值对迁移到新table中,键值对可能在新table中落到另外一个桶中
+ if (oldTab != null) {
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ // 移除旧table中引用在这次循环后尽早回收
+ oldTab[j] = null;
+ // e.next为null说明该桶内只有一个节点
+ if (e.next == null)
+ // 直接将这个节点移到新table中hash对应的索引即可
+ newTab[e.hash & (newCap - 1)] = e;
+ // e.next不为null说明桶内有多个节点,可能是链表也可能是红黑树
+ else if (e instanceof TreeNode)
+ // 该桶是红黑树
+ // 很有可能拆成两棵分别迁移到不同的桶
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ // 该桶是链表
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ do {
+ next = e.next;
+ // 由于oldCap是2的整数幂,只有唯一的1,e.hash&oldCap得到e.hash相应这一位的信息。如果结果为0,则说明 e.hash % newCap < oldCap,则扩容前后该节点落到相同索引的桶(低索引半区);但如果结果为1,则说明 oldCap <= e.hash % newCap < newCap,扩容后该节点将落在新扩展的高索引半区并且与扩容前桶索引(低索引半区)相差oldCap
+ if ((e.hash & oldCap) == 0) {
+ // 通过低索引链表尾节点判断低索引桶是否有节点
+ if (loTail == null)
+ // 低索引桶第一个节点,设置低索引链表头节点
+ loHead = e;
+ else
+ // 追加到低索引链表尾节点
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ // 通过高索引链表尾节点判断高索引桶是否有节点
+ if (hiTail == null)
+ // 高索引桶第一个节点,设置高索引链表头节点
+ hiHead = e;
+ else
+ // 追加到高索引链表尾节点
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {
+ loTail.next = null;
+ // 设置低索引桶
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {
+ hiTail.next = null;
+ // 设置高索引桶
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+}
+```
+
+#### 键值对总数
+``` java
+public int size() {
+ return size;
+}
+
+public boolean isEmpty() {
+ return size == 0;
+}
+```
+
+
+#### 增/改键值对
+``` java
+public V put(K key, V value) {
+ return putVal(hash(key), key, value, false, true);
+}
+
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node[] tab; Node p; int n, i;
+ // 判断table是否初始化
+ if ((tab = table) == null || (n = tab.length) == 0)
+ // 触发resize初始化table
+ n = (tab = resize()).length;
+ // 判断所属桶是否有节点
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ // 没有节点则直接创建链表头节点
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ // 该桶已经有节点
+ Node e; K k;
+ // 判断头/根节点是否是要找的key
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ // 判断是否是红黑树根节点
+ else if (p instanceof TreeNode)
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ // 链表有多个节点且头节点不是要找的key,则向下遍历链表
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ // 遍历了一圈没有找到key,则说明需要新增一个键值对
+ p.next = newNode(hash, key, value, null);
+ // 判断该桶内链表节点数量是否达到转化红黑树阈值TREEIFY_THRESHOLD,这里用TREEIFY_THRESHOLD - 1是因为头节点已经在循环前遍历过了
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ // 达到转化红黑树的阈值,转化红黑树
+ treeifyBin(tab, hash);
+ break;
+ }
+ // 判断该节点是否是要找的key
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ // 如果找到了key,则说明是一个值更新操作
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ }
+ ++modCount;
+ // 递增键值对总数size,判断结果是否超过扩容阈值threshold
+ if (++size > threshold)
+ // 超过threshold,调用resize扩容
+ resize();
+ afterNodeInsertion(evict);
+ return null;
+}
+
+public void putAll(Map extends K, ? extends V> m) {
+ putMapEntries(m, true);
+}
+```
+
+
+#### 转化红黑树
+``` java
+final void treeifyBin(Node[] tab, int hash) {
+ int n, index; Node e;
+ // 如果桶容量小于MIN_TREEIFY_CAPACITY,说明桶数量还太少,则扩容而不是转化红黑树
+ if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+ resize();
+ else if ((e = tab[index = (n - 1) & hash]) != null) {
+ TreeNode hd = null, tl = null;
+ do {
+ TreeNode p = replacementTreeNode(e, null);
+ if (tl == null)
+ hd = p;
+ else {
+ p.prev = tl;
+ tl.next = p;
+ }
+ tl = p;
+ } while ((e = e.next) != null);
+ if ((tab[index] = hd) != null)
+ hd.treeify(tab);
+ }
+}
+```
+
+
+#### 查找
+``` java
+public V get(Object key) {
+ Node e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+}
+
+final Node getNode(int hash, Object key) {
+ Node[] tab; Node first, e; int n; K k;
+ // 判断table是否初始化以及hash对应的桶是否有节点
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ // 判断头/根节点是否是要找的key
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ // 如果头/根节点不是要找的key,且桶内还有其他节点
+ if ((e = first.next) != null) {
+ // 判断是否是红黑树,如是则在红黑树内查找key
+ if (first instanceof TreeNode)
+ return ((TreeNode)first).getTreeNode(hash, key);
+ // 否则遍历链表
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ // 没找到key
+ return null;
+}
+```
+
+
+#### 删除
+``` java
+public V remove(Object key) {
+ Node e;
+ return (e = removeNode(hash(key), key, null, false, true)) == null ?
+ null : e.value;
+}
+
+final Node removeNode(int hash, Object key, Object value,
+ boolean matchValue, boolean movable) {
+ Node[] tab; Node p; int n, index;
+ // 与getNode类似,查找key的节点
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (p = tab[index = (n - 1) & hash]) != null) {
+ Node node = null, e; K k; V v;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ node = p;
+ else if ((e = p.next) != null) {
+ if (p instanceof TreeNode)
+ node = ((TreeNode)p).getTreeNode(hash, key);
+ else {
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key ||
+ (key != null && key.equals(k)))) {
+ node = e;
+ break;
+ }
+ p = e;
+ } while ((e = e.next) != null);
+ }
+ }
+ // 判断节点是否找到且满足值匹配条件(如果开启值匹配选项)
+ if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) {
+ // 判断是否是红黑树节点
+ if (node instanceof TreeNode)
+ // 移除红黑树节点
+ ((TreeNode)node).removeTreeNode(this, tab, movable);
+ // 判断是否是链表头节点
+ else if (node == p)
+ // 无论是下一个节点还是null,都直接赋给桶
+ tab[index] = node.next;
+ else
+ // 父节点next直接指向子节点,及删除链表当前节点
+ p.next = node.next;
+ ++modCount;
+ // 递减键值对总数
+ --size;
+ afterNodeRemoval(node);
+ return node;
+ }
+ }
+ // 没找到key
+ return null;
+}
+
+public void clear() {
+ Node[] tab;
+ modCount++;
+ if ((tab = table) != null && size > 0) {
+ size = 0;
+ // 清除所有桶对头/根节点的引用
+ for (int i = 0; i < tab.length; ++i)
+ tab[i] = null;
+ }
+}
+```
diff --git "a/week_01/16/ArrayList\346\272\220\347\240\201\345\210\206\346\236\220_016.pdf" "b/week_01/16/ArrayList\346\272\220\347\240\201\345\210\206\346\236\220_016.pdf"
new file mode 100644
index 0000000..a6029e3
Binary files /dev/null and "b/week_01/16/ArrayList\346\272\220\347\240\201\345\210\206\346\236\220_016.pdf" differ
diff --git "a/week_01/16/LinkedList\346\272\220\347\240\201\345\210\206\346\236\220_016.pdf" "b/week_01/16/LinkedList\346\272\220\347\240\201\345\210\206\346\236\220_016.pdf"
new file mode 100644
index 0000000..81126d0
Binary files /dev/null and "b/week_01/16/LinkedList\346\272\220\347\240\201\345\210\206\346\236\220_016.pdf" differ
diff --git a/week_01/17/List/ArrayList-17 b/week_01/17/List/ArrayList-17
new file mode 100644
index 0000000..f1f1c98
--- /dev/null
+++ b/week_01/17/List/ArrayList-17
@@ -0,0 +1,48 @@
+##ArrayList
+
+ ##接口实现
+ List,
+ 标记接口 RandomAccess, --可快速访问,即访问速度为1次。
+ 标记接口 Cloneable, ----可克隆。
+ 标记接口 java.io.Serializable--可序列化。
+
+
+ ##底层实现
+ 1.ArrayList底层是数组,可以根据Size动态增长。
+ ##扩容
+ 1.ArrayList的默认长度为10。
+ 2.每一次扩容为当前长度的50%。
+ 依据 int newCapacity = oldCapacity + (oldCapacity >> 1);
+ 3.copy,新起一个数组,再将原始数据放到新的数组里去。
+ ##遍历
+ 1.因为实现 RandomAccess 该接口,则 for循环的速度比Iterator迭代器快,出处RandomAccess文档。
+ 2.关于For,ForEach 循环。
+ for在遍历数组结构的数据快。
+ forEach在遍历链表数据比较快。
+ ##线程安全
+ 1.否
+ ##属性
+ private static final long serialVersionUID = 8683452581122892189L;
+ //默认长度
+ private static final int DEFAULT_CAPACITY = 10;
+ //空数组
+ private static final Object[] EMPTY_ELEMENTDATA = {};
+ //transient 不会被序列化(如实现Externalizable,并且手动序列化了)该修饰符将没有作用
+ transient Object[] elementData;
+ //构造一个空集合的时候,返回该属性
+ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+
+ ##构造方法
+ 1.public ArrayList() {
+ this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+ }
+ 2.ArrayList(int i){
+ i==返回 EMPTY_ELEMENTDATA。
+ i>0返回一个i长度的Object的数组
+
+ }
+ 3.ArrayList(Collection extends E> c){
+ c.长度为0 返回空数组。
+ c.长度不为0 返回一个C长度的Object[]数组。
+
+ }
\ No newline at end of file
diff --git a/week_01/17/List/HashMap-17 b/week_01/17/List/HashMap-17
new file mode 100644
index 0000000..5c7b0ba
--- /dev/null
+++ b/week_01/17/List/HashMap-17
@@ -0,0 +1,102 @@
+##HashMap
+
+ ##接口
+ 1.Map
+ 2.Cloneable --可拷贝
+ 3.Serializable-可序列化
+ ##属性
+ //默认长度16, 必须是2的幂
+ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
+
+ static final int MAXIMUM_CAPACITY = 1 << 30;
+ //扩容因子(数据量到75%,则扩容一次)
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ //变成树的一个阀值
+ static final int TREEIFY_THRESHOLD = 8;
+ //取消树的一个阀值
+ static final int UNTREEIFY_THRESHOLD = 6;
+
+ static final int MIN_TREEIFY_CAPACITY = 64;
+ ##构造函数
+ 1.public HashMap() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+ }
+ 则当前Map 长度16,扩容因子75%
+ 2.HashMap(int i){
+ HashMap(int initialCapacity, float loadFactor)
+ }
+
+ 3.public HashMap(Map extends K, ? extends V> m) {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ putMapEntries(m, false);
+ }
+
+
+ 4.方法
+ /**
+ * Implements Map.put and related methods.
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @param value the value to put
+ * @param onlyIfAbsent if true, don't change existing value
+ * @param evict if false, the table is in creation mode.
+ * @return previous value, or null if none
+ */
+ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node[] tab;
+ Node p;
+ int n, i;
+ //如果表是空的则从表新计算
+ if ((tab = table) == null || (n = tab.length) == 0)
+ //获取表新计算的长度
+ n = (tab = resize()).length;
+ //没有Hahs碰撞,直接在该角标插入新值
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ //新增一个桶到指定表中
+ tab[i] = newNode(hash, key, value, null);
+ //有哈希碰撞
+ else {
+ Node e; K k;
+ //如果在原来的找到了一个相同的桶,则把之前的桶赋值过来
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ //看下 P 是不是是个树,拷贝一个树
+ else if (p instanceof TreeNode)
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ //如果不是一个树
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ //该节点后面没有值,当前Hash只有这一个值
+ if ((e = p.next) == null) {
+ //新建一个节点赋值给表当前的位置
+ p.next = newNode(hash, key, value, null);
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ //如果是同一个值,则不对此节点做操作
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ }
+ //记录该Map修改的次数
+ ++modCount;
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
+ return null;
+ }
+ ##HashMap源码还需要看,请各位大佬,指导一下,第一次看,懵逼中。。。。。。。。。。。。。。。
\ No newline at end of file
diff --git a/week_01/18/ArrayList.md b/week_01/18/ArrayList.md
new file mode 100644
index 0000000..2dc8ab1
--- /dev/null
+++ b/week_01/18/ArrayList.md
@@ -0,0 +1,90 @@
+# ArrayList
+
+## ArrayList的基础特点
+ 1. Resizable-array
+ 2. permits all elements, including null
+ 3. unsynchronized vs Vector
+ 4. adding n elements requires O(n) time
+ 5. it is always at least as large as the list size. As elements are added to an ArrayList, its capacity grows automatically.*
+
+- ArrayList 的类关系?
+## Capacity
+1. ArrayList 的几种构造函数对 ArrayList容量的影响? 传入集合的构造函数的实现?
+ 1. 默认构造函数
+
+ 2. 构造函数,带初始容器initialCapacity参数
+
+- 通过上面的比较,`DEFAULTCAPACITY_EMPTY_ELEMENTDATA ` 和`EMPTY_ELEMENTDATA` ,**前者知道扩容的容量是10**
+
+
+2. 几种控制ArrayList 容量的方法?
+ - 如何缩容?这个方法可以直观的说明 ::size<= capacity:: ,将容量缩减至当前的size 大小
+
+ - 如何扩容?(第一种:目标容量从外部传入,即调用方指定目标容量)
+
+ - 如何扩容?(第二种:目标容量从外部传入,即调用方指定目标容量)
+
+
+## modCount
+1. modCount的表达的意义(什么是 structurally modified)?用途和如何使用?
+ - 意义
+
+ - 用途(什么是 fail-fast behavior)和如何使用(关键看子类方法是否要提供 fail-fast 功能)
+
+
+
+2. ArrayList 中哪些方法会导致集合structurally modified(需要modCount++)?
+> sort,replaceAll等其他方法同样同样分析
+ - 在增加元素的时候modCount++,因为都要调扩容方法,modCount++写在扩容方法里
+
+ - 除了增删改,sort方法为什么也会导致(structurally modified.需要modCount++) ?
+ - 为什么校验 modCount 值?
+ - sort排序的过程也要迭代元素,比如最后**modCount**发生了改变,你的排序是不准确的,要抛出并发修改异常
+ - 这里 sort 用的是**归并排序**
+
+ - 排序虽然不会增删元素,但是可能会将(2,3,1)—>(1,2,3),集合发生了结构性改变;而且,这时候如果有其它方法校验集合的第一个元素就发现了2变成了1,如果不让 modCount++.其它方法就没办法发现这种改变,造成不可预期的错误
+
+ - 注意:对增删来讲没增删一个元素 modCount++,所以批量删除方法这样写的
+
+
+
+## batchRemove方法
+1. removeAll(Collection> c),retainAll(Collection> c)
+怎么实现的?
+
+- contains(Object o) 什么时候抛出异常
+
+
+## ArrayList 的迭代器ListItr,Itr和Iterator
+1. ListItr,ListIterator.Itr和Iterator之间的关系?
+
+
+2. ListItr迭代器拥有的3个属性的含义?
+ 1. int cursor**; *// index of next element to return*
+ 2. int lastRet**= -1; *// index of last element returned; -1 if no such*
+ 3. int expectedModCount= modCount; (校验容器是否被并发修改)
+3. 迭代器的构造函数的作用?
+
+
+4. ListItr迭代器在父类 Itr 迭代器的基础上新增的 previous()方法实现?新增 add,set 方法的特点,以及和 ArrayList中的 add,set 的区别?
+> 关键点是怎么维护cursor,lastRet两个字段
+ - add是加在 cursor 指向的索引位置,因为不返回任何元素,**lastRet= -1**;但是还可以接着加元素,所以**cursor+1**
+ - set是更新lastRet位置的元素,并返回旧值
+
+
+
+## SubList
+1. 为什么要在 ArrayList 里搞一个内部类数据结构 SubList?
+ - 这里面主要是视图思想,同一个数据源可以提供多个不同的视图对象(提供需要的数据,隐藏不需要的数据,安全又方便)
+ - 感觉在 JDK8后这些就被流取代了,Stream流更适合做这些, SubList毕竟还是数据结构
+
+
+
+## Java8引入函数式接口带来的改变
+ 1. [[Spliterator]] <-----待补充
+
+1. `transient Object[] elementData;`
+的作用?为什么用 transient 修饰?
+2. fail-fast behavior of an iterator? fail-fast机制带来的问题?
+
+
diff --git a/week_01/18/ArrayList/18F2F986-FECA-4F67-A7C1-554722D8C9D9.png b/week_01/18/ArrayList/18F2F986-FECA-4F67-A7C1-554722D8C9D9.png
new file mode 100644
index 0000000..7f709f0
Binary files /dev/null and b/week_01/18/ArrayList/18F2F986-FECA-4F67-A7C1-554722D8C9D9.png differ
diff --git a/week_01/18/ArrayList/1B8F3C92-DE88-42D8-98DC-C0D48ABB192B.png b/week_01/18/ArrayList/1B8F3C92-DE88-42D8-98DC-C0D48ABB192B.png
new file mode 100644
index 0000000..c844d54
Binary files /dev/null and b/week_01/18/ArrayList/1B8F3C92-DE88-42D8-98DC-C0D48ABB192B.png differ
diff --git a/week_01/18/ArrayList/260DD573-E696-4AA4-8F9E-C61A5C4FCB20.png b/week_01/18/ArrayList/260DD573-E696-4AA4-8F9E-C61A5C4FCB20.png
new file mode 100644
index 0000000..e8c7924
Binary files /dev/null and b/week_01/18/ArrayList/260DD573-E696-4AA4-8F9E-C61A5C4FCB20.png differ
diff --git a/week_01/18/ArrayList/3236957C-AEE5-41BF-A7C3-E79542C095F2.png b/week_01/18/ArrayList/3236957C-AEE5-41BF-A7C3-E79542C095F2.png
new file mode 100644
index 0000000..7992b4e
Binary files /dev/null and b/week_01/18/ArrayList/3236957C-AEE5-41BF-A7C3-E79542C095F2.png differ
diff --git a/week_01/18/ArrayList/3F7854B9-884A-49EF-A2D3-6EA5E9F506D4.png b/week_01/18/ArrayList/3F7854B9-884A-49EF-A2D3-6EA5E9F506D4.png
new file mode 100644
index 0000000..783e71f
Binary files /dev/null and b/week_01/18/ArrayList/3F7854B9-884A-49EF-A2D3-6EA5E9F506D4.png differ
diff --git a/week_01/18/ArrayList/4A579666-1860-4B86-8921-2749B3F8F839.png b/week_01/18/ArrayList/4A579666-1860-4B86-8921-2749B3F8F839.png
new file mode 100644
index 0000000..b963ea5
Binary files /dev/null and b/week_01/18/ArrayList/4A579666-1860-4B86-8921-2749B3F8F839.png differ
diff --git a/week_01/18/ArrayList/6C380A25-F767-4D36-87DF-FB60DE1B89F5.png b/week_01/18/ArrayList/6C380A25-F767-4D36-87DF-FB60DE1B89F5.png
new file mode 100644
index 0000000..6f2f658
Binary files /dev/null and b/week_01/18/ArrayList/6C380A25-F767-4D36-87DF-FB60DE1B89F5.png differ
diff --git a/week_01/18/ArrayList/76CEBE20-CAB1-4A53-A9F2-6C20D90A08B0.png b/week_01/18/ArrayList/76CEBE20-CAB1-4A53-A9F2-6C20D90A08B0.png
new file mode 100644
index 0000000..cc543a5
Binary files /dev/null and b/week_01/18/ArrayList/76CEBE20-CAB1-4A53-A9F2-6C20D90A08B0.png differ
diff --git a/week_01/18/ArrayList/7B84EAAB-ADA6-47FD-A836-6BC69AA7B3E3.png b/week_01/18/ArrayList/7B84EAAB-ADA6-47FD-A836-6BC69AA7B3E3.png
new file mode 100644
index 0000000..d7d998d
Binary files /dev/null and b/week_01/18/ArrayList/7B84EAAB-ADA6-47FD-A836-6BC69AA7B3E3.png differ
diff --git a/week_01/18/ArrayList/92C4B78A-AFD7-4FA8-8FED-A2016BB83935.png b/week_01/18/ArrayList/92C4B78A-AFD7-4FA8-8FED-A2016BB83935.png
new file mode 100644
index 0000000..60dd8a7
Binary files /dev/null and b/week_01/18/ArrayList/92C4B78A-AFD7-4FA8-8FED-A2016BB83935.png differ
diff --git a/week_01/18/ArrayList/9AFEEE04-BE12-4CB2-9A8F-7EE1570E1660.png b/week_01/18/ArrayList/9AFEEE04-BE12-4CB2-9A8F-7EE1570E1660.png
new file mode 100644
index 0000000..aca6c47
Binary files /dev/null and b/week_01/18/ArrayList/9AFEEE04-BE12-4CB2-9A8F-7EE1570E1660.png differ
diff --git a/week_01/18/ArrayList/A5418EEA-107A-40D1-AD2F-C180951E180F.png b/week_01/18/ArrayList/A5418EEA-107A-40D1-AD2F-C180951E180F.png
new file mode 100644
index 0000000..d37765c
Binary files /dev/null and b/week_01/18/ArrayList/A5418EEA-107A-40D1-AD2F-C180951E180F.png differ
diff --git a/week_01/18/ArrayList/BCA8000C-ABFC-4361-8B4E-927C709A5989.png b/week_01/18/ArrayList/BCA8000C-ABFC-4361-8B4E-927C709A5989.png
new file mode 100644
index 0000000..b046f52
Binary files /dev/null and b/week_01/18/ArrayList/BCA8000C-ABFC-4361-8B4E-927C709A5989.png differ
diff --git a/week_01/18/ArrayList/BF00ECE6-732E-444A-ACB1-A6A8FBF0E713.png b/week_01/18/ArrayList/BF00ECE6-732E-444A-ACB1-A6A8FBF0E713.png
new file mode 100644
index 0000000..dd7c7e8
Binary files /dev/null and b/week_01/18/ArrayList/BF00ECE6-732E-444A-ACB1-A6A8FBF0E713.png differ
diff --git a/week_01/18/ArrayList/C2139442-9F3B-44BE-A66E-17FCDAF013EE.png b/week_01/18/ArrayList/C2139442-9F3B-44BE-A66E-17FCDAF013EE.png
new file mode 100644
index 0000000..aea89af
Binary files /dev/null and b/week_01/18/ArrayList/C2139442-9F3B-44BE-A66E-17FCDAF013EE.png differ
diff --git a/week_01/18/ArrayList/D061D159-5CD3-40A5-AA1E-97E39EADD00D.png b/week_01/18/ArrayList/D061D159-5CD3-40A5-AA1E-97E39EADD00D.png
new file mode 100644
index 0000000..889a377
Binary files /dev/null and b/week_01/18/ArrayList/D061D159-5CD3-40A5-AA1E-97E39EADD00D.png differ
diff --git a/week_01/18/ArrayList/E2F335A9-19FF-4CF4-84C7-559019C8BF6D.png b/week_01/18/ArrayList/E2F335A9-19FF-4CF4-84C7-559019C8BF6D.png
new file mode 100644
index 0000000..0f49c18
Binary files /dev/null and b/week_01/18/ArrayList/E2F335A9-19FF-4CF4-84C7-559019C8BF6D.png differ
diff --git a/week_01/18/ArrayList/FE975389-C228-445D-A53F-BD5AF5290B1E.png b/week_01/18/ArrayList/FE975389-C228-445D-A53F-BD5AF5290B1E.png
new file mode 100644
index 0000000..df81cd0
Binary files /dev/null and b/week_01/18/ArrayList/FE975389-C228-445D-A53F-BD5AF5290B1E.png differ
diff --git a/week_01/18/HashMap.md b/week_01/18/HashMap.md
new file mode 100644
index 0000000..8b07828
--- /dev/null
+++ b/week_01/18/HashMap.md
@@ -0,0 +1,125 @@
+# HashMap
+[HashMap源码之resize方法 - 简书](https://www.jianshu.com/p/4fc089ca25dd)
+[JDK 1.8 中 HashMap 扩容 - 算法网](http://ddrv.cn/a/234367)
+[hash()、tableSizeFor()()](https://blog.csdn.net/fan2012huan/article/details/51097331)
+## HashMap的基础特点
+ 1. permits null values and the null key
+ 2. unsynchronized and permits nulls (vs Hashtable)
+ 3. makes no guarantees as to the order of the map
+ 4. **initial capacity** and **load factor**affect its performance
+ 5. not synchronized
+
+## 基本数据结构和接口
+ 1. 什么是 Map 中的 Entry接口,Entry 表达的含义支持什么操作以及如何获取一个Map 的引用?
+
+- 获取集合视图,有了集合就可以使用集合的迭代器迭代
+
+
+ 2. HashMap 中的静态内部类Node的数据结构是怎样的?
+ - 递归的数据结构(链表)
+
+ 3. HashMap 中的内部类KeySet的数据结构是怎样的,和其它AbstractCollection 的实现类相比较提供了什么特点,这些特点来自于哪里?
+ - 继承 AbstractSet抽象类—> 继承自AbstractCollection抽线累
+ - KeySet对AbstractCollection中的抽象方法提供了实现(AbstractSet没有实现剩下的方法),并添加了额外的方法Spliterator spliterator()
+ - 总结KeySet的特点来自于这几方面
+ 1. AbstractSet提供的hashCode,equals,removeAll实现方法
+ 2. KeySet自己提供的获取并发迭代器Spliterator的方法
+ 3. KeySet实现的AbstractCollection中的抽象方法,比如获取迭代器实现,返回的是KeyIterator迭代器,remove,forEachss方法也是由 KeySet 自己实现,其它剩下的抽线方法实现size,clear,contains 直接调用的外部类 HashMap 的实现(这也是内部类的好处,持有外部类的引用)
+
+ 4. HashMap中的内部类数据结构,除了 KeySet 这一种 HashMap 返回的集合视图对象,还有Values,EntrySet这两种内部类数据结构(集合视图对象),它们之间的实现有哪些异同?
+ - 这3种集合视图对象能获取的迭代器不同,迭代的细节不同
+ - 当然 remove()移除,contains()校验包含 的对象也不同
+
+ 5. HashMap中抽象类HashIterator的数据结构是怎样的,各成员变量的含义?它提供的实现和特点?
+ - HashIterator提供了 HashMap 中3种具体的迭代器的一些共性(作为它们的父类)
+ - **next将表示第一个非空桶中的第一个结点,index将表示下一个桶**。
+
+ - HashIterator 中nextNode()方法的实现?
+
+
+ 6. KeyIterator,ValueIterator,EntryIterator3种迭代器数据结构是怎样的?
+ - 从这三个迭代器来看,HashMap可以提供三种集合视图对象,而3种不同的迭代器在迭代时返回 Node不同的部分
+
+
+ 7. HashMapSpliterator,KeySpliterator,ValueSpliterator,EntrySpliterator 待补充
+
+
+
+## 关键的属性(成员变量)
+ 1. 什么是 HashMap 的loadFactor,initialCapacity,threshold?他们的作用和互相关系?
+ - threshold: HashMap进行扩容的阈值,它的值等于 HashMap 的容量乘以负载因子,The next size value at which to resize (capacity *load factor).
+
+
+ 2. 什么是 HashMap中的transient Node[] table?
+ - The table, initialized on first use, and resized as necessary. When allocated, length is always a power of two.
+ - **HashMap的底层实现仍是数组,只是数组的每一项都是一条链表(初始化时)**
+
+ 3. HashMap 中 TREEIFY_THRESHOLD ,UNTREEIFY_THRESHOLD和MIN_TREEIFY_CAPACITY的作用?
+
+
+
+## 关键方法
+### hash()、tableSizeFor()方法
+1. 为什么要有HashMap的hash()方法,难道不能直接使用KV中K原有的hash值吗?在HashMap的put、get操作时为什么不能直接使用K中原有的hash值?
+ - 注意:0101这个第四位就是由高位>>>16产生的,这样高位就参与计算了,减少了碰撞
+
+
+2. HashMap 中tableSizeFor()方法的作用和算法实现的过程?
+ - 作用: 在实例化HashMap实例时,如果给定了initialCapacity,由于HashMap的capacity都是2的幂,因此这个方法用于找到大于等于initialCapacity的最小的2的幂(initialCapacity如果就是2的幂,则返回的还是这个数)。
+
+
+
+### resize方法
+1. 哪两种情况(具体是3种情况)会调用resize()方法,resize()后的容量分别表现怎样?
+
+
+2. HashMap 中resize 时候,如何完成oldTab中Node迁移到table(newTab)中去可能遇到哪三种情况,分别如何实现的?
+
+
+- 链表扩容示意图
+
+
+
+
+
+
+### put 方法
+ 1. HashMap 中 put 方法的执行逻辑?
+ 2. HashMap 中 put 方法中的如何插入数组的索引位置对应存在红黑树了,*红黑树的插入操作是怎样的,即putTreeVal方法执行过程?
+
+
+## 获取 HashMap 内部的数据结构的实例
+
+
+
+## HashCode 方法
+## HashMap的性能
+ 1. 影响HashMap的性能的两个因素?
+ 2. 为什么the default load factor 设置为 (.75)?
+ 3. HashMap如何扩容?
+
+
+## TREEIFY(树化)
+ 1. 树化相关的属性?
+ 2. TREEIFY和UNTREEIFY的时机和过程?
+
+
+
+## fail-fast机制和安全性
+通过 ABC 三个线程具体说明(ABC 之间相互依赖的情况)
+ 1. the fail-fast behavior of iterators should be used only to detect bugs
+
+
+
+## 其它
+ - 为什么需要 Node[] tab,不直接访问table
+
+
+
+
+
+
+
+## 1.7vs1.8
+ - 1.7,1.8HashMap 扩容的对比?
+ - 待补充
\ No newline at end of file
diff --git a/week_01/18/HashMap/070E9364-3CDA-4685-9DAC-609FC3DCBE8B.png b/week_01/18/HashMap/070E9364-3CDA-4685-9DAC-609FC3DCBE8B.png
new file mode 100644
index 0000000..2dd2d03
Binary files /dev/null and b/week_01/18/HashMap/070E9364-3CDA-4685-9DAC-609FC3DCBE8B.png differ
diff --git a/week_01/18/HashMap/0E7306FA-D012-43E3-8FD2-C2C08745249F.png b/week_01/18/HashMap/0E7306FA-D012-43E3-8FD2-C2C08745249F.png
new file mode 100644
index 0000000..8832357
Binary files /dev/null and b/week_01/18/HashMap/0E7306FA-D012-43E3-8FD2-C2C08745249F.png differ
diff --git a/week_01/18/HashMap/17145701-8f7c6a1b2062212a.png b/week_01/18/HashMap/17145701-8f7c6a1b2062212a.png
new file mode 100644
index 0000000..35a4757
Binary files /dev/null and b/week_01/18/HashMap/17145701-8f7c6a1b2062212a.png differ
diff --git a/week_01/18/HashMap/27063A0A-F3D8-43B7-A6C4-62941FC103D7.png b/week_01/18/HashMap/27063A0A-F3D8-43B7-A6C4-62941FC103D7.png
new file mode 100644
index 0000000..0bfa388
Binary files /dev/null and b/week_01/18/HashMap/27063A0A-F3D8-43B7-A6C4-62941FC103D7.png differ
diff --git a/week_01/18/HashMap/2ead182893de70e85e71ef3dbd0c5b36.jpg.png b/week_01/18/HashMap/2ead182893de70e85e71ef3dbd0c5b36.jpg.png
new file mode 100644
index 0000000..b1673bb
Binary files /dev/null and b/week_01/18/HashMap/2ead182893de70e85e71ef3dbd0c5b36.jpg.png differ
diff --git a/week_01/18/HashMap/3561298E-96B7-4F6D-BE2A-00BB2471F72F.png b/week_01/18/HashMap/3561298E-96B7-4F6D-BE2A-00BB2471F72F.png
new file mode 100644
index 0000000..ddfc9d7
Binary files /dev/null and b/week_01/18/HashMap/3561298E-96B7-4F6D-BE2A-00BB2471F72F.png differ
diff --git a/week_01/18/HashMap/39323DC7-EDBC-4B72-80B1-9BB52A7D51D2.png b/week_01/18/HashMap/39323DC7-EDBC-4B72-80B1-9BB52A7D51D2.png
new file mode 100644
index 0000000..db02c97
Binary files /dev/null and b/week_01/18/HashMap/39323DC7-EDBC-4B72-80B1-9BB52A7D51D2.png differ
diff --git a/week_01/18/HashMap/519be432-d93c-11e4-85bb-dff0a03af9d3.png b/week_01/18/HashMap/519be432-d93c-11e4-85bb-dff0a03af9d3.png
new file mode 100644
index 0000000..2a9b2e4
Binary files /dev/null and b/week_01/18/HashMap/519be432-d93c-11e4-85bb-dff0a03af9d3.png differ
diff --git a/week_01/18/HashMap/58F19611-BA2C-4F60-AAB4-96A7B7AD63A2.png b/week_01/18/HashMap/58F19611-BA2C-4F60-AAB4-96A7B7AD63A2.png
new file mode 100644
index 0000000..0a0278c
Binary files /dev/null and b/week_01/18/HashMap/58F19611-BA2C-4F60-AAB4-96A7B7AD63A2.png differ
diff --git a/week_01/18/HashMap/60B179F9-4E4E-4F18-912E-BAE73438408E.png b/week_01/18/HashMap/60B179F9-4E4E-4F18-912E-BAE73438408E.png
new file mode 100644
index 0000000..b6b142d
Binary files /dev/null and b/week_01/18/HashMap/60B179F9-4E4E-4F18-912E-BAE73438408E.png differ
diff --git a/week_01/18/HashMap/8DF53F3E-ED04-4887-9040-8A3E5EFF4CA6.png b/week_01/18/HashMap/8DF53F3E-ED04-4887-9040-8A3E5EFF4CA6.png
new file mode 100644
index 0000000..be6a79d
Binary files /dev/null and b/week_01/18/HashMap/8DF53F3E-ED04-4887-9040-8A3E5EFF4CA6.png differ
diff --git a/week_01/18/HashMap/A112D80B-06CB-4CA3-B2D3-F858D345F9E9.png b/week_01/18/HashMap/A112D80B-06CB-4CA3-B2D3-F858D345F9E9.png
new file mode 100644
index 0000000..3e3f0b1
Binary files /dev/null and b/week_01/18/HashMap/A112D80B-06CB-4CA3-B2D3-F858D345F9E9.png differ
diff --git a/week_01/18/HashMap/A5B79ABC-3BAA-4572-9314-8A24ACBA054A.png b/week_01/18/HashMap/A5B79ABC-3BAA-4572-9314-8A24ACBA054A.png
new file mode 100644
index 0000000..29f9741
Binary files /dev/null and b/week_01/18/HashMap/A5B79ABC-3BAA-4572-9314-8A24ACBA054A.png differ
diff --git a/week_01/18/HashMap/BE66A21F-8ECA-4D54-9DA6-2B6E56411FE5.png b/week_01/18/HashMap/BE66A21F-8ECA-4D54-9DA6-2B6E56411FE5.png
new file mode 100644
index 0000000..324d7da
Binary files /dev/null and b/week_01/18/HashMap/BE66A21F-8ECA-4D54-9DA6-2B6E56411FE5.png differ
diff --git a/week_01/18/HashMap/C95BFC29-5513-4702-A388-8B42B595D2F3.png b/week_01/18/HashMap/C95BFC29-5513-4702-A388-8B42B595D2F3.png
new file mode 100644
index 0000000..57d6ac6
Binary files /dev/null and b/week_01/18/HashMap/C95BFC29-5513-4702-A388-8B42B595D2F3.png differ
diff --git a/week_01/18/HashMap/CDE9997E-6505-4FEF-B302-3F2BA6D16EFE.png b/week_01/18/HashMap/CDE9997E-6505-4FEF-B302-3F2BA6D16EFE.png
new file mode 100644
index 0000000..16b0f93
Binary files /dev/null and b/week_01/18/HashMap/CDE9997E-6505-4FEF-B302-3F2BA6D16EFE.png differ
diff --git a/week_01/18/HashMap/DB3DDD54-51DE-4F54-92EB-750865A314AE.png b/week_01/18/HashMap/DB3DDD54-51DE-4F54-92EB-750865A314AE.png
new file mode 100644
index 0000000..7d27533
Binary files /dev/null and b/week_01/18/HashMap/DB3DDD54-51DE-4F54-92EB-750865A314AE.png differ
diff --git a/week_01/18/HashMap/FDAB1F01-DF08-4EAB-A8ED-CACE9A01BC77.png b/week_01/18/HashMap/FDAB1F01-DF08-4EAB-A8ED-CACE9A01BC77.png
new file mode 100644
index 0000000..d9bc3f2
Binary files /dev/null and b/week_01/18/HashMap/FDAB1F01-DF08-4EAB-A8ED-CACE9A01BC77.png differ
diff --git a/week_01/18/HashMap/qn34qsnq0r4249psn0q5s604o3po08op.jpg.gif b/week_01/18/HashMap/qn34qsnq0r4249psn0q5s604o3po08op.jpg.gif
new file mode 100644
index 0000000..bb4ab69
Binary files /dev/null and b/week_01/18/HashMap/qn34qsnq0r4249psn0q5s604o3po08op.jpg.gif differ
diff --git a/week_01/18/HashMap/url 2.jpg b/week_01/18/HashMap/url 2.jpg
new file mode 100644
index 0000000..8c68fdc
Binary files /dev/null and b/week_01/18/HashMap/url 2.jpg differ
diff --git a/week_01/18/HashMap/url.jpg b/week_01/18/HashMap/url.jpg
new file mode 100644
index 0000000..51d5fa9
Binary files /dev/null and b/week_01/18/HashMap/url.jpg differ
diff --git a/week_01/18/HashMap/url.png b/week_01/18/HashMap/url.png
new file mode 100644
index 0000000..ab648dd
Binary files /dev/null and b/week_01/18/HashMap/url.png differ
diff --git a/week_01/18/HashMap/zA3ENz.png b/week_01/18/HashMap/zA3ENz.png
new file mode 100644
index 0000000..561148b
Binary files /dev/null and b/week_01/18/HashMap/zA3ENz.png differ
diff --git a/week_01/20/ArrayList.md b/week_01/20/ArrayList.md
new file mode 100644
index 0000000..cb8ff47
--- /dev/null
+++ b/week_01/20/ArrayList.md
@@ -0,0 +1,54 @@
++ 定义
+```java
+public class ArrayList extends AbstractList
+ implements List, RandomAccess, Cloneable, java.io.Serializable
+```
++ 实际上是一个动态数组,容量可以动态的增长,其继承了AbstractList
+```java
+//如果是无参构造方法创建对象的话,ArrayList的初始化长度为10,这是一个静态常量
+private static final int DEFAULT_CAPACITY = 10;
+
+//MPTY_ELEMENTDATA实际上是一个空的对象数组
+ private static final Object[] EMPTY_ELEMENTDATA = {};
+
+//保存ArrayList数据的对象数组缓冲区 elementData的初始容量为10,大小会根据ArrayList容量的增长而动态的增长。
+ private transient Object[] elementData;
+//集合的长度
+ private int size;
+```
++ add方法
+```java
+/**
+ * Appends the specified element to the end of this list.
+ */
+//增加元素到集合的最后
+public boolean add(E e) {
+ensureCapacityInternal(size + 1); // Increments modCount!!
+//因为++运算符的特点 先使用后运算 这里实际上是
+//elementData[size] = e
+//size+1
+elementData[size++] = e;
+ return true;
+}
+```
++ 扩容
++ (1)检查是否需要扩容;
++ (2)如果elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则初始化容量大小为DEFAULT_CAPACITY;
++ (3)新容量是老容量的1.5倍(oldCapacity + (oldCapacity >> 1)),如果加了这么多容量发现比需要的容量还小,则以需要的容量为准;
++ (4)创建新容量的数组并把老数组拷贝到新数组;
+```java
+private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ //新的容量是在原有的容量基础上+50% 右移一位就是二分之一
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ //如果新容量小于最小容量,按照最小容量进行扩容
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ //这里是重点 调用工具类Arrays的copyOf扩容
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+```
\ No newline at end of file
diff --git a/week_01/20/HashMap.md b/week_01/20/HashMap.md
new file mode 100644
index 0000000..64e658c
--- /dev/null
+++ b/week_01/20/HashMap.md
@@ -0,0 +1,173 @@
+#### 简介
++ HashMap采用key/value存储结构,每个key对应唯一的value,查询和修改的速度都很快,能达到O(1)的平均时间复杂度。它是非线程安全的,且不保证元素存储的顺序。
++ HashMap实现了Cloneable,可以被克隆。
++ HashMap实现了Serializable,可以被序列化。
++ HashMap继承自AbstractMap,实现了Map接口,具有Map的所有功能。
+#### 存储结构
++ 
++ 数组 + 链表 + 红黑树(O(1)、O(k)、O(logk))
+
+#### 源码解析
++ 成员变量
+ ```java
+ //默认的初始容量,必须是2的幂。
+ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
+ //最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
+ static final int MAXIMUM_CAPACITY = 1 << 30;
+ //默认装载因子,默认值为0.75,如果实际元素所占容量占分配容量的75%时就要扩容了。如果填充比很大,说明利用的空间很多,但是查找的效率很低,因为链表的长度很大(当然最新版本使用了红黑树后会改进很多),HashMap本来是以空间换时间,所以填充比没必要太大。但是填充比太小又会导致空间浪费。如果关注内存,填充比可以稍大,如果主要关注查找性能,填充比可以稍小。
+ static final float _LOAD_FACTOR = 0.75f;
+
+ //一个桶的树化阈值
+ //当桶中元素个数超过这个值时,需要使用红黑树节点替换链表节点
+ //这个值必须为 8,要不然频繁转换效率也不高
+ static final int TREEIFY_THRESHOLD = 8;
+
+ //一个树的链表还原阈值
+ //当扩容时,桶中元素个数小于这个值,就会把树形的桶元素 还原(切分)为链表结构
+ //这个值应该比上面那个小,至少为 6,避免频繁转换
+ static final int UNTREEIFY_THRESHOLD = 6;
+
+ //哈希表的最小树形化容量
+ //当哈希表中的容量大于这个值时,表中的桶才能进行树形化
+ //否则桶内元素太多时会扩容,而不是树形化
+ //为了避免进行扩容、树形化选择的冲突,这个值不能小于 4 * TREEIFY_THRESHOLD
+ static final int MIN_TREEIFY_CAPACITY = 64;
+
+ //存储数据的Entry数组,长度是2的幂。
+ transient Entry[] table;
+ //
+ transient Set> entrySet;
+ //map中保存的键值对的数量
+ transient int size;
+ //需要调整大小的极限值(容量*装载因子)
+ int threshold;
+ //装载因子
+ final float loadFactor;
+ //map结构被改变的次数
+ transient volatile int modCount;
+ ```
++ 计算阀值
+ ```java
+ static final int tableSizeFor(int cap) {
+ //经过下面的 或 和位移 运算, n最终各位都是1。
+ int n = cap - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ //判断n是否越界,返回 2的n次方作为 table(哈希桶)的阈值
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+ ```
++ 扩容
+ ```java
+ final Node[] resize() {
+ //oldTab 为当前表的哈希桶
+ Node[] oldTab = table;
+ //当前哈希桶的容量 length
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ //当前的阈值
+ int oldThr = threshold;
+ //初始化新的容量和阈值为0
+ int newCap, newThr = 0;
+ //如果当前容量大于0
+ if (oldCap > 0) {
+ //如果当前容量已经到达上限
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ //则设置阈值是2的31次方-1
+ threshold = Integer.MAX_VALUE;
+ //同时返回当前的哈希桶,不再扩容
+ return oldTab;
+ }//否则新的容量为旧的容量的两倍。
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ //如果旧的容量大于等于默认初始容量16
+ //那么新的阈值也等于旧的阈值的两倍
+ newThr = oldThr << 1; // double threshold
+ }
+ //如果当前表是空的,但是有阈值。代表是初始化时指定了容量、阈值的情况
+ else if (oldThr > 0)
+ newCap = oldThr;//那么新表的容量就等于旧的阈值
+ else {
+ //如果当前表是空的,而且也没有阈值。代表是初始化时没有任何容量/阈值参数的情况
+ newCap = DEFAULT_INITIAL_CAPACITY;//此时新表的容量为默认的容量 16
+ //新的阈值为默认容量16 * 默认加载因子0.75f = 12
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ //如果新的阈值是0,对应的是 当前表是空的,但是有阈值的情况
+ float ft = (float)newCap * loadFactor;//根据新表容量 和 加载因子 求出新的阈值
+ //进行越界修复
+ newThr = (newCap < MAXIMUM_CAPACITY && ft <(float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);
+ }
+ //更新阈值
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ //根据新的容量 构建新的哈希桶
+ Node[] newTab = (Node[])new Node[newCap];
+ //更新哈希桶引用
+ table = newTab;
+ //如果以前的哈希桶中有元素
+ //下面开始将当前哈希桶中的所有节点转移到新的哈希桶中
+ if (oldTab != null) {
+ //遍历老的哈希桶
+ for (int j = 0; j < oldCap; ++j) {
+ //取出当前的节点 e
+ Node e;
+ //如果当前桶中有元素,则将链表赋值给e
+ if ((e = oldTab[j]) != null) {
+ //将原哈希桶置空以便GC
+ oldTab[j] = null;
+ //如果当前链表中就一个元素,(没有发生哈希碰撞)
+ if (e.next == null)
+ //直接将这个元素放置在新的哈希桶里。
+ //注意这里取下标 是用 哈希值 与 桶的长度-1 。 由于桶的长度是2的n次方,这么做其实是等于 一个模运算。但是效率更高
+ newTab[e.hash & (newCap - 1)] = e;
+ //如果发生过哈希碰撞 ,而且是节点数超过8个,转化成了红黑树
+ else if (e instanceof TreeNode)
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ //如果发生过哈希碰撞,节点数小于8个。则要根据链表上每个节点的哈希值,依次放入新哈希桶对应下标位置。
+ else {
+ //因为扩容是容量翻倍,所以原链表上的每个节点,现在可能存放在原来的下标,即low位,或者扩容后的下标,即high位。high位=low位+原哈希桶容量
+ //低位链表的头结点、尾节点
+ Node loHead = null, loTail = null;
+ //高位链表的头节点、尾节点
+ Node hiHead = null, hiTail = null;
+ Node next;//临时节点 存放e的下一个节点
+ do {
+ next = e.next;
+ //利用位运算代替常规运算:利用哈希值与旧的容量,可以得到哈希值去模后,是大于等于oldCap还是小于oldCap,等于0代表小于oldCap,应该存放在低位,否则存放在高位
+ if ((e.hash & oldCap) == 0) {
+ //给头尾节点指针赋值
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }//高位也是相同的逻辑
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }//循环直到链表结束
+ } while ((e = next) != null);
+ //将低位链表存放在原index处
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ //将高位链表存放在新index处
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+ }
+ ```
\ No newline at end of file
diff --git a/week_01/22/ArrayList-22.java b/week_01/22/ArrayList-22.java
new file mode 100644
index 0000000..f3d2d1e
--- /dev/null
+++ b/week_01/22/ArrayList-22.java
@@ -0,0 +1,329 @@
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.RandomAccess;
+
+/**
+ * 【源码链接】
+ * Source for java.util.ArrayList:
+ * http://developer.classpath.org/doc/java/util/ArrayList-source.html
+ *
+ * */
+
+/**
+ * 【简介】
+ * ArrayList底层使用的是数组来实现List接口,提供了所有可选的List操作并且允许null值。
+ * 元素的随机访问是常数时间O(1),在列表中间添加或者删除元素的时间复杂度是O(n)的。
+ * 每个List都有一个容量,当达到最大容量时会自动增加自身的容量。
+ * 我们可以通过ensureCapacity和trimToSize来确保容量大小,避免重新分配或浪费内存。
+ *
+ * ArrayList不是synchronized的,如果需要多线程访问,可以这样做:
+ * List list = Collections.synchronizedList(new ArrayList(...))
+ *
+ * 以下就主要方法进行解析说明:
+ * **/
+
+ public class ArrayList extends AbstractList implements List,RandomAccess,Cloneable,Serializable{
+
+ private static final long serialVersionUID = 8683452581122892189L;
+
+ //新建ArrayList的默认容量大小
+ private static final int DEFAULT_CAPACITY = 10;
+
+ //ArrayList的元素个数
+ private int size;
+
+ //存储数据的数组
+ private transient E[] data;
+
+ //根据容量大小来构建ArrayList
+ public ArrayList(int capacity){
+ if(capacity < 0){
+ throw new IllegalArgumentException();
+ }
+ data = (E[]) new Object[capacity];
+ }
+
+ //默认容量大小来构建ArrayList
+ public ArrayList(){
+ this(DEFAULT_CAPACITY);
+ }
+
+ //根据给定元素来构建ArrayList
+ public ArrayList(Collectionc){
+ this((int) (c.size() * 1.1f));
+ addAll(c);
+ }
+
+ //修改size使得等于ArrayList实际大小
+ public void trimToSize(){
+ if(size != data.length){
+ E[] newData = (E[]) new Object[size];
+ System.arraycopy(data, 0, newData, 0, size);
+ data = newData;
+ }
+ }
+
+ //如果ArrayList容量不足以存储元素,则自动扩展到length*2
+ public void ensureCapacity(int minCapacity){
+ int current = data.length;
+ if(minCapacity > current){
+ E[] newData = (E[]) new Object[Math.max(current*2, minCapacity)];
+ System.arraycopy(data, 0, newData, 0, size);
+ data = newData;
+ }
+ }
+
+ //返回List的元素个数
+ public int size(){
+ return size;
+ }
+
+ //判断List是否为空
+ public boolean isEmpty(){
+ return size == 0;
+ }
+
+ //判断element是否在ArrayList中
+ public boolean contains(Object e){
+ return indexOf(e) != -1;
+ }
+
+ //判断element在ArrayList中首次出现的最低位置索引,否则返回-1
+ public int indexOf(Object e){
+ for(int i = 0; i < size; i++){
+ if(e.equals(data[i])){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ //判断element在ArrayList中首次出现的最高位置索引,否则返回-1
+ public int lastIndexOf(Object e){
+ for(int i = size-1; i > 0; i--){
+ if(e.equals(data[i])){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ //ArrayList的浅拷贝
+ public Object clone(){
+ ArrayList clone = null;
+ try{
+ clone = (ArrayList) super.clone();
+ clone.data = (E[]) data.clone();
+ }catch(CloneNotSupportedException e){
+
+ }
+ return clone;
+ }
+
+ //返回一个独立的数组,存储ArrayList的所有元素
+ public Object[] toArray(){
+ E[] array = (E[]) new Object[size];
+ System.arraycopy(data, 0, array, 0, size);
+ return array;
+ }
+
+ //返回一个运行时传入数组类型的独立数组,存储ArrayList的所有元素
+ //如果存储数组的size太小,则扩展为目标类型T的大小
+ public T[] toArray(T[] a){
+ if(a.length < size){
+ a = (T[]) Array.newInstance(a.getClass().getComponentType(), size);
+ }else if(a.length > size){
+ a[size] = null;
+ }
+ System.arraycopy(data, 0, a, 0, size);
+ return a;
+ }
+
+ //检查索引是否在可能的元素范围内
+ private void checkBoundInclusive(int index) {
+ if(index > size){
+ throw new IndexOutOfBoundsException("Index:" + index + ",Size:" + size );
+ }
+ }
+
+ //检查索引是否在现有元素的范围内。
+ private void checkBoundExclusive(int index) {
+ if(index >= size){
+ throw new IndexOutOfBoundsException("Index:" + index + ",Size:" + size );
+ }
+ }
+
+ //检索用户提供的索引处的元素
+ public E get(int index){
+ checkBoundExclusive(index);
+ return data[index];
+ }
+
+ //给特定下标元素进行赋值,返回以前位于指定索引处的元素
+ public E set(int index, E e){
+ checkBoundExclusive(index);
+ E result = data[index];
+ data[index] = e;
+ return result;
+ }
+
+ //在ArrayList的尾部添加元素:如果已满,则size+1;
+ //modCount字段表示list结构上被修改的次数.
+ public boolean add(E e){
+ modCount++;
+ if(size == data.length){
+ ensureCapacity(size + 1);
+ }
+ data[size++] = e;
+ return true;
+ }
+
+ //根据索引下标位置添加元素:如果已满,则size+1;
+ //如果插入位置不是尾部,将index后面元素往后移动一位,再插入元素于index
+ public void add(int index , Collection extends E> c) {
+ checkBoundExclusive(index);
+ modCount++;
+ if (size == data.length) {
+ ensureCapacity(size + 1);
+ }
+ if (index != size) {
+ System.arraycopy(data, index, data, index + 1, size - index);
+ }
+ data[index] = c;
+ size++;
+ }
+
+ //根据索引下标位置移除元素
+ public E remove(int index){
+ checkBoundExclusive(index);
+ E r = data[index];
+ modCount++;
+ if(index != --size){
+ System.arraycopy(data, index, data, index + 1, size - index);
+ }
+ data[size] = null;
+ return r;
+ }
+
+ //清空ArrayList
+ public void clear(){
+ if(size > 0 ){
+ modCount++;
+ Arrays.fill(data, 0, size, null);
+ size = 0 ;
+ }
+ }
+
+ //将提供的集合中的每个元素添加到此列表
+ public boolean addAll(Collection extends E>c){
+ return addAll(size, c);
+ }
+
+ //将提供的集合中的每个元素添加到此列表index开始的位置:先将index后面元素移动csize个位置,然后插入
+ public boolean addAll(int index,Collectionc){
+ checkBoundExclusive(index);
+ Iterator itr = c.iterator();
+ int csize = c.size();
+
+ modCount++;
+ if(csize+size > data.length){
+ ensureCapacity(size + csize);
+ }
+ //移动原列表元素
+ int end = index + csize;
+ if(size > 0 && index != size){
+ System.arraycopy(data, index, data, end, size - index);
+ }
+ size += csize;
+ //添加新元素
+ for(;index < end;index++){
+ data[index] = itr.next();
+ }
+ return csize>0;
+ }
+
+ //移除在某个范围间隔的列表元素:将toIndex后面的元素往前移动(size - toIndex)位
+ protected void removeRange(int fromIndex, int toIndex){
+ int change = toIndex - fromIndex;
+ if(change > 0){
+ modCount++;
+ System.arraycopy(data, toIndex, data, fromIndex, size - toIndex);
+ size -= change;
+ }
+ else if(change < 0){
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ //从此列表中删除给定集合中包含的所有元素
+ //判断元素存在,如【a,b,c,d】中存在【b】,返回下标i=1,将【c,d】前移获得【a,c,d】
+ boolean removeAllInternal(Collection>c){
+ int i,j;
+ for(i=0;ic){
+ int i,j;
+ for(i=0;i extends AbstractSequentialList implements List,Deque,Cloneable,Serializable{
+
+ private static final long serialVersionUID = 876323262645176354L;
+
+ //LinkedList第一个元素
+ transient Entry first;
+
+ //LinkedList最后一个元素
+ transient Entry last;
+
+ //LinkedList的长度
+ transient int size = 0;
+
+ //新建内部类来表示列表中的项,包含单个元素。
+ private static final class Entry{
+ //列表元素
+ T data;
+ //后继指针
+ Entry next;
+ //前继指针
+ Entry previous;
+
+ Entry(T data){
+ this.data = data;
+ }
+ }
+
+ //获取LinkedList位置下标为n的元素,顺序or倒序
+ Entry