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/README.md b/README.md index 2cd378f..9478cf5 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,16 @@ ### 一、学习周期(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 || - +| 时间 | 内容 | 主要类| +| :------------------------------- | ------- | ----| +| 第一周 (2019/12/09-2019/12.15) | 简单集合|ArrayList、HashMap、LinkedList| +| 第二周 (2019/12/16-2019/12.22) | 原子类 |Unsafe、AtomicInteger、AtomicStampedReference、LongAdder| +| 第三周 (2019/12/23-2019/12.29) | 同步器 |Java内存模型、volatile、synchronized、AQS、ReentrantLock、Semaphore、分布式锁| +| 第四周 (2019/12/30-2020/01/05) | 并发集合 |CopyOnWriteArrayList、ConcurrentHashMap、ArrayBlockingQueue、ConcurrentLinkedQueue、DelayQueue| +| 第五周 (2020/01/06-2020/01/12) | 多线程 |Thread、ThreadLocal、ThreadPoolExecutor、Executors| +| 第六周 (2020/01/13-2020/01/19) | 待定 || +| 第七周 (2020/01/20-2020/01/26) | 待定 || +| 第八周 (2020/01/27-2020/02/02) | 待定 || ### 二、作业 @@ -35,13 +34,18 @@ -### 三、源码提交流程 -- 先将[xxx]仓库 `fork` 到自己的 GitHub 账号下。 -- 将 `fork` 后的仓库 `clone` 到本地,然后在本地新建、修改自己的源码学习笔记,**注意:** 仅允许在和自己编号对应的目录下新建或修改自己的源码学习笔记。完成后,将相关修改部分 `push` 到自己的 GitHub 远程仓库。 -- 当完成本周作业,提交 `Pull Request`申请给[xxx]仓库,Pull 作业时,必须备注自己的编号和提交第几周的作业,如`007-week 02`,是指编号为`007`的成员提交的`第二周`的源码学习笔记。 -- 源码学习笔记的命名规则:**`内容标题-编号`**,比如学号为 `007` 的成员完成`ArrayList`类后,请将源码学习笔记名保存为 `ArrayList-007 `。(内容标题自定义) -- 务必按照Pull Request的备注形式和作业文件的命名进行提交,这样方便统计。 +### 三、提交流程 +- 学习笔记或总结以pull request的形式提交 +- 遇到问题自己先尝试查阅资料,再发到群里讨论,并且在github上提一个issue记录该问题 + +### 四、阅读参考 + - 视频链接:https://v.qq.com/x/page/g3032lqhwcz.html + - 参考公众号:彤哥读源码,已完成所有JDK源码解析 + - 阅读顺序:基础类、简单集合、原子类、同步器、并发集合、多线程 + - 思维导图:上面目录列的是全部要阅读的,下方标星的是强烈建议阅读的,没标星的有时间建议多看看 + + ![jdk_source](https://gitee.com/alan-tang-tt/yuan/raw/master/死磕%20java集合系列/resource/jdk_source.png) + -ps:任何学习上的问题可以发布issure求助,其他同学有时间就帮忙看看哈。 diff --git "a/week_01/04/ArrayList \346\272\220\347\240\201\345\210\206\346\236\220.md" "b/week_01/04/ArrayList \346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..7ecd5c9 --- /dev/null +++ "b/week_01/04/ArrayList \346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,447 @@ +# ArrayList 源码分析 + + + +## 继承体系 + +- AbstractList +- List +- RandomAccess +- Cloneable +- java.io.Serializable + +> List 接口,是一种有顺序的集合,sequence (序列,顺序) +> +> index +> +> typically Duplicate elements (Unlike sets) + + + +> ArrayList Resiable-array, permits alll elements including null. +> +> roughly equivalent to Vector (synchronized) +> +> size,isEmpty,get,set,iterator,listIterator -> constant time +> +> add -> amortized(均摊) constant time +> +> other operations -> linear time (roughly) +> +> capacity >= list.size() ; capacity group automatically + + + +## 构造方法 + +1. 默认空构造 + + ​ + + ```java + /** + * Constructs an empty list with an initial capacity of ten. + * private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; + * 为什么说是10呢,可能是因为添加第一个元素时会扩展成10 + * Any empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA + * will be expanded to DEFAULT_CAPACITY when the first element is added + */ + public ArrayList() { + this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; + } + ``` + +2. 传入 initialCapacity的 构造 + + ```java + /** + * Constructs an empty list with the specified initial capacity. + * 当传入 初始容量为0的构造时,为什么和上面的空构造不用同一个 DEFAULTCAPACITY_EMPTY_ELEMENTDATA + * 这俩是为了什么设计而存在的呢? + * 在 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 上的注释写了 + * ----------------------------- + * Shared empty array instance used for default sized empty instances. We + * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when + * first element is added. + * ----------------------------- + * 为什么存在呢? + * 大致意思就是如果你构造函数里传入了0,证明你这个故意为之,就是为了减少空间的使用, + * 默认构造呢 就是会使用默认的10容量 + * @param initialCapacity the initial capacity of the list + * @throws IllegalArgumentException if the specified initial capacity + * is negative + */ + 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. 传入集合的构造 + + ```java + /** + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public ArrayList(Collection c) { + elementData = c.toArray(); + if ((size = elementData.length) != 0) { + // defend against c.toArray (incorrectly) not returning Object[] + // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652) + if (elementData.getClass() != Object[].class) + elementData = Arrays.copyOf(elementData, size, Object[].class); + } else { + // replace with empty array. + this.elementData = EMPTY_ELEMENTDATA; + } + } + ``` + + ```java + @HotSpotIntrinsicCandidate + public static T[] copyOf(U[] original, int newLength, Class newType) { + @SuppressWarnings("unchecked") + // 相同类型的元素 + T[] copy = ((Object)newType == (Object)Object[].class) + ? (T[]) new Object[newLength] + : (T[]) Array.newInstance(newType.getComponentType(), newLength); + System.arraycopy(original, 0, copy, 0, + Math.min(original.length, newLength)); + return copy; + } + ``` + + ## + +## Constant Time Methods + +1. size + + ```java + /** + * Returns the number of elements in this list. + * 返回list中的元素数量 + * @return the number of elements in this list + */ + public int size() { + return size; + } + ``` + +2. isEmpty + + ```java + /** + * Returns {@code true} if this list contains no elements. + * 如果list中不包含元素返回 true + * @return {@code true} if this list contains no elements + */ + public boolean isEmpty() { + return size == 0; + } + ``` + +3. get + + ```java + /** + * Returns the element at the specified position in this list. + * 返回list在指定位置的元素 + * @param index index of the element to return + * @return the element at the specified position in this list + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + Objects.checkIndex(index, size); + return elementData(index); + } + ``` + +4. set + + ```java + /** + * Replaces the element at the specified position in this list with + * the specified element. + * 替换指定位置元素 + * @param index index of the element to replace + * @param element element to be stored at the specified position + * @return the element previously at the specified position + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + Objects.checkIndex(index, size); + E oldValue = elementData(index); + elementData[index] = element; + return oldValue; + } + ``` + +5. iterator + + ```java + /** + * Returns an iterator over the elements in this list in proper sequence. + * 返回此list的遍历器(以正确的顺序) + *

