Skip to content

Commit

Permalink
优化了图片显示
Browse files Browse the repository at this point in the history
Signed-off-by: LtLei <[email protected]>
  • Loading branch information
LtLei committed Dec 5, 2018
1 parent c475b24 commit 761b09b
Show file tree
Hide file tree
Showing 26 changed files with 229 additions and 148 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
**/.vscode/*
*.class
*.class
**/_book/*
SUMMARY.md
run.bat
2 changes: 1 addition & 1 deletion java/collection/Java集合源码分析之Iterable概述.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void set(E e);

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
8 changes: 6 additions & 2 deletions java/collection/Java集合源码分析之LinkedList.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,21 @@
```
public abstract ListIterator<E> listIterator(int index);
```

其他一些方法的实现都利用了这个`listIterator`方法,我们不再一一查看了。下面我们分析`LinkedList`的实现

# LinkedList的结构

`LinkedList`的继承结构如下所示:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_9_1.png"/>
<div align="center"><img src ="/image/img_9_1.png" /><br/>LinkedList结构图</div>

可以看到,`LinkedList`也实现了`Cloneable``java.io.Serializable`等方法,借鉴于`ArrayList`的经验,我们可以想到它的`Clone`也是浅克隆,在序列化方法也采用了同样的方式,我们就不再赘述了。

# 构造方法与成员变量

## 数据单元Node

在介绍链表结构时提到过,其数据单元分为数据域和指针域,分别存储数据和指向下一个元素的位置,在java中只要定义一个实体类就可以解决了。

```
Expand Down Expand Up @@ -58,6 +60,7 @@ transient Node<E> last;
```

## 构造函数

因为链表没有长度方面的问题,所以也不会涉及到扩容等问题,其构造函数也十分简洁了。

```
Expand All @@ -69,6 +72,7 @@ public LinkedList(Collection<? extends E> c) {
addAll(c);
}
```

一个默认的构造函数,什么都没有做,一个是用其他集合初始化,调用了一下`addAll`方法。`addAll`方法我们就不再分析了,它应该是和添加一个元素的方法是一致的。

# 重要方法
Expand Down Expand Up @@ -243,7 +247,7 @@ public boolean offerFirst(E e) {

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
> Unlike sets, lists typically allow duplicate elements. More formally, lists typically allow pairs of elements <tt>e1</tt> and **e2** such that **e1.equals(e2)**, and they typically allow multiple null elements if they allow null elements at all. It is not inconceivable that someone might wish to implement a list that prohibits duplicates, by throwing runtime exceptions when the user attempts to insert them, but we expect this usage to be rare.
# List特有方法

我们关注其不同于Collection的方法,主要有以下这些:

```
//在指定位置,将指定的集合插入到当前的集合中
boolean addAll(int index, Collection<? extends E> c);
Expand Down Expand Up @@ -159,6 +161,7 @@ protected void removeRange(int fromIndex, int toIndex) {
我们先看看`SubList`相关的内容。`SubList`并不是新建了一个集合,只是持有了当前集合的引用,然后控制一下用户可以操作的范围,所以在接口定义时就说明了其更改会直接反应到原集合中。`SubList`定义在`AbstractList`内部,并且是`AbstractList`的子类。在`AbstractList`的基础上增加了对可选范围的控制。

`equals``hashcode`的实现,也关乎我们的使用。在`AbstractList`中,这两个方法不仅与其实例有关,也和其内部包含的元素有关,所以在定义数据元素时,也应该复写这两个方法,以保证程序的正确运行。这里看下其源码加深一下印象吧。

```
public boolean equals(Object o) {
if (o == this)
Expand Down Expand Up @@ -195,7 +198,7 @@ public int hashCode() {

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# ArrayList继承结构

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_7_1.png"/>
<div align="center"><img src ="/image/img_7_1.png" /><br/>ArrayList结构图</div>

可以看到,`ArrayList``AbstractList`的子类,同时实现了`List`接口。除此之外,它还实现了三个标识型接口,这几个接口都没有任何方法,仅作为标识表示实现类具备某项功能。`RandomAccess`表示实现类支持快速随机访问,`Cloneable`表示实现类支持克隆,具体表现为重写了`clone`方法,`java.io.Serializable`则表示支持序列化,如果需要对此过程自定义,可以重写`writeObject``readObject`方法。

Expand Down Expand Up @@ -83,6 +83,7 @@ public ArrayList(Collection<? extends E> c) {
```

# 重要方法

`ArrayList`已经是一个具体的实现类了,所以在`List`接口中定义的所有方法在此都做了实现。其中有些在`AbstractList`中实现过的方法,在这里再次被重写,我们稍后就可以看到它们的区别。

先看一些简单的方法:
Expand Down Expand Up @@ -175,6 +176,7 @@ public void add(int index, E element) {
size++;
}
```

以上两种添加数据的方式都调用到了`ensureCapacityInternal`这个方法,我们看看它是如何完成工作的:

```
Expand Down Expand Up @@ -296,6 +298,7 @@ private void writeObject(java.io.ObjectOutputStream s)
}
}
```

`readObject`是一个相反的过程,就是把数据正确的恢复回来,并将`elementData`设置好即可,感兴趣可以自行阅读源码。

# 总结
Expand All @@ -313,7 +316,7 @@ private void writeObject(java.io.ObjectOutputStream s)

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
数组与链表在处理数据时各有优缺点,数组查询速度很快而插入很慢,链表在插入时表现优秀但查询无力。哈希表则整合了数组与链表的优点,能在插入和查找等方面都有不错的速度。我们之后要分析的`HashMap`就是基于哈希表实现的,不过在JDK1.8中还引入了**红黑树**,其性能进一步提升了。本文主要分析JDK中关于`Map`的定义。

## 接口Map

Map的定义为:

> An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
Expand All @@ -10,6 +11,7 @@ Map的定义为:
在分析其定义的方法前,我们要先了解一下`Map.Entry`这个接口。

# 接口Map.Entry

存储在Map中的数据需要实现此接口,主要提供对key和value的操作,也是我们使用最多的操作。我们先分析它:

```
Expand Down Expand Up @@ -199,7 +201,7 @@ public Set<K> keySet() {

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ NavigableSet<K> descendingKeySet();

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ K lastKey();

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
32 changes: 18 additions & 14 deletions java/collection/Java集合源码分析之Map(五):HashMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
`HashMap`的结构如下所示:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_1.png"/>
<div align="center"><img src ="/image/img_11_1.png" /><br/>HashMap结构图</div>

# 构造函数与成员变量

Expand Down Expand Up @@ -123,7 +123,7 @@ static final int tableSizeFor(int cap) {

如果你是跟随我文章的顺序读到这里,有没有感觉十分熟悉?这就是找到距离`cap`参数最近的2的次幂呀。没有读过也没有关系,这里奉上链接,里面有非常详细的解析。

[Java集合源码分析之Queue(三):ArrayDeque](https://github.com/LtLei/articles/blob/master/java/collection/Java%E9%9B%86%E5%90%88%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8BQueue%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AArrayDeque.md)
[Java集合源码分析之Queue(三):ArrayDeque](Java集合源码分析之Queue(三):ArrayDeque.md)

# 重要方法

Expand All @@ -136,6 +136,7 @@ public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
```

这里我们先关注下`hash`函数,在HashMap中其实现如下:

```
Expand All @@ -144,6 +145,7 @@ static final int hash(Object key) {
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
```

这里用到的方法很简单,就是把key与其高16位异或。文档中有如下说明:
> There is a tradeoff between speed, utility, and quality of bit-spreading.
Expand Down Expand Up @@ -250,6 +252,7 @@ final void treeifyBin(Node<K,V>[] tab, int hash) {
}
}
```

无论是在`put`还是`treeify`时,都依赖于`resize`,它的重要性不言而喻。它不仅可以调整大小,还能调整树化和反树化(从树变为链表)所带来的影响。我们看看它具体做了哪些工作:

```
Expand Down Expand Up @@ -352,6 +355,7 @@ public V remove(Object key) {
null : e.value;
}
```

和插入一样,其实际的操作在`removeNode`方法中完成,我们看下其实现:

```
Expand Down Expand Up @@ -537,45 +541,45 @@ final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,

图中左侧是hash算法完成后的hash值,中间是插入的内容,有的位置还没有数据,有的位置已经插入了一些数据并变为了链表,并且我们假设capacity已经大于64(64是可以树化的阈值)。如下图所示:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_2.png"/>
<div align="center"><img src ="/image/img_11_2.png" /><br/>初始状态</div>

为了完整的演示,现在我们向表中插入一个hash=6的值。由于6的位置现在是空的,所以元素会直接放在此处:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_3.png"/>
<div align="center"><img src ="/image/img_11_3.png" /><br/>插入一个元素</div>

我们继续插入一个hash=6的值,此时,6的位置已经存在一个元素,所以新的元素会通过链表的方式链接在18的后边,如下所示:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_4.png"/>
<div align="center"><img src ="/image/img_11_4.png" /><br/>链接一个元素</div>

现在,我们再插入几个hash=6的值,直到达到链表变为红黑树的阈值(默认是8个):

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_5.png"/>
<div align="center"><img src ="/image/img_11_5.png" /><br/>树化临界点</div>

此时,在6的位置上有了8个元素。这时,我们要向其中加入一个9,就需要进行树化,用红黑树代替链表以提升查询性能。

树化时,先获取第一个元素18,将其转为`TreeNode`节点,并设置为head。然后把后续节点依次转为`TreeNode`,并依次挂在head之后,他们的**prev**指向前一个元素,**next**指向后一个元素。挂完之后类似下图:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_6.png"/>
<div align="center"><img src ="/image/img_11_6.png" /><br/>转为树节点</div>

转为树节点之后,需要通过head,也就是这里的18,来进一步调整。首先,18就是root节点,颜色设置为黑色。然后比较18与20,它们的hash都一样,所以会采用`Comparable`比较。这时20应该在18的右边。然后按照`balanceInsertion`方法此时不需要调整,所以18依然是root,且依然在table表的首位,结果如下:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_7.png"/>
<div align="center"><img src ="/image/img_11_7.png" /><br/>调整20</div>

然后再调整31,31在18的右侧,结果如下:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_8.png"/>
<div align="center"><img src ="/image/img_11_8.png" /><br/>调整31</div>

这时候就破坏了红黑树了,按照在`TreeMap`中介绍的方法,需要进行调整,这里不再展示过程,而直接展示结果了:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_9.png"/>
<div align="center"><img src ="/image/img_11_9.png" /><br/>调整31</div>

如果仅是一棵红黑树,到此调整就完毕了,但是这棵红黑树需要在table表中,所以其根节点必须在首位。我们看到,加入31以后,根节点由18变为了20,所以就需要按照`moveRootToFront`方法将root节点提前。这一操作并不会改变树的结构,仅仅是把新的root和原来的root在table表中的位置交换了一下,如下所示:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_10.png"/>
<div align="center"><img src ="/image/img_11_10.png" /><br/>调整root位置</div>

然后按照这样的规则继续调整剩下的元素,这些步骤和上述类似,最终调整结果如下:

<img src="https://github.com/LtLei/articles/blob/master/java/collection/image/img_11_11.png"/>
<div align="center"><img src ="/image/img_11_11.png" /><br/>最终结果</div>

# 总结

Expand All @@ -591,15 +595,15 @@ final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,

# 相关文章

[Java集合源码分析之Map(四):TreeMap](https://github.com/LtLei/articles/blob/master/java/collection/Java%E9%9B%86%E5%90%88%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8BMap%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9ATreeMap.md)
[Java集合源码分析之Map(四):TreeMap](Java集合源码分析之Map(四):TreeMap.md)

---

本文到此就结束了,如果您喜欢我的文章,可以关注我的微信公众号: **大大纸飞机**

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ static class Entry<K,V> extends HashMap.Node<K,V> {
}
}
```

这个`Entry``HashMap`中被引用过,主要是为了能让`LinkedHashMap`也支持树化。在这里则是用来存储元素。

```
Expand Down Expand Up @@ -121,6 +122,7 @@ private void transferLinks(LinkedHashMap.Entry<K,V> src,
a.before = dst;
}
```

最后我们看下`afterNodeInsertion`做了哪些事情吧:

```
Expand All @@ -143,6 +145,7 @@ protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
```

## 查询

因为要支持访问顺序,所以获取元素的方法和`HashMap`也有所不同。下面我们看下其实现:
Expand Down Expand Up @@ -203,7 +206,7 @@ void afterNodeAccess(Node<K,V> e) { // move node to last

或者扫描下方二维码直接添加:

<img src ="https://github.com/LtLei/articles/blob/master/qrcode.jpg" />
<div align="center"><img src ="/image/qrcode.jpg" /><br/>扫描二维码关注</div>

您也可以关注我的简书:https://www.jianshu.com/u/9ee83a8ee52d

Expand Down
Loading

0 comments on commit 761b09b

Please sign in to comment.