
diff --git a/docs/java/Multithread/Atomic.md b/docs/java/Multithread/Atomic.md
index 785ac34ec33..1afb5f56511 100644
--- a/docs/java/Multithread/Atomic.md
+++ b/docs/java/Multithread/Atomic.md
@@ -36,7 +36,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是
**引用类型**
- AtomicReference:引用类型原子类
-- AtomicStampedRerence:原子更新引用类型里的字段原子类
+- AtomicStampedReference:原子更新引用类型里的字段原子类
- AtomicMarkableReference :原子更新带有标记位的引用类型
**对象的属性修改类型**
@@ -44,6 +44,82 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是
- AtomicIntegerFieldUpdater:原子更新整型字段的更新器
- AtomicLongFieldUpdater:原子更新长整型字段的更新器
- AtomicStampedReference :原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
+- AtomicMarkableReference:原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来,也可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
+
+**CAS ABA 问题**
+- 描述: 第一个线程取到了变量 x 的值 A,然后巴拉巴拉干别的事,总之就是只拿到了变量 x 的值 A。这段时间内第二个线程也取到了变量 x 的值 A,然后把变量 x 的值改为 B,然后巴拉巴拉干别的事,最后又把变量 x 的值变为 A (相当于还原了)。在这之后第一个线程终于进行了变量 x 的操作,但是此时变量 x 的值还是 A,所以 compareAndSet 操作是成功。
+- 例子描述(可能不太合适,但好理解): 年初,现金为零,然后通过正常劳动赚了三百万,之后正常消费了(比如买房子)三百万。年末,虽然现金零收入(可能变成其他形式了),但是赚了钱是事实,还是得交税的!
+- 代码例子(以``` AtomicInteger ```为例)
+
+```java
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AtomicIntegerDefectDemo {
+ public static void main(String[] args) {
+ defectOfABA();
+ }
+
+ static void defectOfABA() {
+ final AtomicInteger atomicInteger = new AtomicInteger(1);
+
+ Thread coreThread = new Thread(
+ () -> {
+ final int currentValue = atomicInteger.get();
+ System.out.println(Thread.currentThread().getName() + " ------ currentValue=" + currentValue);
+
+ // 这段目的:模拟处理其他业务花费的时间
+ try {
+ Thread.sleep(300);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ boolean casResult = atomicInteger.compareAndSet(1, 2);
+ System.out.println(Thread.currentThread().getName()
+ + " ------ currentValue=" + currentValue
+ + ", finalValue=" + atomicInteger.get()
+ + ", compareAndSet Result=" + casResult);
+ }
+ );
+ coreThread.start();
+
+ // 这段目的:为了让 coreThread 线程先跑起来
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ Thread amateurThread = new Thread(
+ () -> {
+ int currentValue = atomicInteger.get();
+ boolean casResult = atomicInteger.compareAndSet(1, 2);
+ System.out.println(Thread.currentThread().getName()
+ + " ------ currentValue=" + currentValue
+ + ", finalValue=" + atomicInteger.get()
+ + ", compareAndSet Result=" + casResult);
+
+ currentValue = atomicInteger.get();
+ casResult = atomicInteger.compareAndSet(2, 1);
+ System.out.println(Thread.currentThread().getName()
+ + " ------ currentValue=" + currentValue
+ + ", finalValue=" + atomicInteger.get()
+ + ", compareAndSet Result=" + casResult);
+ }
+ );
+ amateurThread.start();
+ }
+}
+```
+
+输出内容如下:
+
+```
+Thread-0 ------ currentValue=1
+Thread-1 ------ currentValue=1, finalValue=2, compareAndSet Result=true
+Thread-1 ------ currentValue=2, finalValue=1, compareAndSet Result=true
+Thread-0 ------ currentValue=1, finalValue=2, compareAndSet Result=true
+```
下面我们来详细介绍一下这些原子类。
@@ -210,7 +286,7 @@ public class AtomicIntegerArrayTest {
基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。
- AtomicReference:引用类型原子类
-- AtomicStampedRerence:原子更新引用类型里的字段原子类
+- AtomicStampedReference:原子更新引用类型里的字段原子类
- AtomicMarkableReference :原子更新带有标记位的引用类型
上面三个类提供的方法几乎相同,所以我们这里以 AtomicReference 为例子来介绍。
@@ -268,7 +344,121 @@ class Person {
Daisy
20
```
+#### 4.3 AtomicStampedReference 类使用示例
+
+```java
+import java.util.concurrent.atomic.AtomicStampedReference;
+
+public class AtomicStampedReferenceDemo {
+ public static void main(String[] args) {
+ // 实例化、取当前值和 stamp 值
+ final Integer initialRef = 0, initialStamp = 0;
+ final AtomicStampedReference
asr = new AtomicStampedReference<>(initialRef, initialStamp);
+ System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());
+
+ // compare and set
+ final Integer newReference = 666, newStamp = 999;
+ final boolean casResult = asr.compareAndSet(initialRef, newReference, initialStamp, newStamp);
+ System.out.println("currentValue=" + asr.getReference()
+ + ", currentStamp=" + asr.getStamp()
+ + ", casResult=" + casResult);
+
+ // 获取当前的值和当前的 stamp 值
+ int[] arr = new int[1];
+ final Integer currentValue = asr.get(arr);
+ final int currentStamp = arr[0];
+ System.out.println("currentValue=" + currentValue + ", currentStamp=" + currentStamp);
+
+ // 单独设置 stamp 值
+ final boolean attemptStampResult = asr.attemptStamp(newReference, 88);
+ System.out.println("currentValue=" + asr.getReference()
+ + ", currentStamp=" + asr.getStamp()
+ + ", attemptStampResult=" + attemptStampResult);
+
+ // 重新设置当前值和 stamp 值
+ asr.set(initialRef, initialStamp);
+ System.out.println("currentValue=" + asr.getReference() + ", currentStamp=" + asr.getStamp());
+
+ // [不推荐使用,除非搞清楚注释的意思了] weak compare and set
+ // 困惑!weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191]
+ // 但是注释上写着 "May fail spuriously and does not provide ordering guarantees,
+ // so is only rarely an appropriate alternative to compareAndSet."
+ // todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发
+ final boolean wCasResult = asr.weakCompareAndSet(initialRef, newReference, initialStamp, newStamp);
+ System.out.println("currentValue=" + asr.getReference()
+ + ", currentStamp=" + asr.getStamp()
+ + ", wCasResult=" + wCasResult);
+ }
+}
+```
+输出结果如下:
+```
+currentValue=0, currentStamp=0
+currentValue=666, currentStamp=999, casResult=true
+currentValue=666, currentStamp=999
+currentValue=666, currentStamp=88, attemptStampResult=true
+currentValue=0, currentStamp=0
+currentValue=666, currentStamp=999, wCasResult=true
+```
+
+#### 4.4 AtomicMarkableReference 类使用示例
+
+``` java
+import java.util.concurrent.atomic.AtomicMarkableReference;
+
+public class AtomicMarkableReferenceDemo {
+ public static void main(String[] args) {
+ // 实例化、取当前值和 mark 值
+ final Boolean initialRef = null, initialMark = false;
+ final AtomicMarkableReference amr = new AtomicMarkableReference<>(initialRef, initialMark);
+ System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
+
+ // compare and set
+ final Boolean newReference1 = true, newMark1 = true;
+ final boolean casResult = amr.compareAndSet(initialRef, newReference1, initialMark, newMark1);
+ System.out.println("currentValue=" + amr.getReference()
+ + ", currentMark=" + amr.isMarked()
+ + ", casResult=" + casResult);
+
+ // 获取当前的值和当前的 mark 值
+ boolean[] arr = new boolean[1];
+ final Boolean currentValue = amr.get(arr);
+ final boolean currentMark = arr[0];
+ System.out.println("currentValue=" + currentValue + ", currentMark=" + currentMark);
+
+ // 单独设置 mark 值
+ final boolean attemptMarkResult = amr.attemptMark(newReference1, false);
+ System.out.println("currentValue=" + amr.getReference()
+ + ", currentMark=" + amr.isMarked()
+ + ", attemptMarkResult=" + attemptMarkResult);
+
+ // 重新设置当前值和 mark 值
+ amr.set(initialRef, initialMark);
+ System.out.println("currentValue=" + amr.getReference() + ", currentMark=" + amr.isMarked());
+
+ // [不推荐使用,除非搞清楚注释的意思了] weak compare and set
+ // 困惑!weakCompareAndSet 这个方法最终还是调用 compareAndSet 方法。[版本: jdk-8u191]
+ // 但是注释上写着 "May fail spuriously and does not provide ordering guarantees,
+ // so is only rarely an appropriate alternative to compareAndSet."
+ // todo 感觉有可能是 jvm 通过方法名在 native 方法里面做了转发
+ final boolean wCasResult = amr.weakCompareAndSet(initialRef, newReference1, initialMark, newMark1);
+ System.out.println("currentValue=" + amr.getReference()
+ + ", currentMark=" + amr.isMarked()
+ + ", wCasResult=" + wCasResult);
+ }
+}
+```
+
+输出结果如下:
+```
+currentValue=null, currentMark=false
+currentValue=true, currentMark=true, casResult=true
+currentValue=true, currentMark=true
+currentValue=true, currentMark=false, attemptMarkResult=true
+currentValue=null, currentMark=false
+currentValue=true, currentMark=true, wCasResult=true
+```
### 5 对象的属性修改类型原子类
diff --git a/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md b/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md
index 21fe5034d70..b291dc0aa1a 100644
--- a/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md
+++ b/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md
@@ -144,7 +144,7 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、
锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。
-关于这几种优化的详细信息可以查看:[synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484539&idx=1&sn=3500cdcd5188bdc253fb19a1bfa805e6&chksm=fd98521acaefdb0c5167247a1fa903a1a53bb4e050b558da574f894f9feda5378ec9d0fa1ac7&token=1604028915&lang=zh_CN#rd)
+关于这几种优化的详细信息可以查看笔主的这篇文章:
### 1.5. 谈谈 synchronized和ReentrantLock 的区别
@@ -429,7 +429,7 @@ Atomic 翻译成中文是原子的意思。在化学上,我们知道原子是
**引用类型**
- AtomicReference:引用类型原子类
-- AtomicStampedRerence:原子更新引用类型里的字段原子类
+- AtomicStampedReference:原子更新引用类型里的字段原子类
- AtomicMarkableReference :原子更新带有标记位的引用类型
**对象的属性修改类型**
@@ -612,4 +612,4 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true
- 《Java并发编程的艺术》
- http://www.cnblogs.com/waterystone/p/4920797.html
- https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
--
\ No newline at end of file
+-
diff --git a/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md b/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md
index f013fba71ef..2d1abf2116a 100644
--- a/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md
+++ b/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md
@@ -168,7 +168,7 @@ Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
-
+
下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》):
diff --git a/docs/java/synchronized.md b/docs/java/Multithread/synchronized.md
similarity index 100%
rename from docs/java/synchronized.md
rename to docs/java/Multithread/synchronized.md
diff --git a/docs/java/What's New in JDK8/Java8Tutorial.md b/docs/java/What's New in JDK8/Java8Tutorial.md
index cee4d4e4f0a..8bf59a6a686 100644
--- a/docs/java/What's New in JDK8/Java8Tutorial.md
+++ b/docs/java/What's New in JDK8/Java8Tutorial.md
@@ -442,15 +442,15 @@ optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
首先看看Stream是怎么用,首先创建实例代码的用到的数据List:
```java
-List stringCollection = new ArrayList<>();
-stringCollection.add("ddd2");
-stringCollection.add("aaa2");
-stringCollection.add("bbb1");
-stringCollection.add("aaa1");
-stringCollection.add("bbb3");
-stringCollection.add("ccc");
-stringCollection.add("bbb2");
-stringCollection.add("ddd1");
+List stringList = new ArrayList<>();
+stringList.add("ddd2");
+stringList.add("aaa2");
+stringList.add("bbb1");
+stringList.add("aaa1");
+stringList.add("bbb3");
+stringList.add("ccc");
+stringList.add("bbb2");
+stringList.add("ddd1");
```
Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:
@@ -918,7 +918,7 @@ System.out.println(hints2.length); // 2
-## Whete to go from here?
+## Where to go from here?
关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如`Arrays.parallelSort`, `StampedLock`和`CompletableFuture`等等。
diff --git a/docs/java/ArrayList-Grow.md b/docs/java/collection/ArrayList-Grow.md
similarity index 100%
rename from docs/java/ArrayList-Grow.md
rename to docs/java/collection/ArrayList-Grow.md
diff --git a/docs/java/ArrayList.md b/docs/java/collection/ArrayList.md
similarity index 100%
rename from docs/java/ArrayList.md
rename to docs/java/collection/ArrayList.md
diff --git a/docs/java/HashMap.md b/docs/java/collection/HashMap.md
similarity index 100%
rename from docs/java/HashMap.md
rename to docs/java/collection/HashMap.md
diff --git "a/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md" "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md"
new file mode 100644
index 00000000000..98892078ef9
--- /dev/null
+++ "b/docs/java/collection/Java\351\233\206\345\220\210\346\241\206\346\236\266\345\270\270\350\247\201\351\235\242\350\257\225\351\242\230.md"
@@ -0,0 +1,439 @@
+
+
+- [剖析面试最常见问题之Java基础知识](#剖析面试最常见问题之java基础知识)
+ - [说说List,Set,Map三者的区别?](#说说listsetmap三者的区别)
+ - [Arraylist 与 LinkedList 区别?](#arraylist-与-linkedlist-区别)
+ - [**补充内容:RandomAccess接口**](#补充内容randomaccess接口)
+ - [补充内容:双向链表和双向循环链表](#补充内容双向链表和双向循环链表)
+ - [ArrayList 与 Vector 区别呢?为什么要用Arraylist取代Vector呢?](#arraylist-与-vector-区别呢为什么要用arraylist取代vector呢)
+ - [说一说 ArrayList 的扩容机制吧](#说一说-arraylist-的扩容机制吧)
+ - [HashMap 和 Hashtable 的区别](#hashmap-和-hashtable-的区别)
+ - [HashMap 和 HashSet区别](#hashmap-和-hashset区别)
+ - [HashSet如何检查重复](#hashset如何检查重复)
+ - [HashMap的底层实现](#hashmap的底层实现)
+ - [JDK1.8之前](#jdk18之前)
+ - [JDK1.8之后](#jdk18之后)
+ - [HashMap 的长度为什么是2的幂次方](#hashmap-的长度为什么是2的幂次方)
+ - [HashMap 多线程操作导致死循环问题](#hashmap-多线程操作导致死循环问题)
+ - [ConcurrentHashMap 和 Hashtable 的区别](#concurrenthashmap-和-hashtable-的区别)
+ - [ConcurrentHashMap线程安全的具体实现方式/底层具体实现](#concurrenthashmap线程安全的具体实现方式底层具体实现)
+ - [JDK1.7(上面有示意图)](#jdk17上面有示意图)
+ - [JDK1.8 (上面有示意图)](#jdk18-上面有示意图)
+ - [comparable 和 Comparator的区别](#comparable-和-comparator的区别)
+ - [Comparator定制排序](#comparator定制排序)
+ - [重写compareTo方法实现按年龄来排序](#重写compareto方法实现按年龄来排序)
+ - [集合框架底层数据结构总结](#集合框架底层数据结构总结)
+ - [Collection](#collection)
+ - [1. List](#1-list)
+ - [2. Set](#2-set)
+ - [Map](#map)
+ - [如何选用集合?](#如何选用集合)
+ - [集合的选用](#集合的选用)
+
+
+
+# 剖析面试最常见问题之Java基础知识
+
+## 说说List,Set,Map三者的区别?
+
+- **List(对付顺序的好帮手):** List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
+- **Set(注重独一无二的性质):** 不允许重复的集合。不会有多个元素引用相同的对象。
+- **Map(用Key来搜索的专家):** 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
+
+## Arraylist 与 LinkedList 区别?
+
+- **1. 是否保证线程安全:** ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
+
+- **2. 底层数据结构:** Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向链表数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
+
+- **3. 插入和删除是否受元素位置的影响:** ① **ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。**
+
+- **4. 是否支持快速随机访问:** LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。
+
+- **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
+
+### **补充内容:RandomAccess接口**
+
+```
+public interface RandomAccess {
+}
+```
+
+查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。
+
+在binarySearch()方法中,它要判断传入的list 是否RamdomAccess的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法
+
+```
+ public static
+ int binarySearch(List extends Comparable super T>> list, T key) {
+ if (list instanceof RandomAccess || list.size()< 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);
+ }
+ public HashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+```
+
+下面这个方法保证了 HashMap 总是使用2的幂作为哈希表的大小。
+
+```java
+ /**
+ * 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;
+ }
+```
+
+## HashMap 和 HashSet区别
+
+如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 `clone() `、`writeObject()`、`readObject()`是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。
+
+| HashMap | HashSet |
+| :------------------------------: | :----------------------------------------------------------: |
+| 实现了Map接口 | 实现Set接口 |
+| 存储键值对 | 仅存储对象 |
+| 调用 `put()`向map中添加元素 | 调用 `add()`方法向Set中添加元素 |
+| HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性, |
+
+## HashSet如何检查重复
+
+当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让加入操作成功。(摘自我的Java启蒙书《Head fist java》第二版)
+
+**hashCode()与equals()的相关规定:**
+
+1. 如果两个对象相等,则hashcode一定也是相同的
+2. 两个对象相等,对两个equals方法返回true
+3. 两个对象有相同的hashcode值,它们也不一定是相等的
+4. 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
+5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。
+
+**==与equals的区别**
+
+1. ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同
+2. ==是指对内存地址进行比较 equals()是对字符串的内容进行比较
+3. ==指引用是否相同 equals()指的是值是否相同
+
+## HashMap的底层实现
+
+### JDK1.8之前
+
+JDK1.8 之前 HashMap 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。**
+
+**所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。**
+
+**JDK 1.8 HashMap 的 hash 方法源码:**
+
+JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。
+
+```
+ static final int hash(Object key) {
+ int h;
+ // key.hashCode():返回散列值也就是hashcode
+ // ^ :按位异或
+ // >>>:无符号右移,忽略符号位,空位都以0补齐
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+ }
+```
+
+对比一下 JDK1.7的 HashMap 的 hash 方法源码.
+
+```
+static int hash(int h) {
+ // This function ensures that hashCodes that differ only by
+ // constant multiples at each bit position have a bounded
+ // number of collisions (approximately 8 at default load factor).
+
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+}
+```
+
+相比于 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。
+
+所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
+
+[](https://camo.githubusercontent.com/eec1c575aa5ff57906dd9c9130ec7a82e212c96a/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f332f32302f313632343064626363333033643837323f773d33343826683d34323726663d706e6726733d3130393931)
+
+### JDK1.8之后
+
+相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
+
+[](https://camo.githubusercontent.com/20de7e465cac279842851258ec4d1ec1c4d3d7d1/687474703a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f31382d382d32322f36373233333736342e6a7067)
+
+> TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
+
+**推荐阅读:**
+
+- 《Java 8系列之重新认识HashMap》 :
+
+## HashMap 的长度为什么是2的幂次方
+
+为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ `(n - 1) & hash`”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。
+
+**这个算法应该如何设计呢?**
+
+我们首先可能会想到采用%取余的操作来实现。但是,重点来了:**“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。”** 并且 **采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。**
+
+## HashMap 多线程操作导致死循环问题
+
+主要原因在于 并发下的Rehash 会造成元素之间会形成一个循环链表。不过,jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap,因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失。并发环境下推荐使用 ConcurrentHashMap 。
+
+详情请查看:
+
+## ConcurrentHashMap 和 Hashtable 的区别
+
+ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。
+
+- **底层数据结构:** JDK1.7的 ConcurrentHashMap 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
+- **实现线程安全的方式(重要):** ① **在JDK1.7的时候,ConcurrentHashMap(分段锁)** 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。 **到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化)** 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② **Hashtable(同一把锁)** :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
+
+**两者的对比图:**
+
+图片来源:
+
+HashTable: [](https://camo.githubusercontent.com/b8e66016373bb109e923205857aeee9689baac9e/687474703a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f31382d382d32322f35303635363638312e6a7067)
+
+**JDK1.7的ConcurrentHashMap:** [](https://camo.githubusercontent.com/443af05b6be6ed09e50c78a1dca39bf75acb106d/687474703a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f31382d382d32322f33333132303438382e6a7067)**JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):** [](https://camo.githubusercontent.com/2d779bf515db75b5bf364c4f23c31268330a865e/687474703a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f31382d382d32322f39373733393232302e6a7067)
+
+## ConcurrentHashMap线程安全的具体实现方式/底层具体实现
+
+### JDK1.7(上面有示意图)
+
+首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
+
+**ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成**。
+
+Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
+
+```
+static class Segment extends ReentrantLock implements Serializable {
+}
+```
+
+一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。
+
+### JDK1.8 (上面有示意图)
+
+ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。Java 8在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为O(N))转换为红黑树(寻址时间复杂度为O(long(N)))
+
+synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
+
+## comparable 和 Comparator的区别
+
+- comparable接口实际上是出自java.lang包 它有一个 `compareTo(Object obj)`方法用来排序
+- comparator接口实际上是出自 java.util 包它有一个`compare(Object obj1, Object obj2)`方法用来排序
+
+一般我们需要对一个集合使用自定义排序时,我们就要重写`compareTo()`方法或`compare()`方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写`compareTo()`方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 `Collections.sort()`.
+
+### Comparator定制排序
+
+```java
+ ArrayList arrayList = new ArrayList();
+ arrayList.add(-1);
+ arrayList.add(3);
+ arrayList.add(3);
+ arrayList.add(-5);
+ arrayList.add(7);
+ arrayList.add(4);
+ arrayList.add(-9);
+ arrayList.add(-7);
+ System.out.println("原始数组:");
+ System.out.println(arrayList);
+ // void reverse(List list):反转
+ Collections.reverse(arrayList);
+ System.out.println("Collections.reverse(arrayList):");
+ System.out.println(arrayList);
+
+ // void sort(List list),按自然排序的升序排序
+ Collections.sort(arrayList);
+ System.out.println("Collections.sort(arrayList):");
+ System.out.println(arrayList);
+ // 定制排序的用法
+ Collections.sort(arrayList, new Comparator() {
+
+ @Override
+ public int compare(Integer o1, Integer o2) {
+ return o2.compareTo(o1);
+ }
+ });
+ System.out.println("定制排序后:");
+ System.out.println(arrayList);
+```
+
+Output:
+
+```
+原始数组:
+[-1, 3, 3, -5, 7, 4, -9, -7]
+Collections.reverse(arrayList):
+[-7, -9, 4, 7, -5, 3, 3, -1]
+Collections.sort(arrayList):
+[-9, -7, -5, -1, 3, 3, 4, 7]
+定制排序后:
+[7, 4, 3, 3, -1, -5, -7, -9]
+```
+
+### 重写compareTo方法实现按年龄来排序
+
+```java
+// person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列
+// 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他
+// 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了
+
+public class Person implements Comparable {
+ private String name;
+ private int age;
+
+ public Person(String name, int age) {
+ super();
+ this.name = name;
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ /**
+ * TODO重写compareTo方法实现按年龄来排序
+ */
+ @Override
+ public int compareTo(Person o) {
+ // TODO Auto-generated method stub
+ if (this.age > o.getAge()) {
+ return 1;
+ } else if (this.age < o.getAge()) {
+ return -1;
+ }
+ return age;
+ }
+}
+
+```
+
+```java
+ public static void main(String[] args) {
+ TreeMap pdata = new TreeMap();
+ pdata.put(new Person("张三", 30), "zhangsan");
+ pdata.put(new Person("李四", 20), "lisi");
+ pdata.put(new Person("王五", 10), "wangwu");
+ pdata.put(new Person("小红", 5), "xiaohong");
+ // 得到key的值的同时得到key所对应的值
+ Set keys = pdata.keySet();
+ for (Person key : keys) {
+ System.out.println(key.getAge() + "-" + key.getName());
+
+ }
+ }
+```
+
+Output:
+
+```
+5-小红
+10-王五
+20-李四
+30-张三
+```
+
+## 集合框架底层数据结构总结
+
+### Collection
+
+#### 1. List
+
+- **Arraylist:** Object数组
+- **Vector:** Object数组
+- **LinkedList:** 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环) 详细可阅读[JDK1.7-LinkedList循环链表优化](https://www.cnblogs.com/xingele0917/p/3696593.html)
+
+#### 2. Set
+
+- **HashSet(无序,唯一):** 基于 HashMap 实现的,底层采用 HashMap 来保存元素
+- **LinkedHashSet:** LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
+- **TreeSet(有序,唯一):** 红黑树(自平衡的排序二叉树。)
+
+### Map
+
+- **HashMap:** JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
+- **LinkedHashMap:** LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931)
+- **Hashtable:** 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
+- **TreeMap:** 红黑树(自平衡的排序二叉树)
+
+## 如何选用集合?
+
+## 集合的选用
+
+主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。
\ No newline at end of file
diff --git a/docs/java/LinkedList.md b/docs/java/collection/LinkedList.md
similarity index 99%
rename from docs/java/LinkedList.md
rename to docs/java/collection/LinkedList.md
index 983c1fae0d0..d26bc752267 100644
--- a/docs/java/LinkedList.md
+++ b/docs/java/collection/LinkedList.md
@@ -458,7 +458,7 @@ public class LinkedListDemo {
linkedList.add(3);
linkedList.removeFirstOccurrence(3); // 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表)
System.out.println("After removeFirstOccurrence(3):" + linkedList);
- linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表)
+ linkedList.removeLastOccurrence(3); // 从此列表中移除最后一次出现的指定元素(从尾部到头部遍历列表)
System.out.println("After removeFirstOccurrence(3):" + linkedList);
/************************** 遍历操作 ************************/
diff --git "a/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md" "b/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md"
new file mode 100644
index 00000000000..ec4a001f3cf
--- /dev/null
+++ "b/docs/java/jvm/JDK\347\233\221\346\216\247\345\222\214\346\225\205\351\232\234\345\244\204\347\220\206\345\267\245\345\205\267\346\200\273\347\273\223.md"
@@ -0,0 +1,334 @@
+
+
+- [JDK 监控和故障处理工具总结](#jdk-监控和故障处理工具总结)
+ - [JDK 命令行工具](#jdk-命令行工具)
+ - [`jps`:查看所有 Java 进程](#jps查看所有-java-进程)
+ - [`jstat`: 监视虚拟机各种运行状态信息](#jstat-监视虚拟机各种运行状态信息)
+ - [` jinfo`: 实时地查看和调整虚拟机各项参数](#-jinfo-实时地查看和调整虚拟机各项参数)
+ - [`jmap`:生成堆转储快照](#jmap生成堆转储快照)
+ - [**`jhat`**: 分析 heapdump 文件](#jhat-分析-heapdump-文件)
+ - [**`jstack`** :生成虚拟机当前时刻的线程快照](#jstack-生成虚拟机当前时刻的线程快照)
+ - [JDK 可视化分析工具](#jdk-可视化分析工具)
+ - [JConsole:Java 监视与管理控制台](#jconsolejava-监视与管理控制台)
+ - [连接 Jconsole](#连接-jconsole)
+ - [查看 Java 程序概况](#查看-java-程序概况)
+ - [内存监控](#内存监控)
+ - [线程监控](#线程监控)
+ - [Visual VM:多合一故障处理工具](#visual-vm多合一故障处理工具)
+
+
+
+# JDK 监控和故障处理工具总结
+
+## JDK 命令行工具
+
+这些命令在 JDK 安装目录下的 bin 目录下:
+
+- **`jps`** (JVM Process Status): 类似 UNIX 的 `ps` 命令。用户查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息;
+- **`jstat`**( JVM Statistics Monitoring Tool): 用于收集 HotSpot 虚拟机各方面的运行数据;
+- **`jinfo`** (Configuration Info for Java) : Configuration Info forJava,显示虚拟机配置信息;
+- **`jmap`** (Memory Map for Java) :生成堆转储快照;
+- **`jhat`** (JVM Heap Dump Browser ) : 用于分析 heapdump 文件,它会建立一个 HTTP/HTML 服务器,让用户可以在浏览器上查看分析结果;
+- **`jstack`** (Stack Trace for Java):生成虚拟机当前时刻的线程快照,线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。
+
+### `jps`:查看所有 Java 进程
+
+`jps`(JVM Process Status) 命令类似 UNIX 的 `ps` 命令。
+
+`jps`:显示虚拟机执行主类名称以及这些进程的本地虚拟机唯一 ID(Local Virtual Machine Identifier,LVMID)。`jps -q` :只输出进程的本地虚拟机唯一 ID。
+
+```powershell
+C:\Users\SnailClimb>jps
+7360 NettyClient2
+17396
+7972 Launcher
+16504 Jps
+17340 NettyServer
+```
+
+`jps -l`:输出主类的全名,如果进程执行的是 Jar 包,输出 Jar 路径。
+
+```powershell
+C:\Users\SnailClimb>jps -l
+7360 firstNettyDemo.NettyClient2
+17396
+7972 org.jetbrains.jps.cmdline.Launcher
+16492 sun.tools.jps.Jps
+17340 firstNettyDemo.NettyServer
+```
+
+`jps -v`:输出虚拟机进程启动时 JVM 参数。
+
+`jps -m`:输出传递给 Java 进程 main() 函数的参数。
+
+### `jstat`: 监视虚拟机各种运行状态信息
+
+jstat(JVM Statistics Monitoring Tool) 使用于监视虚拟机各种运行状态信息的命令行工具。 它可以显示本地或者远程(需要远程主机提供 RMI 支持)虚拟机进程中的类信息、内存、垃圾收集、JIT 编译等运行数据,在没有 GUI,只提供了纯文本控制台环境的服务器上,它将是运行期间定位虚拟机性能问题的首选工具。
+
+**`jstat` 命令使用格式:**
+
+```powershell
+jstat -