The returned iterator is fail-fast. + * + * @return an iterator over the elements in this list in proper sequence + */ + public Iterator iterator() { + // 具体实现后续再看 + return new Itr(); + } + ``` + +6. listIterator + + ```java + /** + * Returns a list iterator over the elements in this list (in proper + * sequence), starting at the specified position in the list. + * The specified index indicates the first element that would be + * returned by an initial call to {@link ListIterator#next next}. + * An initial call to {@link ListIterator#previous previous} would + * return the element with the specified index minus one. + * + *

The returned list iterator is fail-fast. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public ListIterator listIterator(int index) { + rangeCheckForAdd(index); + return new ListItr(index); + } + ``` + + + +## 主要看 + +1. add + + ```java + /** + * Appends the specified element to the end of this list. + * 在列表中结尾增加指定元素 + * @param e element to be appended to this list + * @return {@code true} (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + // 修改次数 + 1 + modCount++; + add(e, elementData, size); + return true; + } + + /** + * 此方法使方法字节码小于35,使得在C1编译循环中区分开来 + * This helper method split out from add(E) to keep method + * bytecode size under 35 (the -XX:MaxInlineSize default value), + * which helps when add(E) is called in a C1-compiled loop. + */ + private void add(E e, Object[] elementData, int s) { + if (s == elementData.length) + // 由于元素已满,所以调用grow函数 + elementData = grow(); + elementData[s] = e; + size = s + 1; + } + + + private Object[] grow() { + return grow(size + 1); + } + /** + * 增加容量确保集合可以存储传入的 minCapacity 数量 + * Increases the capacity to ensure that it can hold at least the + * number of elements specified by the minimum capacity argument. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if minCapacity is less than zero + */ + private Object[] grow(int minCapacity) { + return elementData = Arrays.copyOf(elementData, + newCapacity(minCapacity)); + } + + + /** + * 返回 至少大于给定的 minCapacity大小,如果 1.5倍能够满足就返回。 + * 除非给定MAX_ARRAY_SIZE不然不会返回这个值 + * Returns a capacity at least as large as the given minimum capacity. + * Returns the current capacity increased by 50% if that suffices. + * Will not return a capacity greater than MAX_ARRAY_SIZE unless + * the given minimum capacity is greater than MAX_ARRAY_SIZE. + * + * @param minCapacity the desired minimum capacity + * @throws OutOfMemoryError if minCapacity is less than zero + */ + private int newCapacity(int minCapacity) { + // overflow-conscious code + int oldCapacity = elementData.length; + // 1.5倍 + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity - minCapacity <= 0) { + if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) + return Math.max(DEFAULT_CAPACITY, minCapacity); + if (minCapacity < 0) // overflow + // 溢出考虑 + throw new OutOfMemoryError(); + return minCapacity; + } + return (newCapacity - MAX_ARRAY_SIZE <= 0) + ? newCapacity + : hugeCapacity(minCapacity); + } + + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) + ? Integer.MAX_VALUE + : MAX_ARRAY_SIZE; + } + + /** + * 将元素插入list中指定位置。交换此时指定位置的现有元素,后移后置元素 + * Inserts the specified element at the specified position in this + * list. Shifts the element currently at that position (if any) and + * any subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + // 检查是否越界 + rangeCheckForAdd(index); + // 修改次数+1 + modCount++; + final int s; + Object[] elementData; + if ((s = size) == (elementData = this.elementData).length) + elementData = grow(); + // 调用系统数组拷贝 + System.arraycopy(elementData, index, + elementData, index + 1, + s - index); + elementData[index] = element; + size = s + 1; + } + + /** + * A version of rangeCheck used by add and addAll. + */ + private void rangeCheckForAdd(int index) { + if (index > size || index < 0) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + ``` + +2. addAll + + ```java + /** + * 将所有集合中元素加入此list的最后 + * Appends all of the elements in the specified collection to the end of + * this list, in the order that they are returned by the + * specified collection's Iterator. The behavior of this operation is + * undefined if the specified collection is modified while the operation + * is in progress. (This implies that the behavior of this call is + * undefined if the specified collection is this list, and this + * list is nonempty.) + * + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) { + // 转成数组 + Object[] a = c.toArray(); + // 修改数量 + 1 + modCount++; + // 增加的数量 还有空指针的风险 + int numNew = a.length; + if (numNew == 0) + return false; + Object[] elementData; + // 增加的位置 + final int s; + if (numNew > (elementData = this.elementData).length - (s = size)) + elementData = grow(s + numNew); + // 将集合c中的元素拷贝到最后 + System.arraycopy(a, 0, elementData, s, numNew); + size = s + numNew; + return true; + } + ``` + +3. remove + + ```java + /** + * 移除list中第一个出现的元素(如果存在的话),如果list不包含元素,那这个就不会改变。更正式来说就是移除低索 + * 引的元素 满足Objects.equals(o, get(i))的元素存在。 + * Removes the first occurrence of the specified element from this list, + * if it is present. If the list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * {@code i} such that + * {@code Objects.equals(o, get(i))} + * (if such an element exists). Returns {@code true} if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * @param o element to be removed from this list, if present + * @return {@code true} if this list contained the specified element + */ + public boolean remove(Object o) { + final Object[] es = elementData; + final int size = this.size; + int i = 0; + // 代码块 + found: { + // 区分为空 + if (o == null) { + for (; i < size; i++) + if (es[i] == null) + break found; + // 不为空的 + } else { + for (; i < size; i++) + if (o.equals(es[i])) + break found; + } + return false; + } + fastRemove(es, i); + return true; + } + + /** + * 基础的移除方法 跳过界限检查不返回移除的元素 + * Private remove method that skips bounds checking and does not + * return the value removed. + */ + private void fastRemove(Object[] es, int i) { + modCount++; + final int newSize; + if ((newSize = size - 1) > i) + System.arraycopy(es, i + 1, es, i, newSize - i); + es[size = newSize] = null; + } + + ``` + + \ No newline at end of file diff --git "a/week_01/04/HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/week_01/04/HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..048ec8e --- /dev/null +++ "b/week_01/04/HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,1130 @@ +# HashMap + + + +## 继承体系 + +### 继承 + +​ AbstractMap + +### 实现 + +1. Map +2. Cloneable +3. Serializable + +## 主要属性 + +```java +/** + * 默认初始容量16 + * The default initial capacity - MUST be a power of two. + */ +static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 + +/** + * 最大的容量 + * The maximum capacity, used if a higher value is implicitly specified + * by either of the constructors with arguments. + * MUST be a power of two <= 1<<30. + */ +static final int MAXIMUM_CAPACITY = 1 << 30; + +/** + * 默认加载因子 + * The load factor used when none specified in constructor. + */ +static final float DEFAULT_LOAD_FACTOR = 0.75f; + +/** + * 使用树的桶数量阈值 + * The bin count threshold for using a tree rather than list for a + * bin. Bins are converted to trees when adding an element to a + * bin with at least this many nodes. The value must be greater + * than 2 and should be at least 8 to mesh with assumptions in + * tree removal about conversion back to plain bins upon + * shrinkage. + */ +static final int TREEIFY_THRESHOLD = 8; + +/** + * 树退化成链表的阈值 + * The bin count threshold for untreeifying a (split) bin during a + * resize operation. Should be less than TREEIFY_THRESHOLD, and at + * most 6 to mesh with shrinkage detection under removal. + */ +static final int UNTREEIFY_THRESHOLD = 6; + +/** + * The smallest table capacity for which bins may be treeified. + * (Otherwise the table is resized if too many nodes in a bin.) + * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts + * between resizing and treeification thresholds. + */ +static final int MIN_TREEIFY_CAPACITY = 64; +``` + + + +```java +/** + * 桶(表),第一次使用时才会初始化,必要时扩容 + * The table, initialized on first use, and resized as + * necessary. When allocated, length is always a power of two. + * (We also tolerate length zero in some operations to allow + * bootstrapping mechanics that are currently not needed.) + */ +transient Node[] table; + +/** + * 缓存键值对的entrySet + * Holds cached entrySet(). Note that AbstractMap fields are used + * for keySet() and values(). + */ +transient Set> entrySet; + +/** + * 键值对的数量 + * The number of key-value mappings contained in this map. + */ +transient int size; + +/** + * 修改次数 + * The number of times this HashMap has been structurally modified + * Structural modifications are those that change the number of mappings in + * the HashMap or otherwise modify its internal structure (e.g., + * rehash). This field is used to make iterators on Collection-views of + * the HashMap fail-fast. (See ConcurrentModificationException). + */ +transient int modCount; + +/** + * 下一个要重新resize的大小 + * The next size value at which to resize (capacity * load factor). + * + * @serial + */ +// (The javadoc description is true upon serialization. +// Additionally, if the table array has not been allocated, this +// field holds the initial array capacity, or zero signifying +// DEFAULT_INITIAL_CAPACITY.) +int threshold; + +/** + * 加载因子 + * The load factor for the hash table. + * + * @serial + */ +final float loadFactor; +``` + +## 构造函数 + +1. 空构造 + + ```java + /** + * 默认值 16 load factor .75 + * Constructs an empty HashMap with the default initial capacity + * (16) and the default load factor (0.75). + */ + public HashMap() { + this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted + } + ``` + +2. 传入Map的构造 + + ```java + /** + * 构造足够承载目标的容量 + * Constructs a new HashMap with the same mappings as the + * specified Map. The HashMap is created with + * default load factor (0.75) and an initial capacity sufficient to + * hold the mappings in the specified Map. + * + * @param m the map whose mappings are to be placed in this map + * @throws NullPointerException if the specified map is null + */ + public HashMap(Map m) { + this.loadFactor = DEFAULT_LOAD_FACTOR; + putMapEntries(m, false); + } + + /** + * Implements Map.putAll and Map constructor. + * + * @param m the map + * @param evict false when initially constructing this map, else + * true (relayed to method afterNodeInsertion). + */ + final void putMapEntries(Map m, boolean evict) { + int s = m.size(); + if (s > 0) { + if (table == null) { // pre-size + // 除以加载因子 + 1 防止因加入全部元素而要扩容影响性能 + float ft = ((float)s / loadFactor) + 1.0F; + // 防止大于 MAXIMUM_CAPACITY + int t = ((ft < (float)MAXIMUM_CAPACITY) ? + (int)ft : MAXIMUM_CAPACITY); + // 修改threshold值 + if (t > threshold) + threshold = tableSizeFor(t); + } + else if (s > threshold) + // 查看如下 + resize(); + for (Map.Entry e : m.entrySet()) { + K key = e.getKey(); + V value = e.getValue(); + // 进行存值操作 + putVal(hash(key), key, value, false, evict); + } + } + } + ``` + + ```java + /** + * 初始化或将table双倍。如果是初始化,通过初始化容量threshold来分配, + * 然而由于我们使用了二次幂的扩容方式,元素可能存储在原索引位置,或者移动到二次幂的新表中 + * Initializes or doubles table size. If null, allocates in + * accord with initial capacity target held in field threshold. + * Otherwise, because we are using power-of-two expansion, the + * elements from each bin must either stay at same index, or move + * with a power of two offset in the new table. + * + * @return the table + */ + 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; + } + // 旧容量 >= DEFAULT_INITIAL_CAPACITY && 新容量 = 旧容量 * 2 < MAXIMUM_CAPACITY + 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) { + // gc help + 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; + } + ``` + + ```java + /** + * 实现Map.put 和 相关方法 + * 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 如果为true 不需要改变已存在的值 + * @param evict if false, the table is in creation mode. 如果为false ,表是在创建模式 + * @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; + // 容器没有初始化的情况下 需要调用resize + 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) + // 如果已经是树了,那么调用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); + // 如果插入了新数据后链表长度大于8,那么就要进行树化 + 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; + } + } + // 找到相同key的元素 + 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; + } + ``` + + ```java + /** + * Replaces all linked nodes in bin at index for given hash unless + * table is too small, in which case resizes instead. + */ + 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); + } + } + ``` + +3. + + + +## 内部类 + +### 简单看 + + + +### 仔细看 + +1. Node + + ```java + /** + * 基础hash桶节点 + * Basic hash bin node, used for most entries. (See below for + * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) + */ + 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; + } + + 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; + } + } + ``` + + + +2. TreeNode + + ```java + /** + * 树桶,继承了 LinkedHashMap.Entry,所以即可以作为常规或链式节点 + * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn + * extends Node) so can be used as extension of either regular or + * linked node. + */ + 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; + // 构造方法 + TreeNode(int hash, K key, V val, Node next) { + super(hash, key, val, next); + } + + /** + * Returns root of tree containing this node. + * 树的根 + */ + final TreeNode root() { + for (TreeNode r = this, p;;) { + if ((p = r.parent) == null) + return r; + r = p; + } + } + + /** + * 确保给的根是头结点,将Root移到最前面 + * Ensures that the given root is the first node of its bin. + */ + static void moveRootToFront(Node[] tab, TreeNode root) { + int n; + if (root != null && tab != null && (n = tab.length) > 0) { + int index = (n - 1) & root.hash; + TreeNode first = (TreeNode)tab[index]; + if (root != first) { + Node rn; + tab[index] = root; + TreeNode rp = root.prev; + if ((rn = root.next) != null) + ((TreeNode)rn).prev = rp; + if (rp != null) + rp.next = rn; + if (first != null) + first.prev = root; + root.next = first; + root.prev = null; + } + assert checkInvariants(root); + } + } + + /** + * 找到给定hash值和key的Treenode + * Finds the node starting at root p with the given hash and key. + * The kc argument caches comparableClassFor(key) upon first use + * comparing keys. + */ + final TreeNode find(int h, Object k, Class kc) { + TreeNode p = this; + do { + int ph, dir; K pk; + TreeNode pl = p.left, pr = p.right, q; + if ((ph = p.hash) > h) + p = pl; + else if (ph < h) + p = pr; + else if ((pk = p.key) == k || (k != null && k.equals(pk))) + return p; + else if (pl == null) + p = pr; + else if (pr == null) + p = pl; + else if ((kc != null || + (kc = comparableClassFor(k)) != null) && + (dir = compareComparables(kc, k, pk)) != 0) + p = (dir < 0) ? pl : pr; + else if ((q = pr.find(h, k, kc)) != null) + return q; + else + p = pl; + } while (p != null); + return null; + } + + /** + * Calls find for root node. + */ + final TreeNode getTreeNode(int h, Object k) { + return ((parent != null) ? root() : this).find(h, k, null); + } + + /** + * Tie-breaking utility for ordering insertions when equal + * hashCodes and non-comparable. We don't require a total + * order, just a consistent insertion rule to maintain + * equivalence across rebalancings. Tie-breaking further than + * necessary simplifies testing a bit. + */ + static int tieBreakOrder(Object a, Object b) { + int d; + if (a == null || b == null || + (d = a.getClass().getName(). + compareTo(b.getClass().getName())) == 0) + d = (System.identityHashCode(a) <= System.identityHashCode(b) ? + -1 : 1); + return d; + } + + /** + * Forms tree of the nodes linked from this node. + */ + final void treeify(Node[] tab) { + TreeNode root = null; + for (TreeNode x = this, next; x != null; x = next) { + next = (TreeNode)x.next; + x.left = x.right = null; + if (root == null) { + x.parent = null; + x.red = false; + root = x; + } + else { + K k = x.key; + int h = x.hash; + Class kc = null; + for (TreeNode p = root;;) { + int dir, ph; + K pk = p.key; + if ((ph = p.hash) > h) + dir = -1; + else if (ph < h) + dir = 1; + else if ((kc == null && + (kc = comparableClassFor(k)) == null) || + (dir = compareComparables(kc, k, pk)) == 0) + dir = tieBreakOrder(k, pk); + + TreeNode xp = p; + if ((p = (dir <= 0) ? p.left : p.right) == null) { + x.parent = xp; + if (dir <= 0) + xp.left = x; + else + xp.right = x; + root = balanceInsertion(root, x); + break; + } + } + } + } + moveRootToFront(tab, root); + } + + /** + * Returns a list of non-TreeNodes replacing those linked from + * this node. + */ + final Node untreeify(HashMap map) { + Node hd = null, tl = null; + for (Node q = this; q != null; q = q.next) { + Node p = map.replacementNode(q, null); + if (tl == null) + hd = p; + else + tl.next = p; + tl = p; + } + return hd; + } + + /** + * Tree version of putVal. + */ + final TreeNode putTreeVal(HashMap map, Node[] tab, + int h, K k, V v) { + Class kc = null; + boolean searched = false; + TreeNode root = (parent != null) ? root() : this; + for (TreeNode p = root;;) { + int dir, ph; K pk; + if ((ph = p.hash) > h) + dir = -1; + else if (ph < h) + dir = 1; + else if ((pk = p.key) == k || (k != null && k.equals(pk))) + return p; + else if ((kc == null && + (kc = comparableClassFor(k)) == null) || + (dir = compareComparables(kc, k, pk)) == 0) { + if (!searched) { + TreeNode q, ch; + searched = true; + if (((ch = p.left) != null && + (q = ch.find(h, k, kc)) != null) || + ((ch = p.right) != null && + (q = ch.find(h, k, kc)) != null)) + return q; + } + dir = tieBreakOrder(k, pk); + } + + TreeNode xp = p; + if ((p = (dir <= 0) ? p.left : p.right) == null) { + Node xpn = xp.next; + TreeNode x = map.newTreeNode(h, k, v, xpn); + if (dir <= 0) + xp.left = x; + else + xp.right = x; + xp.next = x; + x.parent = x.prev = xp; + if (xpn != null) + ((TreeNode)xpn).prev = x; + moveRootToFront(tab, balanceInsertion(root, x)); + return null; + } + } + } + + /** + * Removes the given node, that must be present before this call. + * This is messier than typical red-black deletion code because we + * cannot swap the contents of an interior node with a leaf + * successor that is pinned by "next" pointers that are accessible + * independently during traversal. So instead we swap the tree + * linkages. If the current tree appears to have too few nodes, + * the bin is converted back to a plain bin. (The test triggers + * somewhere between 2 and 6 nodes, depending on tree structure). + */ + final void removeTreeNode(HashMap map, Node[] tab, + boolean movable) { + int n; + if (tab == null || (n = tab.length) == 0) + return; + int index = (n - 1) & hash; + TreeNode first = (TreeNode)tab[index], root = first, rl; + TreeNode succ = (TreeNode)next, pred = prev; + if (pred == null) + tab[index] = first = succ; + else + pred.next = succ; + if (succ != null) + succ.prev = pred; + if (first == null) + return; + if (root.parent != null) + root = root.root(); + if (root == null + || (movable + && (root.right == null + || (rl = root.left) == null + || rl.left == null))) { + tab[index] = first.untreeify(map); // too small + return; + } + TreeNode p = this, pl = left, pr = right, replacement; + if (pl != null && pr != null) { + TreeNode s = pr, sl; + while ((sl = s.left) != null) // find successor + s = sl; + boolean c = s.red; s.red = p.red; p.red = c; // swap colors + TreeNode sr = s.right; + TreeNode pp = p.parent; + if (s == pr) { // p was s's direct parent + p.parent = s; + s.right = p; + } + else { + TreeNode sp = s.parent; + if ((p.parent = sp) != null) { + if (s == sp.left) + sp.left = p; + else + sp.right = p; + } + if ((s.right = pr) != null) + pr.parent = s; + } + p.left = null; + if ((p.right = sr) != null) + sr.parent = p; + if ((s.left = pl) != null) + pl.parent = s; + if ((s.parent = pp) == null) + root = s; + else if (p == pp.left) + pp.left = s; + else + pp.right = s; + if (sr != null) + replacement = sr; + else + replacement = p; + } + else if (pl != null) + replacement = pl; + else if (pr != null) + replacement = pr; + else + replacement = p; + if (replacement != p) { + TreeNode pp = replacement.parent = p.parent; + if (pp == null) + root = replacement; + else if (p == pp.left) + pp.left = replacement; + else + pp.right = replacement; + p.left = p.right = p.parent = null; + } + + TreeNode r = p.red ? root : balanceDeletion(root, replacement); + + if (replacement == p) { // detach + TreeNode pp = p.parent; + p.parent = null; + if (pp != null) { + if (p == pp.left) + pp.left = null; + else if (p == pp.right) + pp.right = null; + } + } + if (movable) + moveRootToFront(tab, r); + } + + /** + * Splits nodes in a tree bin into lower and upper tree bins, + * or untreeifies if now too small. Called only from resize; + * see above discussion about split bits and indices. + * + * @param map the map + * @param tab the table for recording bin heads + * @param index the index of the table being split + * @param bit the bit of hash to split on + */ + final void split(HashMap map, Node[] tab, int index, int bit) { + TreeNode b = this; + // Relink into lo and hi lists, preserving order + TreeNode loHead = null, loTail = null; + TreeNode hiHead = null, hiTail = null; + int lc = 0, hc = 0; + for (TreeNode e = b, next; e != null; e = next) { + next = (TreeNode)e.next; + e.next = null; + if ((e.hash & bit) == 0) { + if ((e.prev = loTail) == null) + loHead = e; + else + loTail.next = e; + loTail = e; + ++lc; + } + else { + if ((e.prev = hiTail) == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + ++hc; + } + } + + if (loHead != null) { + if (lc <= UNTREEIFY_THRESHOLD) + tab[index] = loHead.untreeify(map); + else { + tab[index] = loHead; + if (hiHead != null) // (else is already treeified) + loHead.treeify(tab); + } + } + if (hiHead != null) { + if (hc <= UNTREEIFY_THRESHOLD) + tab[index + bit] = hiHead.untreeify(map); + else { + tab[index + bit] = hiHead; + if (loHead != null) + hiHead.treeify(tab); + } + } + } + + /* ------------------------------------------------------------ */ + // Red-black tree methods, all adapted from CLR + // 左旋 + static TreeNode rotateLeft(TreeNode root, + TreeNode p) { + TreeNode r, pp, rl; + if (p != null && (r = p.right) != null) { + if ((rl = p.right = r.left) != null) + rl.parent = p; + if ((pp = r.parent = p.parent) == null) + (root = r).red = false; + else if (pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + return root; + } + // 右旋 + static TreeNode rotateRight(TreeNode root, + TreeNode p) { + TreeNode l, pp, lr; + if (p != null && (l = p.left) != null) { + if ((lr = p.left = l.right) != null) + lr.parent = p; + if ((pp = l.parent = p.parent) == null) + (root = l).red = false; + else if (pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + return root; + } + // 增加平衡 + static TreeNode balanceInsertion(TreeNode root, + TreeNode x) { + x.red = true; + for (TreeNode xp, xpp, xppl, xppr;;) { + if ((xp = x.parent) == null) { + x.red = false; + return x; + } + else if (!xp.red || (xpp = xp.parent) == null) + return root; + if (xp == (xppl = xpp.left)) { + if ((xppr = xpp.right) != null && xppr.red) { + xppr.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.right) { + root = rotateLeft(root, x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + root = rotateRight(root, xpp); + } + } + } + } + else { + if (xppl != null && xppl.red) { + xppl.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if (x == xp.left) { + root = rotateRight(root, x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if (xp != null) { + xp.red = false; + if (xpp != null) { + xpp.red = true; + root = rotateLeft(root, xpp); + } + } + } + } + } + } + // 删除平衡 + static TreeNode balanceDeletion(TreeNode root, + TreeNode x) { + for (TreeNode xp, xpl, xpr;;) { + if (x == null || x == root) + return root; + else if ((xp = x.parent) == null) { + x.red = false; + return x; + } + else if (x.red) { + x.red = false; + return root; + } + else if ((xpl = xp.left) == x) { + if ((xpr = xp.right) != null && xpr.red) { + xpr.red = false; + xp.red = true; + root = rotateLeft(root, xp); + xpr = (xp = x.parent) == null ? null : xp.right; + } + if (xpr == null) + x = xp; + else { + TreeNode sl = xpr.left, sr = xpr.right; + if ((sr == null || !sr.red) && + (sl == null || !sl.red)) { + xpr.red = true; + x = xp; + } + else { + if (sr == null || !sr.red) { + if (sl != null) + sl.red = false; + xpr.red = true; + root = rotateRight(root, xpr); + xpr = (xp = x.parent) == null ? + null : xp.right; + } + if (xpr != null) { + xpr.red = (xp == null) ? false : xp.red; + if ((sr = xpr.right) != null) + sr.red = false; + } + if (xp != null) { + xp.red = false; + root = rotateLeft(root, xp); + } + x = root; + } + } + } + else { // symmetric + if (xpl != null && xpl.red) { + xpl.red = false; + xp.red = true; + root = rotateRight(root, xp); + xpl = (xp = x.parent) == null ? null : xp.left; + } + if (xpl == null) + x = xp; + else { + TreeNode sl = xpl.left, sr = xpl.right; + if ((sl == null || !sl.red) && + (sr == null || !sr.red)) { + xpl.red = true; + x = xp; + } + else { + if (sl == null || !sl.red) { + if (sr != null) + sr.red = false; + xpl.red = true; + root = rotateLeft(root, xpl); + xpl = (xp = x.parent) == null ? + null : xp.left; + } + if (xpl != null) { + xpl.red = (xp == null) ? false : xp.red; + if ((sl = xpl.left) != null) + sl.red = false; + } + if (xp != null) { + xp.red = false; + root = rotateRight(root, xp); + } + x = root; + } + } + } + } + } + + /** + * Recursive invariant check + */ + static boolean checkInvariants(TreeNode t) { + TreeNode tp = t.parent, tl = t.left, tr = t.right, + tb = t.prev, tn = (TreeNode)t.next; + if (tb != null && tb.next != t) + return false; + if (tn != null && tn.prev != t) + return false; + if (tp != null && t != tp.left && t != tp.right) + return false; + if (tl != null && (tl.parent != t || tl.hash > t.hash)) + return false; + if (tr != null && (tr.parent != t || tr.hash < t.hash)) + return false; + if (t.red && tl != null && tl.red && tr != null && tr.red) + return false; + if (tl != null && !checkInvariants(tl)) + return false; + if (tr != null && !checkInvariants(tr)) + return false; + return true; + } + } + ``` + + + +## 主要方法 + +### 简单看 + +```java +/** + * 返回指定key映射的值,如果不存在此Key 就返回null + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code (key==null ? k==null : + * key.equals(k))}, then this method returns {@code v}; otherwise + * it returns {@code null}. (There can be at most one such mapping.) + * + *

A return value of {@code null} does not necessarily + * indicate that the map contains no mapping for the key; it's also + * possible that the map explicitly maps the key to {@code null}. + * The {@link #containsKey containsKey} operation may be used to + * distinguish these two cases. + * + * @see #put(Object, Object) + */ +public V get(Object key) { + Node e; + return (e = getNode(hash(key), key)) == null ? null : e.value; +} + +/** + * Implements Map.get and related methods. + * + * @param hash hash for key + * @param key the key + * @return the node, or null if none + */ +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) { + // 为什么要check头节点 因为 map一开始并不做初始化? + 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; +} +``` + + + +```java +/** + * 在这个map中将键值关联,如果map之前存在此key,老的值会被替代 + * Associates the specified value with the specified key in this map. + * If the map previously contained a mapping for the key, the old + * value is replaced. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or + * null if there was no mapping for key. + * (A null return can also indicate that the map + * previously associated null with key.) + */ +public V put(K key, V value) { + // 此方法上面有分析 + return putVal(hash(key), key, value, false, true); +} +``` + +### 仔细读 + diff --git "a/week_01/04/LinkedList\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/week_01/04/LinkedList\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..c0164af --- /dev/null +++ "b/week_01/04/LinkedList\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,401 @@ +# LinkedList + +> ``` +> * Doubly-linked list implementation of the {@code List} and {@code Deque} +> * interfaces. Implements all optional list operations, and permits all +> * elements (including {@code null}). +> ``` +> +> 双向链表实现了List接口和Deque,实现了所有的list操作,允许包含空的所有元素。 +> +> ** not synchronized ** -> Collections#synchronizedList 方法调用 +> +> 实现了 List Deque + + + + + +## 主要属性 + +```java +// 元素的个数 +transient int size = 0; + +/** + * 指向第一个节点 + * Pointer to first node. + * 什么意思? + * Invariant: (first == null && last == null) || + * (first.prev == null && first.item != null) + */ +transient Node first; + +/** + * 指向最后一个节点 + * Pointer to last node. + * Invariant: (first == null && last == null) || + * (last.next == null && last.item != null) + */ +transient Node last; +``` + + + +## 构造方法 + +1. LinkedList() + + ```java + /** + * 构造空的集合 + * Constructs an empty list. + */ + public LinkedList() { + } + ``` + +2. LinkedList(Collection c) + + ```java + /** + * 构造一个list集合包含传入的集合的所有元素,他们的顺序按照 集合c 所iterator的返回顺序 + * Constructs a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection whose elements are to be placed into this list + * @throws NullPointerException if the specified collection is null + */ + public LinkedList(Collection c) { + this(); + // 将集合加到此list中 后续再看 + addAll(c); + } + ``` + + + + + + + +## 方法 + +### 简单看 + +1. 查 + + ```java + /** + * 返回第一个元素 O1 + * Returns the first element in this list. + * + * @return the first element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return f.item; + } + + /** + * 返回最后一个元素 O1 + * Returns the last element in this list. + * + * @return the last element in this list + * @throws NoSuchElementException if this list is empty + */ + public E getLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return l.item; + } + + ``` + +2. 删 + + ```java + /** + * 移出并返回第一元素 + * Removes and returns the first element from this list. + * + * @return the first element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeFirst() { + final Node f = first; + if (f == null) + throw new NoSuchElementException(); + return unlinkFirst(f); + } + + /** + * 移出并返回最后一个元素 + * Removes and returns the last element from this list. + * + * @return the last element from this list + * @throws NoSuchElementException if this list is empty + */ + public E removeLast() { + final Node l = last; + if (l == null) + throw new NoSuchElementException(); + return unlinkLast(l); + } + ``` + + + +3. + + + +### 细看 + +1. addAll + + ```java + /** + * 将传入的集合c全部加入到此集合list中,他们的顺序是按照collection c iterator的顺序。 + * 当传入的collection c 在操作在进行中时,被修改了。此操作的表现 undefined + * Appends all of the elements in the specified collection to the end of + * this list, in the order that they are returned by the specified + * collection's iterator. The behavior of this operation is undefined if + * the specified collection is modified while the operation is in + * progress. (Note that this will occur if the specified collection is + * this list, and it's nonempty.) + * + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(Collection c) { + return addAll(size, c); + } + + /** + * 将传入的集合的所有元素加入到此list中(从指定位置开始),将当前位置交换,右边的往后移。 + * 新增的元素会以集合的 iterator的函数顺序 + * Inserts all of the elements in the specified collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in the list in the order that they are returned by the + * specified collection's iterator. + * + * @param index index at which to insert the first element + * from the specified collection + * @param c collection containing elements to be added to this list + * @return {@code true} if this list changed as a result of the call + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws NullPointerException if the specified collection is null + */ + public boolean addAll(int index, Collection c) { + // 检查索引是否越界 + checkPositionIndex(index); + + Object[] a = c.toArray(); + int numNew = a.length; + if (numNew == 0) + return false; + // pred 要加入的前一个,succ 要插入的元素的位置 + Node pred, succ; + if (index == size) { + succ = null; + pred = last; + } else { + succ = node(index); + pred = succ.prev; + } + // 遍历要加入集合的元素数组 + for (Object o : a) { + // 将元素强转成对应的类型 + @SuppressWarnings("unchecked") E e = (E) o; + Node newNode = new Node<>(pred, e, null); + if (pred == null) + // 前者为空 将此作为链首 + first = newNode; + else + // 否则就将前者的后节点指向他 + pred.next = newNode; + // pred 使用newNode + pred = newNode; + } + + if (succ == null) { + //  插入的位置为空的情况 证明最后一个就是加进去的最后一个即pred + last = pred; + } else { + // 不为空仅需将俩相连 + pred.next = succ; + succ.prev = pred; + } + + size += numNew; + modCount++; + return true; + } + + /** + * 返回在指定索引位置的节点 + * Returns the (non-null) Node at the specified element 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; + } + } + ``` + +2. add + + ```java + /** + * 插入具体元素在指定的位置 移动此位置的元素和右边的元素 + * Inserts the specified element at the specified position in this list. + * Shifts the element currently at that position (if any) and any + * subsequent elements to the right (adds one to their indices). + * + * @param index index at which the specified element is to be inserted + * @param element element to be inserted + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + checkPositionIndex(index); + + if (index == size) + linkLast(element); + else + linkBefore(element, node(index)); + } + ``` + +3. unlinkFirst, unlinkLast + + ```java + /** + * 移出并返回非空的第一节点f,删除头 + * Unlinks non-null first node f. + */ + 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 + // 将头结点 prev至成空 + next.prev = null; + size--; + modCount++; + return element; + } + + /** + * 移出并返回非空的最后一个节点,删除尾 + * Unlinks non-null last node l. + */ + 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; + } + ``` + +4. linkFirst, linkLast + + ```java + /** + * 将元素作为头结点连接 + * Links e as first element. + */ + private void linkFirst(E e) { + // 头结点取出作为 新增节点的next节点 + final Node f = first; + final Node newNode = new Node<>(null, e, f); + // 将头重置成新节点 + first = newNode; + if (f == null) + // 如果头结点为空 那么尾节点也是这个新节点 + last = newNode; + else + // f的prev指向现有节点 + f.prev = newNode; + size++; + modCount++; + } + + /** + * Links e as last element. + */ + 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++; + } + ``` + +5. linkBefore + + ```java + /** + * 将元素e 插入到succ之前 + * Inserts element e before non-null Node succ. + */ + void linkBefore(E e, Node succ) { + // assert succ != null; + // succ之前的位置 + final Node pred = succ.prev; + // pred -> newNode(e) -> succ + final Node newNode = new Node<>(pred, e, succ); + succ.prev = newNode; + if (pred == null) + // 之前的最后一个节点为空 证明这条链无数据 + first = newNode; + else + pred.next = newNode; + size++; + modCount++; + } + ``` + 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 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 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