-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
335 lines (164 loc) · 209 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>MySQL索引</title>
<link href="/2020/06/13/%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL%E7%B4%A2%E5%BC%95/"/>
<url>/2020/06/13/%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL%E7%B4%A2%E5%BC%95/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h4 id="MySQL索引底层原理讲解:"><a href="#MySQL索引底层原理讲解:" class="headerlink" title="MySQL索引底层原理讲解:"></a>MySQL索引底层原理讲解:</h4><blockquote><h6 id="1-MySQL中到底是怎么生成B-树的"><a href="#1-MySQL中到底是怎么生成B-树的" class="headerlink" title="1.MySQL中到底是怎么生成B+树的"></a>1.MySQL中到底是怎么生成B+树的</h6><h6 id="2-我们该如何判断一个SQL是否可以走索引"><a href="#2-我们该如何判断一个SQL是否可以走索引" class="headerlink" title="2.我们该如何判断一个SQL是否可以走索引"></a>2.我们该如何判断一个SQL是否可以走索引</h6><h6 id="3-用实际案例来分析索引底层原理"><a href="#3-用实际案例来分析索引底层原理" class="headerlink" title="3.用实际案例来分析索引底层原理"></a>3.用实际案例来分析索引底层原理</h6><h6 id="4-P7-P8会如何面试索引底层原理"><a href="#4-P7-P8会如何面试索引底层原理" class="headerlink" title="4.P7,P8会如何面试索引底层原理"></a>4.P7,P8会如何面试索引底层原理</h6></blockquote><h5 id="基础:"><a href="#基础:" class="headerlink" title="基础:"></a>基础:</h5><blockquote><h5 id="1-gt-MySQL中varchar与char区别:"><a href="#1-gt-MySQL中varchar与char区别:" class="headerlink" title="1> MySQL中varchar与char区别:"></a>1> MySQL中varchar与char区别:</h5><p>1.定长和变长<br>char表示定长,长度固定,varchar表示变长即长度可变。如char(10),表示最多能存储10个字符,无论你插入的是多少,都是10个,如果少于10个,则用空格填满。而varchar(10),小于10的话,则插入多少个字符就是多少<br>2.存储的容量不同<br>对于char来说,最多能存放的字符个数255,与编码无关。而varchar最多能存放65532个字符,varchar的最大有效长度由最大行大小和使用的字符集确定;整体最大长度是 65,532(65535-3=65532 减1的原因是实际行存储从第二个字节开始’; 减2的原因是varchar头部的2个字节表示长度;)字节 </p></blockquote><blockquote><h5 id="2-gt-局部性原理:"><a href="#2-gt-局部性原理:" class="headerlink" title="2> 局部性原理:"></a>2> 局部性原理:</h5><p>我们在通过sql条件查询 where 关键字 后面的条件查询时,其中的过程是数据库会先将该条数据从磁盘中读取出来,然后在内存中根据查询条件进行逻辑比较匹配到对应的数据后返回结果,而将数据从磁盘中读取这一过程叫做磁盘IO;<br>但是我们可以通过 SHOW GLOBAL STATUS like ‘Innodb_page_size’ 可以发现其实我们在取数据时,MySQL会以页为最小单位返回数据,默认一页是16kb;那为什么会以页为单位呢,这就要说到局部性原理了;<br>局部性原理即用我理解的意思来说就是计算机在读取磁盘数据放入CPU的时候默认你读取的当前数据后还会进行对当前数据相邻的数据进行读取,所以计算机会以该数据的当前页(4Kb)为最小单位对所有数据进行磁盘IO放进内存中,这样做的目的就是为了减少磁盘IO的操作,提高系统的性能</p></blockquote><blockquote><h5 id="3-gt-数据行的概念:"><a href="#3-gt-数据行的概念:" class="headerlink" title="3> 数据行的概念:"></a>3> 数据行的概念:</h5><p>我们在存储和查询数据时所对应的行不仅限于我们自己规定的字段信息,还有Innodb隐藏的三个字段:其中有一个很重要是rowID,作用是在我们定义表中没有定义唯一约束,那么Innidb 会自动将 rowID 成为这个表的唯一约束即主键</p></blockquote><blockquote><h5 id="4-gt-回表:"><a href="#4-gt-回表:" class="headerlink" title="4> 回表:"></a>4> 回表:</h5><p>首先要理解,我们在创建一张表的时候不管你是否指定了唯一约束,指定了就会以它为主键创建聚集索引,没有指定也会让行的隐藏字段rowID最为主键,所以我们每张表都会有一个主键;而我们每次创建的索引其实都会生成一张表即为索引表,结构就是B+Tree。最后我们过创建的索引查询到该行的主键,再通过主键查询到聚集索引表后查询到的数据即为回表;</p></blockquote><blockquote><h5 id="5-gt-最左前缀原则"><a href="#5-gt-最左前缀原则" class="headerlink" title="5>最左前缀原则"></a>5>最左前缀原则</h5><p>即我们在走索引的时会先去根据条件来匹配对应数据的位置,自上向下;我们在创建联合索引的时候会根据索引字段从左向右排序形成B+Tree,也就是说联合索引的最右的字段可能是无序的,所以决定我们走不走索引的关键是左边的索引字段能不能匹配,如果左边是?或者%都不会走索引;</p></blockquote><blockquote><h5 id="6-gt-B-Tree-B-Tree的原理分析"><a href="#6-gt-B-Tree-B-Tree的原理分析" class="headerlink" title="6> B-Tree,B+Tree的原理分析"></a>6> B-Tree,B+Tree的原理分析</h5><h6 id="B-Tree的基本介绍:"><a href="#B-Tree的基本介绍:" class="headerlink" title="B-Tree的基本介绍:"></a>B-Tree的基本介绍:</h6><p>B树通过重新组织节点,降低树的高度,并且减少i/o读写次数来提升效率;<br><img alt data-src="/img/MySQL/B-Tree.png" class="lazyload"><br>1> 如图B树通过重新组织节点,降低了树的高度<br>2> 文件系统及数据库系统的设计者利用磁盘与读取原理(局部性原理),将一个节点的大小设为等于一个页(一页大小通常是4K,MySQL InnoDB是16k),这样每个节点只需要一次I/O就可以完成加载<br>3> 将树的度M设置为1024,在600亿个元素中最多只需要4次I/O操作就可以读取到想要的元素,B树(B+)广泛应用于文件存储及数据库系统中。树的度:树中有几个分叉,度就有几个;公式:树中节点数 = 总分叉数 + 1</p></blockquote><blockquote><h6 id="B树说明:"><a href="#B树说明:" class="headerlink" title="B树说明:"></a>B树说明:</h6><p>1> B树的阶:节点的最多子节点个数。超出节点会溢出,产生节点分裂,图如下:<br><img alt data-src="/img/MySQL/B%E6%A0%91%E9%98%B6.png" class="lazyload"></p></blockquote><p><img alt data-src="/img/MySQL/B%E6%A0%91%E9%98%B6%E8%8A%82%E7%82%B9%E5%88%86%E8%A3%82.png" class="lazyload"></p><blockquote><p>2> B树的搜索,从根节点开始,对节点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子节点,重复查询,直到所对应的儿子节点指针为空,或已经是叶子节点<br>3> 关键字集合分布在整颗树中,即叶子节点和非叶子节点都存放数据<br>4> 搜索有可能在非叶子节点结束<br>5> 其搜索性能等价于在关键字全集内做一次二分查找</p></blockquote><blockquote><h6 id="B-树说明:"><a href="#B-树说明:" class="headerlink" title="B+树说明:"></a>B+树说明:</h6><p>1> B+树的搜索与B树基本相同。区别是B+树只有达到叶子节点才能命中(B树可以在非叶子节点命中),其性能也等价于在关键字全集做一次二分查找<br>2> 所有关键字都出现在叶子节点的链表中(即数据只能在叶子节点【也叫稠密的索引】),且链表中的关键字(数据)恰好是有序的<br>3> 不能在非叶子节点命中<br>4> 非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层<br>5> 更适合文件索引系统<br>6> B树 和 B+树各有自己的应用场景,不能说B+树完全比B树好,反之亦然</p></blockquote><h5 id="MySQL索引:"><a href="#MySQL索引:" class="headerlink" title="MySQL索引:"></a>MySQL索引:</h5><p>围绕一个例子来讲解:<br>定义一张表:IndexTest<br>现在准备往里面插入8条数据:<br>insert into IndexTest values(4,3,1,1,’d’);<br>insert into IndexTest values(1,1,1,1,’a’);<br>insert into IndexTest values(8,8,8,8,’h’);<br>insert into IndexTest values(2,2,2,2,’b’);<br>insert into IndexTest values(5,2,3,5,’d’);<br>insert into IndexTest values(3,3,1,1,’d’);<br>insert into IndexTest values(7,4,5,5,’g’);<br>insert into IndexTest values(6,6,4,4,’f’);</p><p>根据局部性原理我可以牵引出一个以”页”为存储数据单位的概念,那么这个”页”是什么样子的呢:<br>![](/img/MySQL/屏幕快照 2020-02-23 下午3.55.00.png)</p><h5 id="1-gt-用户数据区域:"><a href="#1-gt-用户数据区域:" class="headerlink" title="1>用户数据区域:"></a>1>用户数据区域:</h5><p>用户数据区域存放的数据及为我们自己插入的数据,也就是说我们在用 “insert” 关键字的时候所要插入的数据<br>以上为例 我们进行插入操作 及页数据就会变成 —></p><p>![](/img/MySQL/屏幕快照 2020-02-23 下午4.28.33.png)<br>从中可以发现:我们在使用Innodb的进行数据插入成功后,数据是以唯一索引进行排序后的存储的,这样做势必会影响插入操作的时间,但是同时他会提高我们查询的时间,例:我们在取a=3的数据时,先会把这页数据通过磁盘IO读取到内存中,在内存中进行比较,当比计较完(4,3,1,1,’d’)时它就不会再向后进行比较了,因为Innodb已经将插入的数据已经排好了序。这样做也是对我们查询的性能做了优化。<br>但是继续思考,如果我们现在存的字段很少,极端情况下我们就只存了一个主键Id,但是我们Innodb每一页是以16Kb为存储数据的,这样就会产生用户数据区域中存储数据链很长很长,假定8w,现在我们就是需要查询a=8w的这条数据,那么是不是前面做的优化就没有用了,这是我们就需要了解”页”中的另一个区域—>页目录</p><h5 id="2-gt-页目录:"><a href="#2-gt-页目录:" class="headerlink" title="2> 页目录:"></a>2> 页目录:</h5><p>页目录我们可以理解为书的目录,即将每一章的内容的起始页码记录下来,对每一章节的所有小章节内容进行分组,换成我们的数据存储如下图:<br><img alt data-src="/img/MySQL/%E9%A1%B5-%E7%9B%AE%E5%BD%95.png" class="lazyload"><br>可以看出Innodb已经将我们该页数据的四条内容分成了两组,具体怎么分组涉及到的算法和机制暂不赘述,那么根据前面的解释我们就可以get到页目录里的两个目录项分别就是”1”,”4”;<br><img alt data-src="/img/MySQL/%E9%A1%B5-%E7%9B%AE%E5%BD%952.png" class="lazyload"><br>那么现在就可以看出,如果我们再去找某个数据时,Innodb会先去页目录匹配,再根据页目录的起始位置去查询,这样就算数据量大并且要查询的条件也很极端,也不会进行全部扫描,也能够大大的缩短定位的速度;<br>前面我们说”页”是我们Innodb中存储数据最小且在运行中固定的存储单位,为16Kb,<br>但是随着我们数据量慢慢变大超过了16Kb的话会发生什么呢?</p><h5 id="3-gt-开辟新页:"><a href="#3-gt-开辟新页:" class="headerlink" title="3> 开辟新页:"></a>3> 开辟新页:</h5><p><img alt data-src="/img/MySQL/%E5%BC%80%E8%BE%9F%E6%96%B0%E9%A1%B5.png" class="lazyload"></p><p>结合上面的图也可以发现,这两个页数据其实是连续的,他们通过页头的next指针和prev指针相互关联,并且第二页的数据也是经过排序和分组的;现在我们如果需要查”a=8”这条数据应该怎么查呢?是不是应该先从第100页(其实就是其实页)开始查询页目录然后发现没有在根据next指针查询第200页的页目录然后找到对应数据,也就是说在查询某条数据时是通过其实页然后根据next指针依次查询。<br>思考:假设现在有n页数据我要的数据就在n页,那是不是就需要从起始页查询到n页?很明显,这样不是我们希望的,那沿用刚才页目录的思想,我们就可以将”100页”及”200页”这两页在开辟新的一页,来记录这两页的 “地址”和”最小主键”,这种页就叫目录页:<br><img alt data-src="/img/MySQL/%E7%9B%AE%E5%BD%95%E9%A1%B5.png" class="lazyload"><br>顶端的是目录页,下面的就是数据页,这种结构就是B+Tree也就是我们InonDB的索引结构</p><h5 id="4-gt-B-Tree"><a href="#4-gt-B-Tree" class="headerlink" title="4> B+Tree:"></a>4> B+Tree:</h5><p><img alt data-src="/img/MySQL/B+Tree.png" class="lazyload"><br>可以看出B+Tree的叶子节点保存了我们所有的数据,而非叶子节点又冗余了一份数据,这样的好处是在查询某个数据是可以通过该特点迅速定位到节点数据,同时在范围查询是,比如查询>1的所有数据,就可通过叶子节点先定位到1的节点,再根据数据链一一查出右边的数据即可;<br>根据这些特点我们再对照着刚才分析出来的目录页看看,第一,每一页都是B+Tree的一个节点,而下面的数据页就是我们的叶子节点,上面的目录页就是我们冗余的非叶子节点,非叶子节点也保存了叶子节点的数据,唯一不同的是InnoDB不光让每个节点指向了下个节点,还存了上个节点的信息,也就是双向链表,这样的好处就是在范围查询时更加方便,比如前面说了传统的B+Tree在范围查询时>1的时候确实好查,但是<1呢?InnoDB在传统的基础上又做了优化,更加便于范围查询;</p><h5 id="5-gt-MySQL-InnoDB-B-Tree:"><a href="#5-gt-MySQL-InnoDB-B-Tree:" class="headerlink" title="5> MySQL InnoDB B+Tree:"></a>5> MySQL InnoDB B+Tree:</h5><p><img alt data-src="/img/MySQL/%E4%B8%BB%E9%94%AE%E7%B4%A2%E5%BC%95B+Tree.png" class="lazyload"><br>这就是InnoDB根据主键索引创建出来的B+Tree,我们也称他为聚集索引;<br>而我们通过条件查询从这个Tree的顶端依次查询到底端,这种方式就是我们常说的走索引,而我们自左向右的查询就叫做全表扫描;<br>一些关于主键作为索引的建议:<br>为什么我们不建议用UUID作为生成主键的生成规则:<br>1.是因为如果我们用的UUID作为主键的生成规则,主键ID会非常的长,势必会占用一部分空间,那么可能一页本来可以存10条数据,只能存5条,那么页也会变多,占用的内存也会多;查询数据取数据时磁盘IO次数也会变多,性能也会收到影响,还有页数越多,Tree的高度也会增高,而我们做索引是自上而下,高度越高那查的速度也就相对越慢;<br>2.因为UUDI生成是无序的,我们在插入数据是会先排序,那么使用UUID就会造成每次插入数据每次进行比较+位移的操作,降低插入速度;</p><h5 id="6-gt-联合索引:"><a href="#6-gt-联合索引:" class="headerlink" title="6> 联合索引:"></a>6> 联合索引:</h5><p><img alt data-src="/img/MySQL/%E7%BB%84%E5%90%88%E7%B4%A2%E5%BC%95.png" class="lazyload"><br>所谓组合索引,就是将定义的组合索引字段根据前后顺序一一比计较排序形成B+Tree的叶子节点也就是数据数据页,再根据数据页形成联合索引;<br>数据页不光存储了联合索引字段的数据,还存储了该索引对应行数据的主键的ID,这样做的目的是为了,当查询的内容不光包含索引字段的信息,还有其他的信息,如 select * …,这时就会通过主键ID去查询该行的所有数据,这也叫做回表。如果在联合索引的数据页再把所有的数据存储一份这样的话磁盘空间会大大的被占用,你想嘛,建一个索引就复制一份完整的数据,那不得撑爆磁盘;</p><h5 id="7-gt-案例:"><a href="#7-gt-案例:" class="headerlink" title="7> 案例:"></a>7> 案例:</h5><p>下面这些sql语句会走索引吗?(可以使用 explain 关键字来查看是否走索引)</p><blockquote><p>select * from IndexTest where c=1 and d=1;<br>分析:参考基础最左前缀原则<br>结论:不会</p></blockquote><blockquote><p>select * from IndexTest where b > 1;<br>分析: 查询优化器:“可以走索引但是没必要”<br>简单来说就是这条sql可以走联合索引,但是查询优化器判断觉得这样比全表扫描还慢,所以没有走索引;分析一下如果走索引那么就会先通过联合索引查到对应的主键,再通过主键查询聚集索引表,在自上向下查询到区间的数据。<br>结论:不会</p></blockquote><blockquote><p>select b from IndexTest<br>分析:第一,不管是走联合索引还是聚集索引都不满足条件,但是我们可以分析出,联合索引中他是存了”b”字段的信息的,也就是说我们可以通过联合索引或是全表扫描都可以获取”b”字段的信息;第二:因为是联合索引所以它存的值不完整,也就是说,一页数据中存的数据就更多,可能在聚合索引中一页存10条数据,在联合索引中一页可以存20条,那么势必磁盘IO的次数就少了,节约了时间及空间,所以查询优化器决定走联合索引;<br>结论:会</p></blockquote><h5 id="PS-深入的了解各个数据结构的-生成-执行的过程可以参考:Data-Structure-Visualizations"><a href="#PS-深入的了解各个数据结构的-生成-执行的过程可以参考:Data-Structure-Visualizations" class="headerlink" title="PS:深入的了解各个数据结构的 生成 执行的过程可以参考:Data Structure Visualizations"></a>PS:深入的了解各个数据结构的 生成 执行的过程可以参考:<a href="https://www.cs.usfca.edu/~galles/visualization/Algorithms.html" target="_blank" rel="noopener">Data Structure Visualizations</a></h5>]]></content>
<categories>
<category> 数据库 </category>
</categories>
<tags>
<tag> Mysql </tag>
<tag> 数据库 </tag>
</tags>
</entry>
<entry>
<title>羽毛球比赛数据统计</title>
<link href="/2020/05/24/%E7%BE%BD%E6%AF%9B%E7%90%83/%E6%AF%94%E8%B5%9B%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/"/>
<url>/2020/05/24/%E7%BE%BD%E6%AF%9B%E7%90%83/%E6%AF%94%E8%B5%9B%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h3 id="2020-05-17"><a href="#2020-05-17" class="headerlink" title="2020.05.17"></a>2020.05.17</h3><h5 id="温度-36°C"><a href="#温度-36°C" class="headerlink" title="温度:36°C"></a>温度:36°C</h5><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>23</td><td>21</td></tr><tr><td>王启林</td><td>21</td><td>17</td></tr></tbody></table><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>21</td><td>17</td></tr><tr><td>杨旭光</td><td>16</td><td>21</td></tr></tbody></table><p>总结:天气太热他俩不想动,看不起人呗 /吃瓜</p><h3 id="2020-05-20"><a href="#2020-05-20" class="headerlink" title="2020.05.20"></a>2020.05.20</h3><h5 id="温度-30°C"><a href="#温度-30°C" class="headerlink" title="温度:30°C"></a>温度:30°C</h5><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>21</td><td>17</td><td>13</td></tr><tr><td>王启林</td><td>15</td><td>21</td><td>21</td></tr></tbody></table><p>总结:第一局打的很认真,第二局就开始浪了,第三局因为前面的不认真,而王启林第二局也打出了自信,所以第三局想要认真也很难赢了;每场比赛对手只有一个,就是自己,必须的认真!!!</p><h3 id="2020-05-23"><a href="#2020-05-23" class="headerlink" title="2020.05.23"></a>2020.05.23</h3><h5 id="温度-30°C-1"><a href="#温度-30°C-1" class="headerlink" title="温度:30°C"></a>温度:30°C</h5><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>15</td><td>21</td></tr><tr><td>王启林</td><td>21</td><td>13</td></tr></tbody></table><table><thead><tr><th>单打</th><th>第一局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>10</td></tr><tr><td>张淦</td><td>21</td></tr></tbody></table><p>总结:大部分的失分都是我的主动失误,和张淦打是他的技术比我要高一截,假动作加抓我被动回球就很烦</p><h3 id="2020-05-24"><a href="#2020-05-24" class="headerlink" title="2020.05.24"></a>2020.05.24</h3><h5 id="温度-22°C"><a href="#温度-22°C" class="headerlink" title="温度:22°C"></a>温度:22°C</h5><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th><th>第四局</th><th>第五局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>17</td><td>21</td><td>13</td><td>15</td><td>10</td></tr><tr><td>王启林</td><td>21</td><td>11</td><td>21</td><td>21</td><td>21</td></tr></tbody></table><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th><th>第四局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>20</td><td>21</td><td>21</td><td>18</td></tr><tr><td>杨旭光</td><td>22</td><td>17</td><td>18</td><td>21</td></tr></tbody></table><p>总结:看来果然是天气凉快他俩才舍得跑/gan,我发现我开始的三分基本都是送别人的,失误啊,减少失误我就能赢啊,而且很多分都想着花里胡哨的动作,你可拉倒吧。今天和王打到后面完全就是我心态裂开了,急就会失误,要掌握节奏,节奏!!!</p><h3 id="2020-05-27"><a href="#2020-05-27" class="headerlink" title="2020.05.27"></a>2020.05.27</h3><h4 id="温度-23°C"><a href="#温度-23°C" class="headerlink" title="温度:23°C"></a>温度:23°C</h4><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>17</td><td>21</td><td>22</td></tr><tr><td>王启林</td><td>21</td><td>19</td><td>20</td></tr></tbody></table><p>总结:娱乐局,都没有认真</p><h3 id="2020-05-30"><a href="#2020-05-30" class="headerlink" title="2020.05.30"></a>2020.05.30</h3><h4 id="温度:25°C"><a href="#温度:25°C" class="headerlink" title="温度:25°C"></a>温度:25°C</h4><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>17</td><td>11</td><td>13</td></tr><tr><td>王启林</td><td>21</td><td>11</td><td>21</td></tr></tbody></table><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>15</td><td>10</td><td>9</td></tr><tr><td>张淦</td><td>21</td><td>21</td><td>21</td></tr></tbody></table><p>总结:全败,张淦和王启林这两个我怀疑是戏弄我,每次我觉得我进步了,可以和他们有一战之力了,他们立马就把水品提升一截,菜!!!</p><h3 id="2020-05-31"><a href="#2020-05-31" class="headerlink" title="2020.05.31"></a>2020.05.31</h3><h4 id="温度:26°C"><a href="#温度:26°C" class="headerlink" title="温度:26°C"></a>温度:26°C</h4><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>21</td><td>21</td></tr><tr><td>王启林</td><td>19</td><td>15</td></tr></tbody></table><p>总结:王的状态不怎么好,昨天打的太狠,其实我感觉我打的还可以更好,还有就是我需要注意:不要一味的只有杀球和吊球,这两者是需要高远球来制造更好的机会,打球需要多变性,动脑筋,改变自己的第一反应</p><h3 id="2020-06-03"><a href="#2020-06-03" class="headerlink" title="2020.06.03"></a>2020.06.03</h3><h4 id="温度:23°C"><a href="#温度:23°C" class="headerlink" title="温度:23°C"></a>温度:23°C</h4><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>13</td><td>21</td></tr><tr><td>王启林</td><td>21</td><td>17</td></tr></tbody></table><p>总结:决胜局没达成,一直在打双打</p><h3 id="2020-06-06"><a href="#2020-06-06" class="headerlink" title="2020.06.06"></a>2020.06.06</h3><h4 id="温度:30°C"><a href="#温度:30°C" class="headerlink" title="温度:30°C"></a>温度:30°C</h4><p>总结:因为再练车,让王等了一个小时,心态爆炸,我的错,以后说什么时候到什么时候到!!!</p><h3 id="2020-06-07"><a href="#2020-06-07" class="headerlink" title="2020.06.07"></a>2020.06.07</h3><h4 id="温度:30°C-1"><a href="#温度:30°C-1" class="headerlink" title="温度:30°C"></a>温度:30°C</h4><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>11</td><td>21</td></tr><tr><td>王启林</td><td>21</td><td>13</td></tr></tbody></table><table><thead><tr><th>单打</th><th>第一局</th><th>第二局</th><th>第三局</th></tr></thead><tbody><tr><td>刘虹杰</td><td>13</td><td>21</td><td>15</td></tr><tr><td>张淦</td><td>21</td><td>15</td><td>21</td></tr></tbody></table><p>总结:终于是淦赢了一把张淦,开心。和老王,一胜一负,感觉是后场的反手一直都不稳定,所以被抓的话就算回过去也是被动球,所以后面一直在和老王练后场的反手,有那么点感觉了;今天最重要的是淦赢一把张淦😏<br>和老王的赛后拉升🙈🙈:<br><img alt="enter image description here" data-src="/img/yumaoqiu/WechatIMG62.jpeg" class="lazyload"></p><h3 id="总计"><a href="#总计" class="headerlink" title="总计:"></a>总计:</h3><table><thead><tr><th>对手</th><th>对战次数</th><th>胜-负</th><th>总数</th></tr></thead><tbody><tr><td>王启林</td><td>24</td><td>11-13</td><td>+11</td></tr><tr><td>杨旭光</td><td>6</td><td>3-3</td><td>+3</td></tr><tr><td>张淦</td><td>7</td><td>1-6</td><td>+1</td></tr></tbody></table>]]></content>
<categories>
<category> 羽毛球 </category>
</categories>
<tags>
<tag> 羽毛球 </tag>
</tags>
</entry>
<entry>
<title>Java集合接口:Collection</title>
<link href="/2020/03/10/Java/Java%E9%9B%86%E5%90%88-Collection/"/>
<url>/2020/03/10/Java/Java%E9%9B%86%E5%90%88-Collection/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h3 id="集合接口:Collection"><a href="#集合接口:Collection" class="headerlink" title="集合接口:Collection"></a>集合接口:Collection</h3><p><img alt="enter image description here" data-src="/img/Collection/Collection%E6%8E%A5%E5%8F%A3%E7%BB%A7%E6%89%BF%E6%A0%91.png" class="lazyload"><br>实现子接口:List,Set<br>1.List:<br>1> List接口概述:<br>· 鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组<br>· List集合类中元素有序,且可重复,集合中的每个元素都有其对应的顺序索引<br>· List容器中的元素都对应一个整数性的序号记载其容器中的位置,可以根据序号存取容器的元素<br>· JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector</p><p>2> ArrayList、LinkedList 和 Vector三者的异同:<br>· 同:都是List接口的实现类,存储数据的特点相同,都是元素有序,且可重复的数据<br>· 异:—> ArrayList:作为List接口主要的实现类,线程不安全,效率高,底层使用Object[] elementData存储,查询快,插入和删除较LinkedList慢<br> —> LinkedList: 底层使用双向链表存储,插入和删除快,查询较ArrayList慢<br> —> Vector: 作为List接口的古老实现类,线程安全,效率低(使用synchronized修饰),底层使用Object[] elementData存储</p><p>3> ArrayList源码分析(以JDK 8为例)(查询时间复杂度O(1)常数阶)(删除或插入[中间插入]时间复杂度O(n)线性阶):<br>3.1> 通过new关键字来创建ArrayList:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shared empty array instance used for default sized empty instances. We</span></span><br><span class="line"><span class="comment"> * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when</span></span><br><span class="line"><span class="comment"> * first element is added.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Constructs an empty list with an initial capacity of ten.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; <span class="comment">//创建数组默认 elementData = {}</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>3.2> 调用add()方法添加元素:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Appends the specified element to the end of this list.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> e element to be appended to this list</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> <tt>true</tt> (as specified by {<span class="doctag">@link</span> Collection#add})</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>{</span><br><span class="line"> ensureCapacityInternal(size + <span class="number">1</span>); <span class="comment">// 第一次添加size = 0</span></span><br><span class="line"> elementData[size++] = e;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>调用ensureCapacityInternal()扩容方法,通过私有方法 calculateCapacity()确认数组的长度:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureCapacityInternal</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>{ </span><br><span class="line"> ensureExplicitCapacity(calculateCapacity(elementData, minCapacity) <span class="comment">//明确数组长度 = 10);</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">int</span> <span class="title">calculateCapacity</span><span class="params">(Object[] elementData, <span class="keyword">int</span> minCapacity)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {<span class="comment">// 判断是否是第一次添加 </span></span><br><span class="line"> <span class="keyword">return</span> Math.max(DEFAULT_CAPACITY, minCapacity);<span class="comment">//取最大值 DEFAULT_CAPACITY = 10</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> minCapacity;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Default initial capacity.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_CAPACITY = <span class="number">10</span>; <span class="comment">// 默认长度</span></span><br></pre></td></tr></table></figure><p>调用ensureExplicitCapacity()方法判断是否进行扩容:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureExplicitCapacity</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>{</span><br><span class="line"> modCount++;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// overflow-conscious code</span></span><br><span class="line"> <span class="comment">//如果数组长度还没用完就不进行扩容 第一次 -> minCapacity:10 elementData.length:0 满足条件进行扩容</span></span><br><span class="line"> <span class="keyword">if</span> (minCapacity - elementData.length > <span class="number">0</span>)</span><br><span class="line"> grow(minCapacity);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>调用grow()方法进行扩容:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Increases the capacity to ensure that it can hold at least the</span></span><br><span class="line"><span class="comment"> * number of elements specified by the minimum capacity argument.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> minCapacity the desired minimum capacity</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">grow</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>{</span><br><span class="line"> <span class="comment">// overflow-conscious code</span></span><br><span class="line"> <span class="keyword">int</span> oldCapacity = elementData.length; <span class="comment">//原数组长度</span></span><br><span class="line"> <span class="keyword">int</span> newCapacity = oldCapacity + (oldCapacity >> <span class="number">1</span>); <span class="comment">//新数组长度是原来数组的1.5倍</span></span><br><span class="line"> If (newCapacity - minCapacity < <span class="number">0</span>) <span class="comment">//扩容后新数组长度如果还小于目标数组的长度直接将新数组长度定为目标数组长度</span></span><br><span class="line"> newCapacity = minCapacity;</span><br><span class="line"> <span class="keyword">if</span> (newCapacity - MAX_ARRAY_SIZE > <span class="number">0</span>) <span class="comment">//超过long int最大值 -8 </span></span><br><span class="line">将新数组最大长度设为 <span class="keyword">long</span> <span class="keyword">int</span> 最大值</span><br><span class="line"> newCapacity = hugeCapacity(minCapacity);</span><br><span class="line"> <span class="comment">// minCapacity is usually close to size, so this is a win:</span></span><br><span class="line"> elementData = Arrays.copyOf(elementData, newCapacity); <span class="comment">//创建一个新数组调用System.arraycopy()方法将原数组的元素复制到copy中返回</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>总结:<br>1.当我们在通过”new ArrayList()”调用ArrayList的无参构造来创建数组列表对象时,底层Object[] elementData成员变量 初始化为{},并没有向JDK 7一样初始化长度为10,这样的好处是节约了内存空间成本,类似于单例模式的懒汉式;<br>2.当我们第一次调用”add()”方法时,底层才创建了长度为10的数组,并将数据通过Arrays.copyOf()方法创建一个新的数组,再通过System.arraycopy()将数组元素复制到新数组中返回,添加elementData中<br>3.所以ArrayList底层还是用数组实现,它之所以能够动态扩容是因为底层的扩容机制和复制数组的操作而完成的,这样的它特点也很明显,在查询数组时通过索引直接定位,所以很快,但是在插入元素或添加元素时因为底层的复制数组机制所以很慢,并且还会占用很大内存空间,建议使用带参的构造器对数组进行初始化<br>PS:一个彩蛋,在第一次创建数组时,作者的注释没有改还是写的1.7默认的10</p><p>4> LinkedList源码分析(查询时间复杂度O(n)线性阶)(删除或插入时间复杂度O(1)常数阶)<br>4.1> 通过new关键字创建LinkedList</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">transient</span> <span class="keyword">int</span> size = <span class="number">0</span>; <span class="comment">//链表长度</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Pointer to first node.</span></span><br><span class="line"><span class="comment"> * Invariant: (first == null && last == null) ||</span></span><br><span class="line"><span class="comment"> * (first.prev == null && first.item != null)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">transient</span> Node<E> first; <span class="comment">//第一个节点</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Pointer to last node.</span></span><br><span class="line"><span class="comment"> * Invariant: (first == null && last == null) ||</span></span><br><span class="line"><span class="comment"> * (last.next == null && last.item != null)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">transient</span> Node<E> last; <span class="comment">//最后一个节点</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Constructs an empty list.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LinkedList</span><span class="params">()</span> </span>{ </span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Constructs a list containing the elements of the specified</span></span><br><span class="line"><span class="comment"> * collection, in the order they are returned by the collection's</span></span><br><span class="line"><span class="comment"> * iterator.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> c the collection whose elements are to be placed into this list</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> NullPointerException if the specified collection is null</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">LinkedList</span><span class="params">(Collection<? extends E> c)</span> </span>{ <span class="comment">//有参构造将一个集合中所有的元素插入到LinkedList中,因为LinkedList是线程不安全的所以可能在调用this();的时候已经有其他线程箱里面添加了数据</span></span><br><span class="line"> <span class="keyword">this</span>();</span><br><span class="line"> addAll(c);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>4.2> 节点信息:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span><<span class="title">E</span>> </span>{ <span class="comment">//节点内部类</span></span><br><span class="line"> E item; <span class="comment">//元素</span></span><br><span class="line"> Node<E> next; <span class="comment">//下个节点</span></span><br><span class="line"> Node<E> prev; <span class="comment">//上个节点</span></span><br><span class="line"></span><br><span class="line"> Node(Node<E> prev, E element, Node<E> next) { <span class="comment">//有参构造</span></span><br><span class="line"> <span class="keyword">this</span>.item = element;</span><br><span class="line"> <span class="keyword">this</span>.next = next;</span><br><span class="line"> <span class="keyword">this</span>.prev = prev;</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>4.3> 通过有参构造器来添加节点 -> 调用addAll()方法 源码分析:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">addAll</span><span class="params">(<span class="keyword">int</span> index, Collection<? extends E> c)</span> </span>{</span><br><span class="line"> checkPositionIndex(index); <span class="comment">//校验数组是否越界及参数是否合法</span></span><br><span class="line"></span><br><span class="line"> Object[] a = c.toArray(); <span class="comment">//将集合转为数组</span></span><br><span class="line"> <span class="keyword">int</span> numNew = a.length; <span class="comment">//数组长度</span></span><br><span class="line"> <span class="keyword">if</span> (numNew == <span class="number">0</span>) <span class="comment">//为0直接退出程序</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line"> Node<E> pred, succ; <span class="comment">//创建两个临时节点变量 pred:前置节点 succ:后置节点</span></span><br><span class="line"> <span class="keyword">if</span> (index == size) { <span class="comment">//说明是添加在尾部</span></span><br><span class="line"> succ = <span class="keyword">null</span>; <span class="comment">//后置节点为null</span></span><br><span class="line"> Pred = last; <span class="comment">//前置节点为当前链表的最后一个节点</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> succ = node(index); <span class="comment">//index对用的节点为后置节点</span></span><br><span class="line"> pred = succ.prev; <span class="comment">//记录index对应位置的前置节点</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (Object o : a) { <span class="comment">//遍历</span></span><br><span class="line"> <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>) E e = (E) o; <span class="comment">//指定范型</span></span><br><span class="line"> Node<E> newNode = <span class="keyword">new</span> Node<>(pred, e, <span class="keyword">null</span>); <span class="comment">//当前插入节点初始化 前置,当前节点,后置</span></span><br><span class="line"> <span class="keyword">if</span> (pred == <span class="keyword">null</span>) <span class="comment">//说明链表没有元素</span></span><br><span class="line"> first = newNode; <span class="comment">//直接将 first 指向 当前元素</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> pred.next = newNode; <span class="comment">//记录的前置节点的next指向当前节点</span></span><br><span class="line"> Pred = newNode; <span class="comment">//当前操作完毕后,就将当前的节点赋值给前置临时变量pred</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (succ == <span class="keyword">null</span>) { <span class="comment">//说明没有尾部节点</span></span><br><span class="line"> Last = pred; <span class="comment">//将最后的节点last就为当前节点</span></span><br><span class="line"> } <span class="keyword">else</span> { <span class="comment">//非尾部</span></span><br><span class="line"> pred.next = succ; <span class="comment">//当前节点的next就指向临时变量succ 也就是index的节点</span></span><br><span class="line"> succ.prev = pred; <span class="comment">//处理将index的节点的前节点指向当前节点</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Size += numNew; <span class="comment">//每成功插入一个节点信息链表长度加1</span></span><br><span class="line"> modCount++; <span class="comment">//修改次数添加</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>4.4> 当我们通过add()方法添加(默认是添加在尾部):</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">linkLast</span><span class="params">(E e)</span> </span>{</span><br><span class="line"> <span class="keyword">final</span> Node<E> l = last; <span class="comment">//记录最后一个节点信息</span></span><br><span class="line"> <span class="keyword">final</span> Node<E> newNode = <span class="keyword">new</span> Node<>(l, e, <span class="keyword">null</span>); <span class="comment">//创建当前节点</span></span><br><span class="line"> Last = newNode; <span class="comment">//将当前节点变为链表的最后节点</span></span><br><span class="line"> If (l == <span class="keyword">null</span>) <span class="comment">//说明当前链表为空</span></span><br><span class="line"> first = newNode; <span class="comment">//头节点也是当前节点</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> l.next = newNode; <span class="comment">//不为空,将前一个的最后节点的next指向当前节点</span></span><br><span class="line"> size++; <span class="comment">//链表长度加1</span></span><br><span class="line"> modCount++; <span class="comment">//修改次数增加</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>4.5> 总结:<br>1.从源码来分析,双向链表在处理数据的相关操作时主要其实就是在维护各个节点之间的指针关系,每个节点都维护了上个节点和下个节点的信息,这样的好处就是能够在插入或删除时更加方便的处理节点之间的关系,缺点也很明显,就是需要维护指针带来的空间损耗问题<br>2.对比ArrayList,LinkedList他不需要考虑扩容及复制操作的性能损耗,在处理删除和插入时也是非常的方便,只需要修改节点指针即可;但是在查询指定索引的中间元素时,因为需要根据头节点开始向后查询,虽然也做了优化,但是对比ArrayList还会慢了些<br>3.在需要频繁插入,删除数据时用LinkedList,在需要频繁查询数据时用ArrayList</p><p>5> Vector分析:<br>1.和ArrayList一样使用数组形式存储数据<br>2.较ArrayList,它是线程安全的<br>3.扩容机制和ArrayList不一样,ArrayList是原数组的1.5倍,它是2倍<br>4.在初始化时,会直接创建长度为10的数组,和JDK 1.8 的ArrayList不同<br>5.基本上已经不用它了,ArrayList在处理多线程安全的情况时,可以用Collections工具类synchronizedList(List<t> list)方法将list放进去就变成线程安全的了</t></p><p>2.Set:<br>1> Set接口概述:存储无序的,不可重复的数据<br>2> HashSet、LinkedHashSet和TreeSet的异同:<br>a> 同:首先他们都是实现与Set集合接口的,所以Set具备的特点也就是他们的同<br>b> 异:<br>->HashSet:首先HashSet是作为Set接口的主要实现类,线程不安全,可以存储null值;<br>-> LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历<br>-> TreeSet:可以按照添加对象的属性进行排序</p><p>3> HashSet:<br>3.1> HashSet添加元素的过程:<br><img alt="enter image description here" data-src="/img/Collection/HashSet%E6%B7%BB%E5%8A%A0%E5%85%83%E7%B4%A0%E7%BB%93%E6%9E%84%E7%A4%BA%E6%84%8F%E5%9B%BE.png" class="lazyload"><br>1.当我们向HashSet中添加元素a时,首先元素a会调用所在类中的HashCode方法计算Hash值,再通过其计算出了的Hash值通过算法(散链函数)计算出的结果就决定了存在HashSet底层数组的存放位置(即索引),判断该位置是否存在元素:<br>1.1> 不存在直接将元素a放在该位置上<br>1.2> 存在,即比较该位置 或 该位置以下存在的链表各个元素的Hash值是否相同<br>1.2.1> 不相同则直接放在链表的最下面<br>1.2.2> 相同则调用元素a所在类的equals()方法<br>1.2.2.1> 返回false,则将元素以链表的方式添加在链表尾部<br>1.2.2.2> 返回true,则代表该元素已经存在,添加失败<br>注意:<br>JDK 7:当元素以链表的方式存储时 -> 新添加元素会存放于数组中即链表的首部<br>JDK 8:当元素以链表的方式存储时 -> 新添加的元素会放在链表的尾部<br>自己的理解:<br>JDK 7 之所以将新添加的元素放在头部的原因我理解是因为站在一个”大概率查询”的基础上,简单来说就是,我判定你新添加的元素即是你很快就将用到的元素,所以我把它放在首部,你将要用到它的时候,就不用再去查询链表了,节约了查询的时间;<br>但是这样也有一个大的弊端,就是需要维护指针的指向问题,新的元素放在首部,那后边的所有指针都需要改变指向的节点;并且你在判断新的元素是否可以添加成功过程中(以链表的形式为例)其实已经对链表做过一次遍历了,这又是一次时间成本的开销;<br>所以JDK 1.8 做了优化,即将新的元素放在链表的尾部,这样就是一次遍历 + 维护一个节点指针 的成本<br>3.2> 要求:<br>1.我们在向Set集合中添加元素时,一定要重写hashCode()和equals()方法<br>2.重写的两个方法尽量保持一致性:相等的对象必须具有相等的散列码 -><br>推荐直接利用开发工具生成这两个方法即可</p><p>4> LinkedHashSet:<br>4.1> LinkedHashSet概述:<br><img alt="enter image description here" data-src="/img/Collection/LinkedHashSet%E7%BB%93%E6%9E%84%E5%9B%BE.png" class="lazyload"><br>1.是HashSet的子类<br>2.遍历元素时可以按照添加的顺序返回,注意不能因为返回的顺序一样就误解它是有序的,它还是无序的,它无序的定义是说它在添加元素时不是按照索引的顺序进行添加,之所以返回的顺序是按添加的顺序是因为在其内部增加了两个指针引用变量,指向前置节点指针和指向后置节点指针;<br>3.对于频繁的遍历操作,LinedHashSet效率优于HashSet</p><p>5>TreeSet:<br>5.1>TreeSet概述:<br><img alt="enter image description here" data-src="/img/Collection/TreeSet%E7%BB%93%E6%9E%84%E5%9B%BE.png" class="lazyload"><br>1.TreeSet在添加元素时要求是相同类对象<br>2.有序,查询速度比List快<br>3.返回对象是经过排序的 -> 也就是说在添加元素时就已经将元素排好了序<br>4.两种排序方式:自然排序 和 定制排序<br>4.1> 自然排序:通过实现 Comparable 接口对指定属性进行排序,注意的是,在比较两个对象是否相同的标准为:compareTo()返回0,不再是equals();<br>4.2> 定制排序:创建一个比较器 comparator;定制排序的规则,调用TreeSet有参构造将comparatot放进去,默认会调用定制排序;在比较两个对象是否相同的标准为:compare()返回0,不再是equals();</p><p>2.Map:<br>1> Map接口概述:双列数据,存储key-value对的数据结构</p><p>2> Map实现接口的子接口有哪些?<br>-> HashMap:作为HashMap的主要实现类,线程不安全,效率较高;key - value 可以存null值,但是key不可重复<br>-> LinkedHashMap:作为HashMap的子类,实现Map接口;在HashMap的基础上为每个Entry<K,V>对象添加了”before”, “after”这两个指针;主要是为了在频繁的遍历时能够更快的定位元素,执行效率在HashMap之上<br>-> TreeMap:添加按照 key - value 对的形式进行排序,实现排序遍历;需要考虑key的自然排序或定制排序,底层使用红黑树结构<br>-> Hashtable:古老实现类,线程安全,效率低;不能存储 null 的 key 和 value </p><p>3> 结构理解:<br>-> Map 中的 key:无序,不可重复,使用Set结构进行存储所有的key,key所在的类需要重写HashCode() 和 equals() (以HashMap为例)<br>-> Map 中的 value:无序,可重复,使用Collection存储所有的value,value所在的类需要重写equals()方法<br>-> 一个键值对构成一个Entry对象<br>-> Map 中的 Entry:无序的,不可重复的,使用Set存储所有的Entry</p><p>4> HashMap:<br>4.1> HashMap的底层源码即原理分析:<br>-> JDK 8之前:当使用new关键字创建HashMap时,底层会初始化一个长度为16的Entry数组,我们通过put(key,value)向HashMap中添加元素时,首先会调用key所在类的hashCode方法计算hash值,再通过散列函数计算出将要添加的元素在数组中的位置<br>1.如果计算出的索引位置为空,则直接添加成功<br>2.该位置上已有元素:<br>2.1 将要添加的元素和该位置已存在的元素的hashCode码进行一一比较<br>2.1.2 如果不相同则将元素添加成功<br>2.1.3 相同则调用该类的equlas()方法进行比较<br>2.1.3.4 如果equals()返回为false,元素添加成功<br>2.1.3.5 如果equals()返回为true,新元素的value 覆盖 旧元素的value,key不变<br>3.在不断添加的过程中,会涉及到扩容的问题,当超过临界值并且将要加入的元素索引位置不为空(即该索引位置已经有元素)的情况下,默认扩容为原来容量的2倍,并将原有的数组数据复制过来<br>-> JDK 8:<br>和1.7比较底层实现不同的是:<br>1.在通过 new 关键字创建HashMap时,底层是没有自动初始化长度为16的数组,而是在第一次添加元素的时候对数组长度进行初始化 -> aka16<br>2.JDK 7 是使用数组加链表的结构存储元素,但在JDK 8,HashMap是通过数组加链表(单链表)加红黑树的结构进行数组存储,目的在于更加高效的定位元素;当数组的某一索引位置上的元素以链表的形式存在并且节点个数 > 8,并且当前数组长度 > 64时,此时,该索引位置上的所有元素以红黑树的形式进行存储<br>3.JDK 8 HashMap将存储<key,value>的节点Entry[]对象,换成了Node[]</p><p>4.2> JDK 8 HashMap源码分析<br>当我们使用 new 关键字 创建HashMap:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Constructs an empty <tt>HashMap</tt> with the default initial capacity</span></span><br><span class="line"><span class="comment"> * (16) and the default load factor (0.75).</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">HashMap</span><span class="params">()</span> </span>{ <span class="comment">//调用无参构造器</span></span><br><span class="line"> <span class="keyword">this</span>.loadFactor = DEFAULT_LOAD_FACTOR; <span class="comment">//负载因子初始化为0.75</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>进行 put() 元素时:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>{ </span><br><span class="line"> <span class="comment">//调用putVal方法,hash(key) -> 调用hashCode方法 计算hash值</span></span><br><span class="line"> <span class="keyword">return</span> putVal(hash(key), key, value, <span class="keyword">false</span>, <span class="keyword">true</span>); </span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">boolean</span> onlyIfAbsent,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">boolean</span> evict)</span> </span>{</span><br><span class="line"> Node<K,V>[] tab; Node<K,V> p; <span class="keyword">int</span> n, I;</span><br><span class="line"> <span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>) <span class="comment">//判断数组长度是否为空</span></span><br><span class="line"> n = (tab = resize()).length; <span class="comment">//调用resize()方法对数组长度进行初始化</span></span><br><span class="line"> <span class="keyword">if</span> ((p = tab[I = (n - <span class="number">1</span>) & hash]) == <span class="keyword">null</span>) <span class="comment">//判断该索引位置的是否为空</span></span><br><span class="line"> tab[I] = newNode(hash, key, value, <span class="keyword">null</span>); <span class="comment">//为空则直接添加新元素</span></span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">//不为空</span></span><br><span class="line"> Node<K,V> e; K k; <span class="comment">//e:新临时节点信息 k:新临时节点信息中元素的 key</span></span><br><span class="line"> <span class="keyword">if</span> (p.hash == hash &&</span><br><span class="line"> ((k = p.key) == key || (key != <span class="keyword">null</span> && key.equals(k)))) <span class="comment">//判断当前的数组索引位置 与 新节点的 hash值 和 key 是否相等</span></span><br><span class="line"> e = p; <span class="comment">//相等 将原节点的信息赋值给新的临时节点</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (p <span class="keyword">instanceof</span> TreeNode) <span class="comment">//判断原节点是否已是红黑树结构</span></span><br><span class="line"> e = ((TreeNode<K,V>)p).putTreeVal(<span class="keyword">this</span>, tab, hash, key, value);</span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">//链表形式</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> binCount = <span class="number">0</span>; ; ++binCount) { <span class="comment">//链表形式的情况 遍历链表</span></span><br><span class="line"> <span class="keyword">if</span> ((e = p.next) == <span class="keyword">null</span>) { <span class="comment">//找到链表的尾部</span></span><br><span class="line"> p.next = newNode(hash, key, value, <span class="keyword">null</span>); <span class="comment">//将原链表的最后一个节点指向新的节点</span></span><br><span class="line"> <span class="comment">//判断链表的长度是否超过8(TREEIFY_THRESHOLD:链表长度超过该默认值扩展成为红黑树),-1是因为从0开始计算长度的</span></span><br><span class="line"> <span class="keyword">if</span> (binCount >= TREEIFY_THRESHOLD - <span class="number">1</span>) <span class="comment">// -1 for 1st</span></span><br><span class="line"> treeifyBin(tab, hash); <span class="comment">//变成红黑树</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (e.hash == hash &&</span><br><span class="line"> ((k = e.key) == key || (key != <span class="keyword">null</span> && key.equals(k)))) <span class="comment">//该数组以链表形式存在 如果找到了有节点的 hash值 和 key 相等的情况直接break;</span></span><br><span class="line"> <span class="keyword">break</span>; </span><br><span class="line"> p = e; <span class="comment">//如果当前节点 和 新的节点 不一样 则将原节点赋值给p 继续找p(当前节点)的下个节点元素</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//e(新临时节点信息)不为null 说明是需要覆盖值的操作</span></span><br><span class="line"> <span class="keyword">if</span> (e != <span class="keyword">null</span>) { <span class="comment">// existing mapping for key</span></span><br><span class="line"> V oldValue = e.value; <span class="comment">//新节点value覆盖原节点的value,此时 oldValue 是原节点的 value,可以打印覆盖值的那段代码,可以看到被覆盖的值(也就是原节点的value)</span></span><br><span class="line"> <span class="keyword">if</span> (!onlyIfAbsent || oldValue == <span class="keyword">null</span>) <span class="comment">//这里分两种情况 1.调用的是put()方法时,一定会进到下面进行覆盖原节点value的操作并返回被覆盖的value 2.在调用putIfAbsent()时,会发生两种情况 1> 直接返回就原节点的value 不会进行 新节点的value覆盖操作 2> 如果原节点的value值为null时,还是会新节点的value 覆盖 null 的操作 </span></span><br><span class="line"> e.value = value; <span class="comment">//新节点的 value 覆盖 原节点的 value</span></span><br><span class="line"> afterNodeAccess(e); <span class="comment">//代码可扩展</span></span><br><span class="line"> <span class="keyword">return</span> oldValue; <span class="comment">//因为是节点已经存在的情况 所以直接return 不用进行下面的操作</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ++modCount; <span class="comment">//用于迭代器遍历时如果产生了意外的修改的操作时(即在多线程情况下,遍历元素的时候,有线程对原HashMap进行了修改操作)直接抛出异常的快速失败机制(Fail-Fast)</span></span><br><span class="line"> <span class="keyword">if</span> (++size > threshold) <span class="comment">//判断 当前的HashMap size+1 是否大于 临界阀值</span></span><br><span class="line"> resize(); <span class="comment">//进行扩容操作</span></span><br><span class="line"> afterNodeInsertion(evict); <span class="comment">//代码可扩展</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>; <span class="comment">//添加完毕</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">final</span> Node<K,V>[] resize() { <span class="comment">//数组长度扩容机制方法</span></span><br><span class="line"> Node<K,V>[] oldTab = table; <span class="comment">//原数组</span></span><br><span class="line"> <span class="keyword">int</span> oldCap = (oldTab == <span class="keyword">null</span>) ? <span class="number">0</span> : oldTab.length; <span class="comment">//原数组长度</span></span><br><span class="line"> <span class="keyword">int</span> oldThr = threshold; <span class="comment">//原数组的临界阀值 -> 决定是否进行扩容</span></span><br><span class="line"> <span class="keyword">int</span> newCap, newThr = <span class="number">0</span>; <span class="comment">//定义新的数组长度 及 新的临界阀值</span></span><br><span class="line"> <span class="keyword">if</span> (oldCap > <span class="number">0</span>) { <span class="comment">//说明原数组已存放元素</span></span><br><span class="line"> <span class="keyword">if</span> (oldCap >= MAXIMUM_CAPACITY) {</span><br><span class="line"> threshold = Integer.MAX_VALUE;</span><br><span class="line"> <span class="keyword">return</span> oldTab;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> ((newCap = oldCap << <span class="number">1</span>) < MAXIMUM_CAPACITY &&</span><br><span class="line"> oldCap >= DEFAULT_INITIAL_CAPACITY)</span><br><span class="line"> newThr = oldThr << <span class="number">1</span>; <span class="comment">// double threshold</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (oldThr > <span class="number">0</span>) <span class="comment">// initial capacity was placed in threshold</span></span><br><span class="line"> newCap = oldThr;</span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">// zero initial threshold signifies using defaults</span></span><br><span class="line"> newCap = DEFAULT_INITIAL_CAPACITY; <span class="comment">//新数组长度初始化为16</span></span><br><span class="line"> newThr = (<span class="keyword">int</span>)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); <span class="comment">//新数组临界阀值为 12 = 16 * 0.75(负载因子)</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (newThr == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">float</span> ft = (<span class="keyword">float</span>)newCap * loadFactor;</span><br><span class="line"> newThr = (newCap < MAXIMUM_CAPACITY && ft < (<span class="keyword">float</span>)MAXIMUM_CAPACITY ?</span><br><span class="line"> (<span class="keyword">int</span>)ft : Integer.MAX_VALUE);</span><br><span class="line"> }</span><br><span class="line"> threshold = newThr; <span class="comment">//新的临界阀值 就为 原数组的临界阀值</span></span><br><span class="line"> <span class="meta">@SuppressWarnings</span>({<span class="string">"rawtypes"</span>,<span class="string">"unchecked"</span>})</span><br><span class="line"> Node<K,V>[] newTab = (Node<K,V>[])<span class="keyword">new</span> Node[newCap]; <span class="comment">//创建一个长度为16的新数组</span></span><br><span class="line"> table = newTab; <span class="comment">//经过相关操作的新数组 就为 原数组</span></span><br><span class="line"> <span class="keyword">if</span> (oldTab != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < oldCap; ++j) {</span><br><span class="line"> Node<K,V> e;</span><br><span class="line"> <span class="keyword">if</span> ((e = oldTab[j]) != <span class="keyword">null</span>) {</span><br><span class="line"> oldTab[j] = <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (e.next == <span class="keyword">null</span>)</span><br><span class="line"> newTab[e.hash & (newCap - <span class="number">1</span>)] = e;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (e <span class="keyword">instanceof</span> TreeNode)</span><br><span class="line"> ((TreeNode<K,V>)e).split(<span class="keyword">this</span>, newTab, j, oldCap);</span><br><span class="line"> <span class="keyword">else</span> { <span class="comment">// preserve order</span></span><br><span class="line"> Node<K,V> loHead = <span class="keyword">null</span>, loTail = <span class="keyword">null</span>;</span><br><span class="line"> Node<K,V> hiHead = <span class="keyword">null</span>, hiTail = <span class="keyword">null</span>;</span><br><span class="line"> Node<K,V> next;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> next = e.next;</span><br><span class="line"> <span class="keyword">if</span> ((e.hash & oldCap) == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (loTail == <span class="keyword">null</span>)</span><br><span class="line"> loHead = e;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> loTail.next = e;</span><br><span class="line"> loTail = e;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (hiTail == <span class="keyword">null</span>)</span><br><span class="line"> hiHead = e;</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> hiTail.next = e;</span><br><span class="line"> hiTail = e;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">while</span> ((e = next) != <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">if</span> (loTail != <span class="keyword">null</span>) {</span><br><span class="line"> loTail.next = <span class="keyword">null</span>;</span><br><span class="line"> newTab[j] = loHead;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (hiTail != <span class="keyword">null</span>) {</span><br><span class="line"> hiTail.next = <span class="keyword">null</span>;</span><br><span class="line"> newTab[j + oldCap] = hiHead;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> newTab; <span class="comment">//返回新数组</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> Java </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 集合 </tag>
</tags>
</entry>
<entry>
<title>Java数据机构-记手写的一个简单链表结构</title>
<link href="/2020/02/06/Java/%E8%AE%B0%E6%89%8B%E5%86%99%E7%9A%84%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E9%93%BE%E8%A1%A8%E7%BB%93%E6%9E%84/"/>
<url>/2020/02/06/Java/%E8%AE%B0%E6%89%8B%E5%86%99%E7%9A%84%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E9%93%BE%E8%A1%A8%E7%BB%93%E6%9E%84/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="Java数据机构-记·手写的一个简单链表结构"><a href="#Java数据机构-记·手写的一个简单链表结构" class="headerlink" title="Java数据机构-记·手写的一个简单链表结构"></a>Java数据机构-记·手写的一个简单链表结构</h2><h5 id="链表的特点:"><a href="#链表的特点:" class="headerlink" title="链表的特点:"></a>链表的特点:</h5><blockquote><p>链表是以分散的形式存储在内存中的,在查询元素时只能通过上个元素中存储的下个元素的信息进行查询。也就是说,链表不支持随机访问,要想查询某个元素,只能从头节点开始遍历。时间复杂度相较于数组的随机访问会增大;</p></blockquote><h5 id="详细的可参照:Java数据机构-链表特性"><a href="#详细的可参照:Java数据机构-链表特性" class="headerlink" title="详细的可参照:Java数据机构-链表特性"></a>详细的可参照:<a href="http://www.rainbow1218.com/2019/11/10/Java/Java%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E9%9B%86%E5%90%88%E7%9B%B8%E5%85%B3/">Java数据机构-链表特性</a></h5><p>#####单向链表:<br>1.首先得定义Node节点及相关结构</p><blockquote><p>1)节点有两个属性(因为实现的是一个单向链表结构所以只定两个属性):</p><blockquote><p>a>当前节点的value<br>b>下个节点</p></blockquote></blockquote><p>2.链表的实现类:<br>为了方便能够访问私有属性,所以Node是LinkedList(实现类)的内部类;<br>链表结构很像“火车“,每节车厢对应的是每个Node节点,而连接每节车厢的那个链条就是我们需要处理的引用传递,而如果我们想要这个火车跑起来那必须得有个火车头,那么我们就需在定义个头节点和链表的size(火车一共有多少节车厢);</p><blockquote><p>1.定义链表类两个属性</p><blockquote><p>a>头节点<br>b>size</p></blockquote></blockquote><p>结构如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LinkedList</span><<span class="title">T</span>> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> size;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Node head; <span class="comment">//头节点</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getSize</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> size;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span><<span class="title">T</span>> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> T value; <span class="comment">//元素</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Node next; <span class="comment">//下个节点</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Node</span><span class="params">(T value)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.value = value;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">``` </span><br><span class="line"><span class="number">2</span>.实现链表相关操作:</span><br><span class="line"></span><br><span class="line">a> 先定一个校验index和链表长度是否有效的私有方法:</span><br><span class="line">```java</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 校验参数的合法性</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> index 索引</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">checkout</span><span class="params">(<span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (size < <span class="number">0</span> || size < index) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ArrayIndexOutOfBoundsException(<span class="string">"数组越界异常"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>b> 添加头节点:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加入头节点</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value 元素</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addHead</span><span class="params">(T value)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (value == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Node<T> newNode = <span class="keyword">new</span> Node<>(value);</span><br><span class="line"></span><br><span class="line"> Node node = head;</span><br><span class="line"></span><br><span class="line"><span class="comment">//头节点为null直接加入</span></span><br><span class="line"> <span class="keyword">if</span> (node == <span class="keyword">null</span>) {</span><br><span class="line"> head = newNode;</span><br><span class="line"> size++;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//将新节点的的下个节点指向头节点</span></span><br><span class="line"> newNode.next = head;</span><br><span class="line"> head = newNode;</span><br><span class="line"> size++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>c> 添加末节点:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加入未节点</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value 元素</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addEnd</span><span class="params">(T value)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (value == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> Node<T> newNode = <span class="keyword">new</span> Node<>(value);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (head == <span class="keyword">null</span>) {</span><br><span class="line"> head = newNode;</span><br><span class="line"> size++;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//根据头节点开始向后找未节点</span></span><br><span class="line"> Node<T> prev = <span class="keyword">null</span>;<span class="comment">//前节点</span></span><br><span class="line"> Node<T> next = head;<span class="comment">//当前节点</span></span><br><span class="line"> <span class="keyword">while</span> (next != <span class="keyword">null</span>) {</span><br><span class="line"> prev = next;</span><br><span class="line"> next = next.next;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//next == null 说明 prev就是未节点 将prev.next指向新节点</span></span><br><span class="line"> prev.next = newNode;</span><br><span class="line"> size++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>d> 插入指定的元素:</p><blockquote><p>实现思路:<br>1.首先查找到指定索引的Node节点<br>2.然后将 新节点的下个节点指向前节点的下个节点 ,再将 前节点的下一个节点指向新节点</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 插入指定元素</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value 元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> index 指定的索引</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">targetNode</span><span class="params">(T value, <span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"><span class="comment">//检验参数合法性</span></span><br><span class="line"> <span class="keyword">this</span>.checkout(index);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (index == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">this</span>.addHead(value);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (index == size) {</span><br><span class="line"> <span class="keyword">this</span>.addEnd(value);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> Node<T> newNode = <span class="keyword">new</span> Node<>(value);</span><br><span class="line"> Node prev = <span class="keyword">null</span>; <span class="comment">//前节点</span></span><br><span class="line"> Node next = head.next; <span class="comment">//当前节点</span></span><br><span class="line"> <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (count < index) {</span><br><span class="line"> prev = next;</span><br><span class="line"> next = next.next;</span><br><span class="line"> }</span><br><span class="line"> prev.next = newNode; <span class="comment">//前节点下个几点指向新节点</span></span><br><span class="line"> newNode.next = next; <span class="comment">//新节点下个节点指向当前节点的下个节点</span></span><br><span class="line"> size++;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>e> 根据指定的索引删除:</p><blockquote><p>实现思路:<br>1.根据索引查找对用节点<br>2.将 该节点 的上个节点指向 该节点 的下个节点<br>3.将 该节点 的 下个节点 指为 null </p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 根据指定的 Index 删除节点</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> index</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> ture 成功</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Boolean <span class="title">removeIndex</span><span class="params">(<span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.checkout(index);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (index == <span class="number">1</span>) {</span><br><span class="line"> Node node = head;</span><br><span class="line"> head = node.next;</span><br><span class="line"> size--;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"></span><br><span class="line"> Node prev = <span class="keyword">null</span>; <span class="comment">//上节点</span></span><br><span class="line"> Node next = head; <span class="comment">//该节点</span></span><br><span class="line"> <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (index <= size) {</span><br><span class="line"> <span class="keyword">if</span> (count == index) {</span><br><span class="line"> prev.next = next.next;</span><br><span class="line"> next.next = <span class="keyword">null</span>;</span><br><span class="line"> size--;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> prev = next;</span><br><span class="line"> next = next.next;</span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>f> 根据指定值删除</p><blockquote><p>实现思路:<br>1.根据 value 值 查找 对应 索引<br>2.调用 removeIndex 方法 删除节点</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 遍历 value 获取 Index 传入 removeIndex 中</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value 元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> true: 成功</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Boolean <span class="title">removeNode</span><span class="params">(T value)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (value == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Node prev = head;</span><br><span class="line"> <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (prev != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">if</span> (prev.value.equals(value)) {</span><br><span class="line"> <span class="keyword">this</span>.removeIndex(count);</span><br><span class="line"> }</span><br><span class="line"> prev = prev.next;</span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>g> 链表反转(这个就有点意思了)</p><blockquote><p>实现思路:<br>1.首先的理清思路,既然是链表全部都反转那么就是只需要将 当前节点的后节点指向前节点就ok了;<br>2.所以我们就需要 获取的 当前节点的前节点 当前节点 当前节点的后节点, 再将 当前 引用关系 反转;<br>3.最后再将 未节点 变成 头节点 ;</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 链表反转</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reverse</span><span class="params">()</span> </span>{</span><br><span class="line"> Node prev = <span class="keyword">null</span>; <span class="comment">//前节点</span></span><br><span class="line"> Node cur = head; <span class="comment">//当前节点</span></span><br><span class="line"> Node next; <span class="comment">// 当前节点后节点</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (cur != <span class="keyword">null</span>) {</span><br><span class="line"> next = cur.next;</span><br><span class="line"> cur.next = prev;</span><br><span class="line"> prev = cur;</span><br><span class="line"> cur = next;</span><br><span class="line"> }</span><br><span class="line"> head = prev;</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>h>将指定的元素 和 指定的索引元素 互换(也蛮有意思)</p><blockquote><p>实现思路:<br>1.首先根据元素找到 该元素 及 该元素的前节点<br>2.再根据索引找到 该元素 及 该元素的前节点<br>3.如果是 头节点 就将 head 设为 将要交换的节点</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将指定的元素 和 指定的索引元素 互换</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> value 元素</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> index 指定位置</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">targetReverse</span><span class="params">(T value, <span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.checkout(index);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (value == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//1.获取指定节点的元素前节点</span></span><br><span class="line"> Node prev = <span class="keyword">null</span>; <span class="comment">//前节点</span></span><br><span class="line"> Node cur = head; <span class="comment">//当前节点</span></span><br><span class="line"> <span class="keyword">while</span> (cur != <span class="keyword">null</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (cur.value.equals(value)) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> prev = cur;</span><br><span class="line"> cur = cur.next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//2.获取指定索引的节点元素的前节点</span></span><br><span class="line"> Node prev1 = head;<span class="comment">//前元素</span></span><br><span class="line"> Node cur1 = prev1.next;<span class="comment">//当前元素</span></span><br><span class="line"> Node next;<span class="comment">//下个元素</span></span><br><span class="line"> <span class="keyword">int</span> count = index == <span class="number">1</span> ? <span class="number">0</span> : <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (count <= size) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (count == <span class="number">0</span>) {</span><br><span class="line"> prev1 = <span class="keyword">null</span>;</span><br><span class="line"> cur1 = head;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (count == index - <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> prev1 = prev1.next; <span class="comment">//index 指定元素的上个元素</span></span><br><span class="line"> cur1 = cur1.next;<span class="comment">//index 指定的元素</span></span><br><span class="line"></span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//两个 相同元素,相同index 直接return</span></span><br><span class="line"> <span class="keyword">if</span> (cur.value.equals(cur1)) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//处理头节点 或 未节点的情况</span></span><br><span class="line"> <span class="comment">//处理前节点</span></span><br><span class="line"> <span class="keyword">if</span> (prev == <span class="keyword">null</span>) { <span class="comment">//说明是头节点</span></span><br><span class="line"> head = cur1;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> prev.next = cur1;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (prev1 == <span class="keyword">null</span>) {</span><br><span class="line"> head = cur;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> prev1.next = cur;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//处理后节点</span></span><br><span class="line"> next = cur.next;<span class="comment">//index指定元素的后节点</span></span><br><span class="line"> cur.next = cur1.next;</span><br><span class="line"> cur1.next = next;</span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>i>根据index获取Node元素</p><blockquote><p>实现逻辑:<br>1.控制变量 count 获取指定 index 的 Node节点信息 </p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> index 索引</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 对应的元素</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">getNode</span><span class="params">(<span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.checkout(index);</span><br><span class="line"></span><br><span class="line"> Node node = head;</span><br><span class="line"> <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (count < index) {</span><br><span class="line"> node = node.next;</span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (T) node.value;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>3.总结:</p><blockquote><p>1.缺点:单链表相关实现上比较单一且清晰,但是在进行查询元素的时候因为没有前驱节点的信息无法做相关的优化处理 及 在 交换 元素的时候,也会多一些实现的逻辑,代码就显得不够优美,在性能上较双向链表也会显得不足;<br>2.优点:如上面所说如果每个节点都存有上个节点的信息,那么必然在内存空间利用上就劣与单链表;</p></blockquote><p>PS:<br>最近疫情爆发,为了能更加有效的控制住疫情的进一步扩散,全国人民都在家进行自我隔离。但是日常生活的必须品还是得买,所以前面我就在考虑做我们小区的志愿者,帮大家把生活的必须品买完再送给大家,目的一是可以完成我的目标,目的二可以在外面活动活动,但我看现在正处于关键的时期,就算我买了大家估计也都不敢开门拿,而且我马上也得回成都了。算了,就不给国家和大家添乱了。<br>现在大家都是在家憋坏的状态,但是想想那些医生,护士,还有军人,那些“战斗“在一线的人们,我们嫌弃无聊的地方正是他们回不去的地方!<br>我能做的只有尽量多学一些知识,多看一些学习视频,丰富自己,提升自己,完成自己对自己的规划。为能在以后的工作中运用到,我一直觉得我这种普通人没办法帮助祖国做什么大事,所以我只能让自己多有价值一些,不给国家添乱,先活好自己,再去谈其他的吧。</p>]]></content>
<categories>
<category> Java </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 数据结构 </tag>
</tags>
</entry>
<entry>
<title>大数据存储数据优化</title>
<link href="/2019/11/21/%E6%95%B0%E6%8D%AE%E5%BA%93/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8%E4%BC%98%E5%8C%96/"/>
<url>/2019/11/21/%E6%95%B0%E6%8D%AE%E5%BA%93/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8%E4%BC%98%E5%8C%96/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="大数据存储数据优化"><a href="#大数据存储数据优化" class="headerlink" title="大数据存储数据优化"></a>大数据存储数据优化</h2><p>接着前面搭的项目继续今天得内容,现在项目结构如下:</p><p><img alt="enter image description here" data-src="/img/DSO/9B9CCB21-702C-4823-A837-326A857D10EB.png" class="lazyload"></p><p>新增了网关服务和集成了SpringData JPA组件。今天就先暂用Order服务来测试数据;</p><h4 id="准备环境"><a href="#准备环境" class="headerlink" title="准备环境"></a>准备环境</h4><p>服务名称:orderService(订单服务),zuulService(网关服务)</p><h5 id="orderService"><a href="#orderService" class="headerlink" title="orderService"></a>orderService</h5><p>pom.xml:</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependencies</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.cloud<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-cloud-starter-netflix-eureka-client<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!--springboot 热部署--></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-devtools<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-jdbc<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.alibaba<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>druid<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.0.18<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>mysql<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mysql-connector-java<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!--测试环境--></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-test<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!--JPA--></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-data-jpa<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- lombok --></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.projectlombok<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>lombok<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.16.10<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-test<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclusions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclusion</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.junit.vintage<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>junit-vintage-engine<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">exclusion</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">exclusions</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-web<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.2.1.RELEASE<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>compile<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>junit<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>junit<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.apache.curator<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>curator-framework<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.6.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependencies</span>></span></span><br></pre></td></tr></table></figure><p>其中 springboot 热部署 和 lombok 可以节约很多调试和开发时间,强烈推荐!!!</p><p>application.properties配置如下:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">server.port=8082</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 应用名称</span></span><br><span class="line"><span class="string">spring.application.name=order</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># eureka服务端地址</span></span><br><span class="line"><span class="string">eureka.client.service-url.defaultZone=http://localhost:8761/eureka/</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置默认数据源,并使用 阿里连接池</span></span><br><span class="line"><span class="string">spring.datasource.type=com.alibaba.druid.pool.DruidDataSource</span></span><br><span class="line"><span class="comment"># 注意 rewriteBatchedStatements=true 这个参数是开启批处理 很重要!!! 测试1W条数据没他许需要4分钟 开启只需要762ms 需要搭配JdbcTemplate使用</span></span><br><span class="line"><span class="string">spring.datasource.url=jdbc:mysql://localhost:3306/spring_data?serverTimezone=UTC&rewriteBatchedStatements=true</span></span><br><span class="line"><span class="string">spring.datasource.username=root</span></span><br><span class="line"><span class="string">spring.datasource.password=root</span></span><br><span class="line"><span class="string">spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="string">spring.datasource.max-active=20</span></span><br><span class="line"><span class="string">spring.datasource.max-idle=8</span></span><br><span class="line"><span class="string">spring.datasource.min-idle=8</span></span><br><span class="line"><span class="string">spring.datasource.initial-size=10</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">######JPA 配置#####</span></span><br><span class="line"><span class="comment"># 声明数据库</span></span><br><span class="line"><span class="string">spring.jpa.database=mysql</span></span><br><span class="line"><span class="comment"># 是否显示SQL语句</span></span><br><span class="line"><span class="string">spring.jpa.show-sql=true</span></span><br><span class="line"><span class="comment"># Hibernate 自动DDL 操作</span></span><br><span class="line"><span class="comment"># create 每次加载hibernate时都会删除上一次的生成的表</span></span><br><span class="line"><span class="comment"># create-drop 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除</span></span><br><span class="line"><span class="comment"># update 最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库)</span></span><br><span class="line"><span class="string">spring.jpa.hibernate.ddl-auto=update</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#配置方言</span></span><br><span class="line"><span class="string">spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect</span></span><br><span class="line"><span class="string">spring.jpa.properties.hibernate.generate_statistics=true</span></span><br><span class="line"><span class="string">spring.jpa.properties.hibernate.jdbc.batch_size=200</span></span><br><span class="line"><span class="string">spring.jpa.properties.hibernate.order_inserts=true</span></span><br></pre></td></tr></table></figure><p>注意:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 这是老的JDBC驱动</span></span><br><span class="line"><span class="string">spring.datasource.driverClassName</span> <span class="string">=</span> <span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="comment"># 这是新的 不更新会启动会报错</span></span><br><span class="line"><span class="string">spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver</span></span><br></pre></td></tr></table></figure><p>开启批处理:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">spring.datasource.url=jdbc:mysql://localhost:3306/spring_data?serverTimezone=UTC&rewriteBatchedStatements=true</span></span><br></pre></td></tr></table></figure><p>配置参数rewriteBatchedStatements 为 true;具体什么用后面会介绍。</p><h5 id="zuulService"><a href="#zuulService" class="headerlink" title="zuulService"></a>zuulService</h5><p>pom.xml:</p><figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependencies</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.cloud<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-cloud-starter-netflix-zuul<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.cloud<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-cloud-starter-netflix-eureka-client<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.cloud<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-cloud-starter-ribbon<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-test<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclusions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclusion</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.junit.vintage<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>junit-vintage-engine<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">exclusion</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">exclusions</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependencies</span>></span></span><br></pre></td></tr></table></figure><p>application.properties配置如下:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">server.port=8083</span></span><br><span class="line"><span class="string">spring.application.name=zuul</span></span><br><span class="line"><span class="string">eureka.client.service-url.defaultZone=http://localhost:8761/eureka/</span></span><br><span class="line"><span class="comment"># 路由前缀</span></span><br><span class="line"><span class="string">zuul.routes.zuul.path=/hello/**</span></span><br><span class="line"><span class="comment"># 通过id 指定访问得服务名称(这里指定的是order服务)</span></span><br><span class="line"><span class="string">zuul.routes.zuul.service-id=order</span> </span><br><span class="line"><span class="string">zuul.host.connect-timeout-millis=50000</span></span><br><span class="line"><span class="string">zuul.host.socket-timeout-millis=60000</span></span><br><span class="line"><span class="string">ribbon.ConnectTimeout=3000</span></span><br><span class="line"><span class="string">ribbon.ReadTimeout=10000</span></span><br></pre></td></tr></table></figure><p>注意:<br>zuul也需要当成服务注册eureka,这样才能拿到所有服务的信息,做相应的处理。关于zuul的作用可以点击这:<a href="https://www.jianshu.com/p/d49eb9b26351" target="_blank" rel="noopener">传送门</a></p><h4 id="数据准备"><a href="#数据准备" class="headerlink" title="数据准备"></a>数据准备</h4><h5 id="Entity"><a href="#Entity" class="headerlink" title="Entity"></a>Entity</h5><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.order.base.BaseEntity;</span><br><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.persistence.Column;</span><br><span class="line"><span class="keyword">import</span> javax.persistence.Entity;</span><br><span class="line"><span class="keyword">import</span> javax.persistence.Table;</span><br><span class="line"><span class="keyword">import</span> javax.validation.constraints.NotBlank;</span><br><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"></span><br><span class="line"><span class="comment">//lombok注解 自动生成get set</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table</span>(name = <span class="string">"order_info"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Order</span> <span class="keyword">extends</span> <span class="title">BaseEntity</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@NotBlank</span></span><br><span class="line"> <span class="meta">@Column</span>(columnDefinition = <span class="string">"varchar(18) default ' ' comment '订单名称' "</span>)</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@NotBlank</span></span><br><span class="line"> <span class="meta">@Column</span>(columnDefinition = <span class="string">"varchar(18) default ' ' comment '订单编码' "</span>)</span><br><span class="line"> <span class="keyword">private</span> String code;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Column</span>(columnDefinition = <span class="string">"decimal(18,2) default 0 comment '订单价格' "</span>)</span><br><span class="line"> <span class="keyword">private</span> BigDecimal price;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@NotBlank</span></span><br><span class="line"> <span class="meta">@Column</span>(columnDefinition = <span class="string">"varchar(18) default ' ' comment '会员Id' "</span>)</span><br><span class="line"> <span class="keyword">private</span> String userId;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Column</span>(columnDefinition = <span class="string">"varchar(128) default '' comment '备注' "</span>)</span><br><span class="line"> <span class="keyword">private</span> String remark;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Repository:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.order.dao.mysql.Order;</span><br><span class="line"><span class="keyword">import</span> org.springframework.data.repository.CrudRepository;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Repository;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Repository</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">OrderRepository</span> <span class="keyword">extends</span> <span class="title">CrudRepository</span><<span class="title">Order</span>, <span class="title">String</span>> </span>{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Service:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.order.base.SnowFlake;</span><br><span class="line"><span class="keyword">import</span> com.example.order.dao.mysql.Order;</span><br><span class="line"><span class="keyword">import</span> com.example.order.repository.OrderRepository;</span><br><span class="line"><span class="keyword">import</span> com.example.order.servcie.OrderService;</span><br><span class="line"><span class="keyword">import</span> com.example.order.vo.req.OrderSaveReqVo;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.annotation.Transactional;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.Timestamp;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title">OrderService</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> OrderRepository orderRepository;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> NamedParameterJdbcTemplate namedParameterJdbcTemplate;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="meta">@Transactional</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">save</span><span class="params">(OrderSaveReqVo reqVo)</span> </span>{</span><br><span class="line"> <span class="comment">//雪花算法分布式下生成唯一id</span></span><br><span class="line"> SnowFlake snowFlake = <span class="keyword">new</span> SnowFlake(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> ArrayList<Order> orders = <span class="keyword">new</span> ArrayList<Order>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i <= <span class="number">1000</span>; i++) {</span><br><span class="line"></span><br><span class="line"> Order order = <span class="keyword">new</span> Order();</span><br><span class="line"> order.setCode(reqVo.getCode());</span><br><span class="line"> order.setName(reqVo.getName());</span><br><span class="line"> order.setRemark(reqVo.getRemark());</span><br><span class="line"> order.setPrice(reqVo.getPrice());</span><br><span class="line"> order.setUserId(reqVo.getUserId());</span><br><span class="line"> order.setId(snowFlake.nextId());</span><br><span class="line"> order.setCreateTimestamp(<span class="keyword">new</span> Timestamp(System.currentTimeMillis()));</span><br><span class="line"></span><br><span class="line"> orders.add(order);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* SqlParameterSource[] beanSources = SqlParameterSourceUtils.createBatch(orders.toArray());</span></span><br><span class="line"><span class="comment"> String sql = "insert into order_info (id,code,name,remark,price,user_id,create_timestamp) values (:id,:code,:name,:remark,:price,:userId,:createTimestamp)";</span></span><br><span class="line"><span class="comment"> namedParameterJdbcTemplate.batchUpdate(sql, beanSources);*/</span></span><br><span class="line"> orderRepository.saveAll(orders);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"成功"</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>一个简单的保存操作我们来一个一个测试保存数据的性能如何;</p><p>方案一:</p><p>我们将数据源参数:”rewriteBatchedStatements=true”删除,1K条数据测试保存时间:</p><p><img alt="enter image description here" data-src="/img/DSO/5325AD12-1BDC-4246-9BC1-A44202938829.png" class="lazyload"></p><p>当时我看完都有点懵,1K条简单的数据保存了38s…..要被祭天。<br>分析:</p><blockquote><p>代码层次:一个简单的for循环,一个简单的保存,排除。极大可能就只有JPA saveAll会耗费很长时间;面向百度编程,可以使用JdbcTemplate批量保存。修改代码如下:</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.order.base.SnowFlake;</span><br><span class="line"><span class="keyword">import</span> com.example.order.dao.mysql.Order;</span><br><span class="line"><span class="keyword">import</span> com.example.order.repository.OrderRepository;</span><br><span class="line"><span class="keyword">import</span> com.example.order.servcie.OrderService;</span><br><span class="line"><span class="keyword">import</span> com.example.order.vo.req.OrderSaveReqVo;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.jdbc.core.namedparam.SqlParameterSource;</span><br><span class="line"><span class="keyword">import</span> org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.transaction.annotation.Transactional;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.Timestamp;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">OrderServiceImpl</span> <span class="keyword">implements</span> <span class="title">OrderService</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> OrderRepository orderRepository;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//NamedParameterJdbcTemplate 更加简便的封装 和 JdbcTemplat性能差不多</span></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> NamedParameterJdbcTemplate namedParameterJdbcTemplate;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="meta">@Transactional</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">save</span><span class="params">(OrderSaveReqVo reqVo)</span> </span>{</span><br><span class="line"> SnowFlake snowFlake = <span class="keyword">new</span> SnowFlake(<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> ArrayList<Order> orders = <span class="keyword">new</span> ArrayList<Order>();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i <= <span class="number">1000</span>; i++) {</span><br><span class="line"></span><br><span class="line"> Order order = <span class="keyword">new</span> Order();</span><br><span class="line"> order.setCode(reqVo.getCode());</span><br><span class="line"> order.setName(reqVo.getName());</span><br><span class="line"> order.setRemark(reqVo.getRemark());</span><br><span class="line"> order.setPrice(reqVo.getPrice());</span><br><span class="line"> order.setUserId(reqVo.getUserId());</span><br><span class="line"> order.setId(snowFlake.nextId());</span><br><span class="line"> order.setCreateTimestamp(<span class="keyword">new</span> Timestamp(System.currentTimeMillis()));</span><br><span class="line"></span><br><span class="line"> orders.add(order);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> SqlParameterSource[] beanSources = SqlParameterSourceUtils.createBatch(orders.toArray());</span><br><span class="line"> String sql = <span class="string">"insert into order_info (id,code,name,remark,price,user_id,create_timestamp) values (:id,:code,:name,:remark,:price,:userId,:createTimestamp)"</span>;</span><br><span class="line"> namedParameterJdbcTemplate.batchUpdate(sql, beanSources);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"成功"</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>用Postman测试时间:</p><p><img alt="enter image description here" data-src="/img/DSO/1DA166A0-A0F0-4104-9B8F-0966A28D6A08.png" class="lazyload"></p><p>不减反增!!!查询大量的资料,最终发现问题所在:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 注意 rewriteBatchedStatements=true 这个参数是开启批处理 很重要!!! 测试1W条数据没他许需要4分钟 开启只需要762ms 需要搭配JdbcTemplate使用</span></span><br><span class="line"><span class="string">spring.datasource.url=jdbc:mysql://localhost:3306/spring_data?serverTimezone=UTC&rewriteBatchedStatements=true</span></span><br></pre></td></tr></table></figure><p>加入批处理的配置:rewriteBatchedStatements=true</p><p>直接测试1W条数据:</p><p>JdbcTemplate:1061ms</p><p><img alt="enter image description here" data-src="/img/DSO/6BA83FA6-5A22-478e-BCE7-17A990B5DD41.png" class="lazyload"></p><p>JPA:2.4s</p><p><img alt="enter image description here" data-src="/img/DSO/4CB3FEE3-7081-4822-BE9A-EC2D4EA65942.png" class="lazyload"></p><p>解决!!!</p><p>也可以分析出来,大数据(和1K的数据相比)存储性能上来看,JdbcTemplate性能要大大好于JPA的,但我还是喜欢JPA,JPA真香!!</p><p>延伸:<br>我后面还做的10W,100W,1000W的数据测试:</p><table><thead><tr><th>数据量</th><th>10W</th><th>100W</th><th>1000W</th></tr></thead><tbody><tr><td>JPA</td><td>38s</td><td>437s</td><td>…</td></tr><tr><td>JdbcTemplate</td><td>3.25s</td><td>48s</td><td>…</td></tr></tbody></table><p>1000W数据时会报错:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">java.lang.OutOfMemoryError: GC overhead limit exceeded</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>这是在快OOM之前JVM的保护措施,代表垃圾回收耗费了98%的时间,但是回收的内存还不到2%,那么JVM会认为即将发生OOM,让程序提前结束。</p><p>可以看出,JdbcTemplate的存储性能确实远远好于Jpa,但是也有瓶颈;在上百万级别的数据可以考虑两种方案进行优化:<br>redis和多线程批量保存。这两种方案在后面我做好数据统计会再发出来,今天暂不赘述;</p><h4 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h4><p>值得多说的是我在测试数据的时候遇到的一些问题,在这里记录一下:</p><p>问题一:</p><p>启动Zuul服务请求超时解决:</p><p>开始时 zuul 的 application.properties:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">server.port=8083</span></span><br><span class="line"><span class="string">spring.application.name=zuul</span></span><br><span class="line"><span class="string">eureka.client.service-url.defaultZone=http://localhost:8761/eureka/</span></span><br><span class="line"><span class="string">zuul.routes.zuul.path=/hello/**</span></span><br><span class="line"><span class="string">zuul.routes.zuul.service-id=order</span></span><br></pre></td></tr></table></figure><p>刚开始测试阶段(没有开启加批处理),我在保存100条数据需要10s左右,就会报网关超时;</p><p><img alt="enter image description here" data-src="/img/DSO/0454F32F-F401-4ac8-8142-655EB684B9F7.png" class="lazyload"></p><p>后面改了 application.properties 配置 新增了这两个参数:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">zuul.host.connect-timeout-millis=50000</span></span><br><span class="line"><span class="string">zuul.host.socket-timeout-millis=60000</span></span><br></pre></td></tr></table></figure><p>但是不生效,测试不到2s就已经报错;</p><p>后来查阅资料才知道,原来如果 application.properties 配置指定的服务(service-id)那就会走ribbon负载均衡,修改配置文件:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="string">server.port=8083</span></span><br><span class="line"><span class="string">spring.application.name=zuul</span></span><br><span class="line"><span class="string">eureka.client.service-url.defaultZone=http://localhost:8761/eureka/</span></span><br><span class="line"><span class="string">zuul.routes.zuul.path=/hello/**</span></span><br><span class="line"><span class="string">zuul.routes.zuul.service-id=order</span></span><br><span class="line"><span class="string">zuul.host.connect-timeout-millis=50000</span></span><br><span class="line"><span class="string">zuul.host.socket-timeout-millis=60000</span></span><br><span class="line"><span class="string">ribbon.ConnectTimeout=3000</span></span><br><span class="line"><span class="string">ribbon.ReadTimeout=10000</span></span><br></pre></td></tr></table></figure><p>注意:</p><p>如果指定了 “zuul.routes.zuul.service-id”这个参数指定得服务,就会走ribbon负载均衡,只有直接指定具体的url才会走zuul网关;</p><p>问题二:</p><p>在使用JPA 执行deleteAll()时速度过慢:</p><p>设置orderService服务application.properties参数:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 是否显示SQL语句</span></span><br><span class="line"><span class="string">spring.jpa.show-sql=true</span></span><br></pre></td></tr></table></figure><p>发现JPA执行deleteAll()时,其实是一条一条删的…..不慢才怪;</p><p>改造:</p><p>service层:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="meta">@Transactional</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">delete</span><span class="params">()</span> </span>{</span><br><span class="line"> orderRepository.deleteOrderInfo();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>OrderRepository:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Modifying</span></span><br><span class="line"><span class="meta">@Query</span>(value = <span class="string">"DELETE FROM order_info"</span>, nativeQuery = <span class="keyword">true</span>)</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">deleteOrderInfo</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure><p>需要注意的是:@Modifying 和 @Transactional 这两个注解;</p><p>@Modifying:表明需要对数据进行修改操作;一般搭配@Query注解一起使用;</p><p>@Transactional :声明式事务管理,对数据库数据进行修改操作时需要提交事务,不加这个注解提交事务会异常;</p><p>持续更新<del>~</del></p>]]></content>
<categories>
<category> 数据库 </category>
</categories>
<tags>
<tag> 数据库 </tag>
<tag> 优化 </tag>
</tags>
</entry>
<entry>
<title>微架构SpringCloud-Eureka服务注册中心</title>
<link href="/2019/11/15/%E5%88%86%E5%B8%83%E5%BC%8F/Eureka/"/>
<url>/2019/11/15/%E5%88%86%E5%B8%83%E5%BC%8F/Eureka/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="微架构SpringCloud-Eureka服务注册中心"><a href="#微架构SpringCloud-Eureka服务注册中心" class="headerlink" title="微架构SpringCloud-Eureka服务注册中心"></a>微架构SpringCloud-Eureka服务注册中心</h2><p> Erueka是一个基于REST,服务注册与发现的组件。<br> 它主要包括两个组件:</p><blockquote><p>· Eureka Client: 一个Java客户端,用于简化与 Eureka Server 的交互(通常就是微服务中的客户端和服务端)<br>· Eureka Server:提供服务注册和发现的能力(通常就是微服务中的注册中心)</p></blockquote><h5 id="Eureka-的作用"><a href="#Eureka-的作用" class="headerlink" title="Eureka 的作用"></a>Eureka 的作用</h5><blockquote><p>开发过程中,各个服务之间的调用来实现业务逻辑不同走向,而如果将所有的服务都放进一个工程里就会出现项目工程的大量冗余及各种资源的叠加。就像是将所有年纪的学生放进一个教室里,造成项目极难管理,甚至阻碍了开发。那么我们就可以将不同的服务拆分开来,专注与某个服务逐一管理,微服务由此而生。各个服务之间相互的通信完成不同的业务逻辑走向,但是随着服务的不断增长,那服务之间的通信也就会出现相对松散,链路也就相对复杂。举个例子:我们去机场取票,我们肯定是去对应的航空公司取票。但是如果机场很大,又有很多家航空公司,你咋办呢?只能一家一家去找?我想应该会去找服务站的人咨询吧。微服务也是一样,而Eureka就充当这服务站的角色;他会告诉你,你所需要的服务(对应的航空公司)在哪里,是不是就做到了服务的链路简便(节约时间)。</p></blockquote><p><img alt="enter image description here" data-src="/img/Eureka/A593C580-086A-4da4-B79E-01CC6CAC344A.png" class="lazyload"></p><h5 id="搭建一个Eureka服务中心"><a href="#搭建一个Eureka服务中心" class="headerlink" title="搭建一个Eureka服务中心"></a>搭建一个Eureka服务中心</h5><p>参考:<a href="https://www.jianshu.com/p/c89e2479595d" target="_blank" rel="noopener">https://www.jianshu.com/p/c89e2479595d</a></p><p>注意:</p><p>我的项目结构如下:</p><p><img alt="enter image description here" data-src="/img/Eureka/86BF389E-25BC-4bbc-9845-AF083A6D9C3E.png" class="lazyload"></p><p>EurekaServer启动成功,再启动两个服务的时候会报错:</p><p><img alt="enter image description here" data-src="/img/Eureka/BCBDA4F0-A84F-45d7-AD6B-8D12B0B4B177.png" class="lazyload"></p><p>报错提示:端口被占用,需要指定两个服务的端口<br><img alt="enter image description here" data-src="/img/Eureka/879B2DA4-BC24-4d42-A3D2-8478FB220409.png" class="lazyload"><br><img alt="enter image description here" data-src="/img/Eureka/0DA6EB6E-5DD2-4893-A560-0234D1471C12.png" class="lazyload"></p><h5 id="Eureka集群"><a href="#Eureka集群" class="headerlink" title="Eureka集群"></a>Eureka集群</h5><blockquote><p>集群概念:集群通信系统是一种用于集团调度指挥通信的移动通信系统,主要应用在专业移动通信领域。该系统具有的可用信道可为系统的全体用户共用,具有自动选择信道功能,它是共享资源、分担费用、共用信道设备及服务的多用途、高效能的无线调度通信系统。<br>人话:小时候老师罚抄10遍课文,一个人要抄10遍,一遍需要10分钟,抄完需要100分钟。但是,这时你的小伙伴有10个,一人抄一遍,抄完只需要十分钟。</p></blockquote><p>Eureka集群也一样,回到前面的例子,这时你到了服务站(EurekaServer),正好赶上高峰期,你前面排了一群人都在咨询,一群人都在催,服务站慢慢的也疲于应付最后导致崩溃,对应到计算机就是服务器宕机,哦吼,全完蛋,整个应用系统全部瘫痪;<br><img alt="enter image description here" data-src="/img/Eureka/D9D930CF-6E83-44f0-AF1F-1753D2263728.png" class="lazyload"></p><p>方案:<br>咋办呢?一直等下去?插队可耻不可取。这时机场做出了对应措施,增加服务站。对!没错,对应的就是增加EurekaSever,将客户请求分流;</p><p><img alt="enter image description here" data-src="/img/Eureka/4B4E64B6-1E81-48d6-8C14-49E0A00931D5.png" class="lazyload"></p><h5 id="搭建一个Eureka集群服务注册中心"><a href="#搭建一个Eureka集群服务注册中心" class="headerlink" title="搭建一个Eureka集群服务注册中心"></a>搭建一个Eureka集群服务注册中心</h5><p>参考:<a href="https://www.jianshu.com/p/c7b13e06bd46" target="_blank" rel="noopener">https://www.jianshu.com/p/c7b13e06bd46</a></p><h5 id="Eureka应用集群"><a href="#Eureka应用集群" class="headerlink" title="Eureka应用集群"></a>Eureka应用集群</h5><p>再再回到前面的例子,这时候你终于到了对应的航空公司准备换登机牌,嗯,赶巧了,换登机牌也在排队…..对!没错,一样的道理,开设新的换登机牌服务站;</p><p><img alt="enter image description here" data-src="/img/Eureka/0090A2D3-1100-42ce-9059-6C972EF391EB.png" class="lazyload"></p><h5 id="搭建一个Eureka集群服务应用中心"><a href="#搭建一个Eureka集群服务应用中心" class="headerlink" title="搭建一个Eureka集群服务应用中心"></a>搭建一个Eureka集群服务应用中心</h5><p>参考:<a href="https://www.jianshu.com/p/c7b13e06bd46" target="_blank" rel="noopener">https://www.jianshu.com/p/c7b13e06bd46</a></p><h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><p>这样基本上就实现了Eureka集群,达到集群的高可用的特点;Eureka还给我们提供了很多的高可用的机制,在后面使用的过程中也会边学边更新进来。</p><p>知识点:<br>Eureka,它实际上起到的是一个注册服务!它就像一个云端通讯录!应用启动,注册到EurekaSever上,同时也获取了其他云端成云的联系信息【并缓存到本应用信息】,所以若“基站”服务突然不存在了,并不妨碍宕机之前以注册到“基站”应用间的通信!但若一个新的应用也试着注册到Eureka,首先,它无法注册,因为“基站”服务已经不存在了,其二,那么新注册的应用也无法与之前的应用进行通信,因为它们之间没有在一个云端里交流!若想更新“通讯方式”,需重启Eureka服务以自动捕获Eureka客户端刷新“通讯录”。</p>]]></content>
<categories>
<category> 分布式 </category>
</categories>
<tags>
<tag> 微服务 </tag>
</tags>
</entry>
<entry>
<title>记录我心中Top10的电影</title>
<link href="/2019/11/10/%E7%94%B5%E5%BD%B1/%E6%88%91%E5%BF%83%E4%B8%ADTop10%E7%9A%84%E7%94%B5%E5%BD%B1/"/>
<url>/2019/11/10/%E7%94%B5%E5%BD%B1/%E6%88%91%E5%BF%83%E4%B8%ADTop10%E7%9A%84%E7%94%B5%E5%BD%B1/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="Top10:久石让武道馆音乐会"><a href="#Top10:久石让武道馆音乐会" class="headerlink" title="Top10:久石让武道馆音乐会"></a>Top10:久石让武道馆音乐会</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/9SXZ3PYrM5sF4el.jpg" class="lazyload"></p><blockquote><p>三年前我看了一个片段,好像是幽灵公主,然后就一直存在手机里了。我一直都很喜欢宫崎骏的动漫,后面也看过几部让我印象深刻的电影,神奇的是有好几部都是久石让配的乐。也让我明白了音乐就是一部电影的灵魂,而久石让的音乐给我的感觉就是”纯粹”;</p></blockquote><h2 id="Top9-第六感生死缘"><a href="#Top9-第六感生死缘" class="headerlink" title="Top9: 第六感生死缘"></a>Top9: 第六感生死缘</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/3Zg7YPKb498wFu6.jpg" class="lazyload"></p><blockquote><p>初中在红山电影频道看了后半段,当时啥也不懂,只觉得朦朦胧胧的女主很好看。最后死神和比尔走下了桥,布莱克从桥的那边走了回来只觉得心里有种失落感,后来想从头看忘了名字,多亏皮特演的电影多。从头看了一遍还专门在快播写了第一次影评,现在还记得我写的有句话:”如果说这部电影的剧情是躯体,那配乐就是注入躯体的灵魂”。zuo~</p></blockquote><h2 id="Top8-互联网之子"><a href="#Top8-互联网之子" class="headerlink" title="Top8:互联网之子"></a>Top8:互联网之子</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/blHXJxwQMnUve2A.png" class="lazyload"></p><blockquote><p>讲述了亚伦·斯沃茨开挂的一生,有趣的是现在我写博客用的Markdown就是他和另一人共同设计编写的。我们都是站在巨人的肩膀上发现世界的;</p></blockquote><h2 id="Top7-英雄"><a href="#Top7-英雄" class="headerlink" title="Top7:英雄"></a>Top7:英雄</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/QJFPuvaqxr7fU89.png" class="lazyload"></p><blockquote><p>老谋子的二板斧,”活着”没看过,”英雄”算是侠义片中的精品了,我觉得是第一了。”东邪西毒”是爱情片。为此专门去了趟九寨沟,很美,但还是和电影差点。所以这部电影,不光在剧情上是巨佳,在艺术,意境,配乐上都是佳品!小我。天下。</p></blockquote><h2 id="Top6-海洋"><a href="#Top6-海洋" class="headerlink" title="Top6:海洋"></a>Top6:海洋</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/A1ydtVExDmSazh5.jpg" class="lazyload"></p><blockquote><p>纪录片的魅力在于可以用第一人称的视角去感受不一样的人生或着他生。蓝色的星球,蓝色代表着海洋,海洋拥抱着陆地。在自然的面前人类承认自我的渺小才是对生命的尊重吧。配乐满分,部分配乐还作为了《巫师3》CG动画的插曲;</p></blockquote><h2 id="Top5-不能说得秘密"><a href="#Top5-不能说得秘密" class="headerlink" title="Top5:不能说得秘密"></a>Top5:不能说得秘密</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/11/FPvlmCkJoOsEB1w.png" class="lazyload"></p><blockquote><p>周董的处女座,我的启蒙片,哈哈哈哈哈哈哈,小学看完的我有点蒙,一度非常的迷恋这部电影,天天下课借我同桌的手机听《不能说的秘密》,后来顺利的将这位同桌变成了我的初恋。现在能保持每天吃一个苹果全托了”An apple a day keeps the doctor away.”;好的电影对于我就是能回忆起那时的自己那时的他们,让我感叹岁月蹉跎的同时还能念起你催我换手机的样子。</p></blockquote><h2 id="Top4:完美陌生人"><a href="#Top4:完美陌生人" class="headerlink" title="Top4:完美陌生人"></a>Top4:完美陌生人</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/11/G45aUtrXfpA7Pui.png" class="lazyload"></p><blockquote><p>唯一一部不靠配乐进来的,可以想像内容有多硬核。想了半天怎么表述,总之硬核。</p></blockquote><h2 id="Top3-哈利波特-死亡圣器(上下)"><a href="#Top3-哈利波特-死亡圣器(上下)" class="headerlink" title="Top3:哈利波特-死亡圣器(上下)"></a>Top3:哈利波特-死亡圣器(上下)</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/11/3ecIzQCNuTWOYMR.png" class="lazyload"></p><blockquote><p>小时候的我觉得哈利波特是个魔幻冒险片,慢慢长大了,哈利也从白净的孩子长成了络腮胡,我也是,但比他帅。随着《火焰杯》虫尾巴对塞德里克使用了”阿瓦达索命”开始,《哈利波特》正式告别了从前,成为了一部恐怖的压抑的残酷的成长电影。死亡圣器上是初中我月考刚考完一个人去看的,在南门电影院,因为最后多比死了,加上考砸了,电影结束后我第一次感受到了压抑;死亡圣器下好像是和李智皓和柴世锦一起看的,电影结束后我们三个坐在椅子上愣着没走。《哈利波特》的终结,代表着我们也正是告别了童年,以后的我们也将随人海渐行渐远最后消失。我是幸运的,老李还在。感谢他对本文章100块的打赏,懂咩?</p></blockquote><h2 id="Top2-恶人"><a href="#Top2-恶人" class="headerlink" title="Top2:恶人"></a>Top2:恶人</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/11/B9izpYQdHljP2RO.jpg" class="lazyload"></p><blockquote><p>久石让配乐,艺术源于生活,越是贴近生活越能给你会心一击。这部电影豆瓣评分并不高,但是确实给了我很久的负能量。在背阴处呆久了的人看到初升的朝阳都会流泪吧。</p></blockquote><h2 id="Top1-星际穿越"><a href="#Top1-星际穿越" class="headerlink" title="Top1:星际穿越"></a>Top1:星际穿越</h2><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/11/WLE5XnYZDKmdNtI.jpg" class="lazyload"></p><blockquote><p>-. . .– - — -. … - …. .. .-. -.. .-.. .- .– </p></blockquote>]]></content>
<categories>
<category> 电影 </category>
</categories>
<tags>
<tag> 电影 </tag>
</tags>
</entry>
<entry>
<title>Java类加载机制<一> — 类加载器</title>
<link href="/2019/11/10/Java/Java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6-%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8/"/>
<url>/2019/11/10/Java/Java%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6-%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="Java类加载机制-lt-一-gt-—-类加载器"><a href="#Java类加载机制-lt-一-gt-—-类加载器" class="headerlink" title="Java类加载机制<一> — 类加载器"></a>Java类加载机制<一> — 类加载器</h2><blockquote><p>虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机类加载机制。在Java语言中,类的加载、连接和初始化过程都是在程序运行期间完成的,这是java作为动态语言的基础。另外值得注意的是上面提到的Class文件,并不一定值得是磁盘上的.class文件,而只需要是任何符合字节码规范的一串二进制字节流就可以了。</p></blockquote><h5 id="类加载过程:"><a href="#类加载过程:" class="headerlink" title="类加载过程:"></a>类加载过程:</h5><blockquote><p>类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。其中验证、准备、解析三个阶段称之为连接。<br><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/ZO6sBeQXhGEcPFx.jpg" class="lazyload"></p></blockquote><h5 id="类加载器:"><a href="#类加载器:" class="headerlink" title="类加载器:"></a>类加载器:</h5><blockquote><p>类加载由类加载器完成。</p><p>类加载器负责将可能是网络上,或者磁盘中的class文件加载到内存中,并为其生成对应的java,lang,class对象。一旦一个类已经被jvm加载完成那么这个类就不会再被加载了,那么怎么才能确认一个类只能被加载一次?在java中,一个类用其全限定类名(包名 + 类名)作为其唯一标识,但在JVM中采取的是“全限定类名”+ “类加载器”作为唯一标识。也就是说,一个类如果是不同的类加载器加载,那么生成的class文件是不同的。</p><p>举个栗子:现在在loader包下面有一个Test的类,被类加载器ClassLoader的实例Kl负责加载,则该Test类在对应的class对象JVM中表示为(Test.loader.Kl),那么如果该类被类加载器ClassLoader的实例Kl2加载时,那么Test类对应的class对象在JVM中表示为(Test.loader.Kl2)。这意味着两个同名类的类加载器(Test.loader.Kl)和(Test.loader.Kl2)是不同的,他们互不兼容。</p><p>JVM预定义有三种类加载器,当JVM开始启动时,Java开始使用以下三种类加载器:</p><blockquote><p>1.启动类加载器(根类加载器):BootstrapClassLoader<br>是嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负载加载JAVA_HOME/lib下的类库,启动类加载器无法被应用程序直接使用。</p></blockquote></blockquote><blockquote><blockquote><p>2.扩展类加载器:Extension ClassLoader<br>Java编写,且他的父类加载器是Bootstrap, 是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类库。开发者可以使用扩展类加载器。</p></blockquote></blockquote><blockquote><blockquote><p>3.系统类加载器:App ClassLoader<br>系统类加载器,也称为应用程序类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。它的父加载器为Ext ClassLoader。</p></blockquote></blockquote><p>上述三者的层级关系如下图:<br><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/wobQOWUN8L7IfGs.png" class="lazyload"></p><p>需要注意的是:类加载的体系并不是“继承”体系,而是“委托”体系。 大多数类加载器首先会到自己的parent中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次。</p><p>下面我们通过程序来验证:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">System.out.println(ClassLoader.getSystemClassLoader().getParent());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出的结果是:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">sun.misc.Launcher$ExtClassLoader@<span class="number">42e816</span></span><br></pre></td></tr></table></figure><p>对比三者的层级关系图来看,App ClassLoader 的父类加载器确实是Ext ClassLoader,那么我们如果再往上走一层,猜想没错的话,输出的结果应该是Bootstrap ClassLoader</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出的结果是:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">null</span></span><br></pre></td></tr></table></figure><p>注意:这里不是说 Ext ClassLoader 没有父类加载器,而是因为Bootstrap ClassLoader是C++写的;</p><p>下面我们从源码中看看他们之间的继承关系:</p><p><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/rFWlAoh2Pw3K8eZ.png" class="lazyload"></p><p>结论:除了启动类加载器Bootstrap ClassLoader,其他类加载器都是ClassLoader的子类;</p><p>再来看看 AppClassLoader的源码:<br>可以看出,系统加载器:AppClassLoader只能加载到指定的“java.class.path”路径下的Class文件。</p><h5 id="双亲委派机制:"><a href="#双亲委派机制:" class="headerlink" title="双亲委派机制:"></a>双亲委派机制:</h5><blockquote><p>如果一个类加载器收到了一个类加载请求,它不会自己去尝试加载这个类,而是把这个请求转交给父类加载器去完成。每一个层次的类加载器都是如此。因此所有的类加载请求都应该传递到最顶层的启动类加载器中,只有到父类加载器反馈自己无法完成这个加载请求(在它的搜索范围没有找到这个类)时,子类加载器才会尝试自己去加载。委派的好处就是避免有些类被重复加载。</p></blockquote><p>双亲委派的实现比较简单,我们来看下源码:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">synchronized</span> Class<?> loadClass(String name, <span class="keyword">boolean</span> resolve) </span><br><span class="line"><span class="keyword">throws</span> ClassNotFoundException </span><br><span class="line">{ </span><br><span class="line"><span class="comment">// First, check if the class has already been loaded </span></span><br><span class="line">Class c = findLoadedClass(name); </span><br><span class="line"> <span class="comment">//该类还没有被加载,那么则 </span></span><br><span class="line"><span class="keyword">if</span> (c == <span class="keyword">null</span>) { </span><br><span class="line"><span class="keyword">try</span> { </span><br><span class="line"> <span class="comment">//如其父类不为空,那么则使用其父类进行加载(父类委托机制) </span></span><br><span class="line"> <span class="keyword">if</span> (parent != <span class="keyword">null</span>) { </span><br><span class="line"> c = parent.loadClass(name, <span class="keyword">false</span>); </span><br><span class="line"> } </span><br><span class="line"> <span class="comment">//如果其父类为空则使用BootstrapClass进行加载 </span></span><br><span class="line"> <span class="keyword">else</span> { </span><br><span class="line"> c = findBootstrapClass0(name); </span><br><span class="line"> } </span><br><span class="line"> } <span class="keyword">catch</span> (ClassNotFoundException e) { </span><br><span class="line"> <span class="comment">// If still not found, then invoke findClass in order // to find the class. </span></span><br><span class="line"> <span class="comment">//如果此类的父类加载器无法加载该类,那么由此类加载器的findClass进行类字节码的加载 </span></span><br><span class="line"> c = findClass(name); </span><br><span class="line"> } </span><br><span class="line">} </span><br><span class="line"> <span class="comment">//是否需要解析类,如果resolve=true时,则保证已经装载,而且已经连接了。resolve=falses时,则仅仅是去装载这个类,不关心是否连接了,所以此时可能被连接了,也可能没有被连接 </span></span><br><span class="line"><span class="keyword">if</span> (resolve) { </span><br><span class="line"> resolveClass(c); </span><br><span class="line">} </span><br><span class="line"><span class="keyword">return</span> c; </span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass方法,若父类加载器不存在,则使用启动类加载器。如果父类加载器加载失败,则抛出异常之后看,再调用自己的findClass方法进行加载。</p><p>除了上面介绍的的loadClass方法下面在介绍几个比较重要的方法:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> Class<?> defineClass </span><br><span class="line"><span class="keyword">protected</span> Class<?> findClass(String name) </span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">resolveClass</span><span class="params">(Class<?> c)</span></span></span><br></pre></td></tr></table></figure><blockquote><p>上面的loadClass方法的作用只是启动类的加载,指定类应该由谁进行加载。而具体的怎么加载需要用到defineClass方法:其能够将byte字节流解析成JVM能够识别的Class对象。</p><p>findClass:负责取得需要加载的类的字节码,然后可以调用definedClass方法生成类的Class对象。有了这样两个方法我们不仅仅可以通过class文件实例化对象,还可以通过网络接受字节码实例化对象。一般我们配合使用defineClass和findClass方法,直接覆盖ClassLoader父类的findClass方法来实现类的加载规则,findClass取得需要加载的类的字节码,defineClass根据字节码创建类对象;</p></blockquote>]]></content>
<categories>
<category> Java </category>
</categories>
<tags>
<tag> Java </tag>
</tags>
</entry>
<entry>
<title>Java数据机构-链表特性</title>
<link href="/2019/11/10/Java/Java%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E9%9B%86%E5%90%88%E7%9B%B8%E5%85%B3/"/>
<url>/2019/11/10/Java/Java%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84-%E9%9B%86%E5%90%88%E7%9B%B8%E5%85%B3/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="Java数据机构-链表特性"><a href="#Java数据机构-链表特性" class="headerlink" title="Java数据机构-链表特性"></a>Java数据机构-链表特性</h2><h5 id="链表的特点:"><a href="#链表的特点:" class="headerlink" title="链表的特点:"></a>链表的特点:</h5><blockquote><p>链表是以分散的形式存储在内存中的,在查询元素时只能通过上个元素中存储的下个元素的信息进行查询。也就是说,链表不支持随机访问,要想查询某个元素,只能从头节点开始遍历。时间复杂度相较于数组的随机访问会增大;</p></blockquote><h5 id="时间复杂度:"><a href="#时间复杂度:" class="headerlink" title="时间复杂度:"></a>时间复杂度:</h5><blockquote><p>在描述算法复杂度时,经常用到o(1), o(n), o(logn), o(nlogn)来表示对应算法的时间复杂度。这里进行归纳一下它们代表的含义:这是算法的时空复杂度的表示。不仅仅用于表示时间复杂度,也用于表示空间复杂度。</p></blockquote><blockquote><p>O后面的括号中有一个函数,指明某个算法的耗时/耗空间与数据增长量之间的关系。其中的n代表输入数据的量。</p><p>比如时间复杂度为O(n),就代表数据量增大几倍,耗时也增大几倍。比如常见的遍历算法。</p><p>再比如时间复杂度O(n^2),就代表数据量增大n倍时,耗时增大n的平方倍,这是比线性更高的时间复杂度。比如冒泡排序,就是典型的O(n^2)的算法,对n个数排序,需要扫描n×n次。</p><p>再比如O(logn),当数据增大n倍时,耗时增大logn倍(这里的log是以2为底的,比如,当数据增大256倍时,耗时只增大8倍,是比线性还要低的时间复杂度)。二分查找就是O(logn)的算法,每找一次排除一半的可能,256个数据中查找只要找8次就可以找到目标。</p><p>O(nlogn)同理,就是n乘以logn,当数据增大256倍时,耗时增大256*8=2048倍。这个复杂度高于线性低于平方。归并排序就是O(nlogn)的时间复杂度。</p><p>O(1)就是最低的时空复杂度了,也就是耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变。 哈希算法就是典型的O(1)时间复杂度,无论数据规模多大,都可以在一次计算后找到目标(不考虑冲突的话)</p></blockquote><h5 id="链表分为单链表和双向列表"><a href="#链表分为单链表和双向列表" class="headerlink" title="链表分为单链表和双向列表"></a>链表分为单链表和双向列表</h5><p>共同点:</p><blockquote><p>1.无法进行随机访问<br>2.他们都能够在O(1)时间内在给定的节点或在列表开头添加一个新节点<br>3.他们都能够在O(1)时间内删除列表的第一个节点</p><p>但在删除一个给定的节点(包括最后一个节点)时略有不同单向列表在删除一个给定节点时只能从第一个节点依次遍历进行查找删除,在时间复杂度上是O(n),而双向列表因为记录了前驱节点的信息所以我们可以在O(1)的时间内删除给定的节点</p></blockquote><p>优点:</p><blockquote><p>1.因为链表在内存中储存的形式为不连续的,所以在内存空间利用上会比较高,并且在删除和插入操作上时间复杂度O(1),相比与数组会快<br>2.链表支持动态的扩容</p></blockquote><p>缺点:</p><blockquote><p>1.因为链表的节点会存储下个节点的信息(双向链表会存储上个节点的信息)所以一个节点的内存占用会比数组大<br>2.链表的查询这只能从头节点依次向后遍历,不支持随机访问,所有查询的效率会很低</p></blockquote><p>总结:</p><blockquote><p>1.如果对数据要经常进行删除或插入的操作建议使用链表<br>2.如果对数据只进行查询并且数据的大小已经确定建议使用数组</p></blockquote><p>各个数据结构的常见操作及排序算法的时间复杂度汇总:<br><img alt="enter image description here" data-src="https://i.loli.net/2019/11/10/HutP7dzG3b9xpae.png" class="lazyload"></p><p>图片源自:<a href="https://blog.csdn.net/weixin_44387066/article/details/87694330" target="_blank" rel="noopener">https://blog.csdn.net/weixin_44387066/article/details/87694330</a></p>]]></content>
<categories>
<category> Java </category>
</categories>
<tags>
<tag> Java </tag>
<tag> 数据结构 </tag>
</tags>
</entry>
<entry>
<title>分布式锁</title>
<link href="/2019/11/10/%E5%88%86%E5%B8%83%E5%BC%8F/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/"/>
<url>/2019/11/10/%E5%88%86%E5%B8%83%E5%BC%8F/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="Java分布式锁"><a href="#Java分布式锁" class="headerlink" title="Java分布式锁"></a>Java分布式锁</h2><h5 id="为什么要使用分布式锁:"><a href="#为什么要使用分布式锁:" class="headerlink" title="为什么要使用分布式锁:"></a>为什么要使用分布式锁:</h5><blockquote><p>为了保证一个方法或属性在高并发的情况下同一时间只能被同一个线程执行,在传统单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLock或Synchronized)进行互斥控制。但是,随之业务发展的需要,原单机部署的系统演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同的机器上,这将原来的单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。</p><p>为了解决这个问题,就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!</p></blockquote><h5 id="分布式锁应该具备哪些条件"><a href="#分布式锁应该具备哪些条件" class="headerlink" title="分布式锁应该具备哪些条件"></a>分布式锁应该具备哪些条件</h5><blockquote><ul><li>在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;</li><li>高可用、高性能的获取锁与释放锁;</li><li>具备可重入特性;</li><li>具备锁失效机制、防止死锁;</li><li>具备非阻塞锁特性,即没有获取到锁直接返回获取锁失败;</li></ul></blockquote><h5 id="分布式锁的实现方式"><a href="#分布式锁的实现方式" class="headerlink" title="分布式锁的实现方式"></a>分布式锁的实现方式</h5><blockquote><p>目前几乎所有大型网站及应用都是分布式部署,分布式场景中的数据一致性问题一直是一个比较重要的话题,分布式的CAP 理论告诉我们任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。</p></blockquote><blockquote><p>一般情况下,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证最终一致性,只要这个最终时间是在用户可以接受的范围内即可。</p></blockquote><blockquote><p>在很多时候,为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一信方法在同一时间内只能被同一个线程执行。</p></blockquote><blockquote><p>而分布式锁的具体实现方案有如下三种:</p><ul><li>基于数据库实现;</li><li>基于缓存(Redis等)实现;</li><li>基于Zookeeper实现;</li></ul></blockquote><blockquote><p>以上尽管有三种方案,但是我们需要根据不同的业务进行选型。</p></blockquote><h5 id="基于数据库的实现方式"><a href="#基于数据库的实现方式" class="headerlink" title="基于数据库的实现方式"></a>基于数据库的实现方式</h5><blockquote><p>基于数据库的实现方式的思想核心为:<br>在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。</p></blockquote><p> 一:创建一张表:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> <span class="keyword">IF</span> <span class="keyword">EXISTS</span> <span class="string">`method_lock`</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`method_lock`</span> (</span><br><span class="line"> <span class="string">`id`</span> <span class="built_in">INT</span>(<span class="number">11</span>) <span class="keyword">UNSIGNED</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT</span><br><span class="line"> <span class="keyword">COMMENT</span> <span class="string">'主键'</span>,</span><br><span class="line"> <span class="string">`method_name`</span> <span class="built_in">VARCHAR</span>(<span class="number">64</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span></span><br><span class="line"> <span class="keyword">COMMENT</span> <span class="string">'锁定的方法名'</span>,</span><br><span class="line"> <span class="string">`desc`</span> <span class="built_in">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span></span><br><span class="line"> <span class="keyword">COMMENT</span> <span class="string">'备注信息'</span>,</span><br><span class="line"> <span class="string">`update_time`</span> <span class="built_in">TIMESTAMP</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">CURRENT_TIMESTAMP</span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> <span class="keyword">CURRENT_TIMESTAMP</span>,</span><br><span class="line"> PRIMARY <span class="keyword">KEY</span> (<span class="string">`id`</span>),</span><br><span class="line"> <span class="keyword">UNIQUE</span> <span class="keyword">KEY</span> <span class="string">`uidx_method_name`</span> (<span class="string">`method_name`</span>) <span class="keyword">USING</span> BTREE</span><br><span class="line">)</span><br><span class="line"> <span class="keyword">ENGINE</span> = <span class="keyword">InnoDB</span></span><br><span class="line"> AUTO_INCREMENT = <span class="number">3</span></span><br><span class="line"> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span> = utf8</span><br><span class="line"> <span class="keyword">COMMENT</span> = <span class="string">'锁定中的方法'</span>;</span><br></pre></td></tr></table></figure><p>二:想要执行某个方法,就使用这个方法名向表中插入数据:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> method_lock (method_name, <span class="keyword">desc</span>) <span class="keyword">VALUES</span> (<span class="string">'methodName'</span>, <span class="string">'测试的methodName'</span>);</span><br></pre></td></tr></table></figure><blockquote><p>由于我们对method_name做了唯一性约束,如果有多个请求同时提交插入操作时,数据库能确保只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体中的内容。</p></blockquote><p>三、执行完成后,删除对应的行数据释放锁</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> method_lock <span class="keyword">where</span> method_name =<span class="string">'methodName'</span>;</span><br></pre></td></tr></table></figure><blockquote><p>这里只是基于数据库实现的一种方法(比较粗糙的一种)。</p></blockquote><p>但是对于分布式锁应该具备的条件来说,还有一些问题需要解决及优化:</p><blockquote><ul><li>因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能。所以,数据库需要双机部署、数据同步、主备切换;</li><li>它不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据。所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器线程相同,若相同则直接获取锁。</li><li>没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;</li><li>不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取;</li><li>依赖数据库需要一定的资源开销,性能问题需要考虑;</li></ul></blockquote><p>数据库实现方案二:</p><blockquote><p>现在基于Mysql引擎InnoDB来实现:</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">lock</span><span class="params">()</span></span>{</span><br><span class="line"> connection.setAutoCommit(<span class="keyword">false</span>)</span><br><span class="line"> <span class="keyword">while</span>(<span class="keyword">true</span>){</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> result = select * from methodLock where method_name=xxx <span class="keyword">for</span> update;</span><br><span class="line"> <span class="keyword">if</span>(result==<span class="keyword">null</span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">catch</span>(Exception e){</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> sleep(<span class="number">1000</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>在查询语句后面加“for update”这时数据库会自动给表加排他锁(注意:InnoDB再给表加排他锁时,只有通过索引检索时才会走行级锁,)</p></blockquote><h5 id="基于缓存(Redis)的实现方式"><a href="#基于缓存(Redis)的实现方式" class="headerlink" title="基于缓存(Redis)的实现方式"></a>基于缓存(Redis)的实现方式</h5><p>使用Redis实现分布式锁的理由:</p><blockquote><p>1.Redis具有很高的性能;<br>2.Redis的命令对此支持较好,实现起来很方便;</p></blockquote><p>Redis命令介绍:</p><p>SETNX:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">// 当且仅当key不存在时,<span class="keyword">set</span>一个<span class="keyword">key</span>为val的字符串,返回<span class="number">1</span>;</span><br><span class="line">// 若<span class="keyword">key</span>存在,则什么都不做,返回<span class="number">0</span>。</span><br><span class="line">SETNX <span class="keyword">key</span> val;</span><br></pre></td></tr></table></figure><p>expire:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">// 为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。</span><br><span class="line">expire key timeout;</span><br></pre></td></tr></table></figure><p>delete:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">// 删除key</span><br><span class="line"><span class="keyword">delete</span> <span class="keyword">key</span>;</span><br></pre></td></tr></table></figure><p>我们通过Redis实现分布式锁时,主要通过上面的这三个命令。</p><p>通过Redis实现分布式的核心思想为:</p><blockquote><p>1.获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间自动释放锁,锁的value值为一个随 机生成的UUID,通过这个value值,在释放锁的时候进行判断。<br>2.获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。<br>3.释放锁的时候,通过UUID判断是不是当前持有的锁,若时该锁,则执行delete进行锁释放。</p></blockquote><p>具体实现代码如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DistributedLock</span> </span>{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> JedisPool jedisPool;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String KEY_PREF = <span class="string">"lock:"</span>; <span class="comment">// 锁的前缀</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">DistributedLock</span><span class="params">(JedisPool jedisPool)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.jedisPool = jedisPool;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加锁</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> lockName String 锁的名称(key)</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> acquireTimeout long 获取超时时间</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> timeout long 锁的超时时间</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 锁标识</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">lockWithTimeout</span><span class="params">(String lockName, <span class="keyword">long</span> acquireTimeout, <span class="keyword">long</span> timeout)</span> </span>{</span><br><span class="line"> Jedis conn = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 获取连接</span></span><br><span class="line"> conn = jedisPool.getResource();</span><br><span class="line"> <span class="comment">// 随机生成一个value</span></span><br><span class="line"> String identifier = UUID.randomUUID().toString();</span><br><span class="line"> <span class="comment">// 锁名,即 key值</span></span><br><span class="line"> String lockKey = KEY_PREF + lockName;</span><br><span class="line"> <span class="comment">// 超时时间, 上锁后超过此时间则自动释放锁</span></span><br><span class="line"> <span class="keyword">int</span> lockExpire = (<span class="keyword">int</span>) (timeout / <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取锁的超时时间,超过这个时间则放弃获取锁</span></span><br><span class="line"> <span class="keyword">long</span> end = System.currentTimeMillis() + acquireTimeout;</span><br><span class="line"> <span class="keyword">while</span> (System.currentTimeMillis() < end) {</span><br><span class="line"> <span class="keyword">if</span> (conn.setnx(lockKey, identifier) == <span class="number">1</span>) {</span><br><span class="line"> conn.expire(lockKey, lockExpire);</span><br><span class="line"> <span class="comment">// 返回value值,用于释放锁时间确认</span></span><br><span class="line"> <span class="keyword">return</span> identifier;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 返回-1代表key没有设置超时时间,为key设置一个超时时间</span></span><br><span class="line"> <span class="keyword">if</span> (conn.ttl(lockKey) == -<span class="number">1</span>) {</span><br><span class="line"> conn.expire(lockKey, lockExpire);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">10</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException ie) {</span><br><span class="line"> Thread.currentThread().interrupt();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (JedisException e) {</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> <span class="keyword">if</span> (conn != <span class="keyword">null</span>) {</span><br><span class="line"> conn.close();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">**</span><br><span class="line"> * 释放锁</span><br><span class="line"> *</span><br><span class="line"> * <span class="meta">@param</span> lockName String 锁key</span><br><span class="line"> * <span class="meta">@param</span> identifier String 释放锁的标识</span><br><span class="line"> * <span class="meta">@return</span> <span class="keyword">boolean</span></span><br><span class="line"> */</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">releaseLock</span><span class="params">(String lockName, String identifier)</span> </span>{</span><br><span class="line"> Jedis conn = <span class="keyword">null</span>;</span><br><span class="line"> String lockKey = KEY_PREF + lockName;</span><br><span class="line"> <span class="keyword">boolean</span> retFlag = <span class="keyword">false</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> conn = jedisPool.getResource();</span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">true</span>) {</span><br><span class="line"> <span class="comment">// 监视lock, 准备开始事务</span></span><br><span class="line"> conn.watch(lockKey);</span><br><span class="line"> <span class="comment">// 通过前面返回的value值判断是不是该锁,若时该锁,则删除释放锁</span></span><br><span class="line"> <span class="keyword">if</span> (identifier.equals(conn.get(lockKey))) {</span><br><span class="line"> <span class="comment">// 开启redis事务</span></span><br><span class="line"> Transaction transaction = conn.multi();</span><br><span class="line"> transaction.del(lockKey);</span><br><span class="line"> <span class="comment">// 关闭redis事务</span></span><br><span class="line"> List<Object> results = transaction.exec();</span><br><span class="line"> <span class="keyword">if</span> (results == <span class="keyword">null</span>) <span class="keyword">continue</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> retFlag = <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> conn.unwatch();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> <span class="keyword">if</span> (conn != <span class="keyword">null</span>) {</span><br><span class="line"> conn.close();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> retFlag;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="基于Zookeeper的实现方式"><a href="#基于Zookeeper的实现方式" class="headerlink" title="基于Zookeeper的实现方式"></a>基于Zookeeper的实现方式</h5><p>基于Zookeeper临时有序节点同样可以实现分布式锁。</p><p>大致思想为:</p><blockquote><p>每个客户端对某个方法加锁时,在zookeeper上的该方法对应的指定节点目录下,生成一个唯一的瞬时有序节点。</p></blockquote><blockquote><p>判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。如果获取到比自己小的兄弟节点不存在,则说明当前线程顺序号最小,获得锁。</p></blockquote><blockquote><p>如果判断自己不是那最小的一个节点,则设置监听比自己次小的节点;</p></blockquote><blockquote><p>如果已处理完成,则删除自己的节点。</p></blockquote><p>优点:</p><blockquote><p>具备高可用、可重入、阻塞锁特性、可解决失效死锁问题。</p></blockquote><p>缺点:</p><blockquote><p>因为需要频繁的创建和删除节点,性能上不如Redis方式。</p></blockquote><h5 id="三种方案的比较"><a href="#三种方案的比较" class="headerlink" title="三种方案的比较"></a>三种方案的比较</h5><p>从理解的难易程度(从低到高):</p><blockquote><p>数据库 > 缓存 > Zookeeper</p></blockquote><p>从实现的复杂性角度(从低到高):</p><blockquote><p>Zookeeper >= 缓存 > 数据库</p></blockquote><p>从性能角度(从高到低):</p><blockquote><p>缓存 > Zookeeper >= 数据库</p></blockquote><p>从可靠性角度(从高到低):</p><blockquote><p>Zookeeper > 缓存 > 数据库</p></blockquote>]]></content>
<categories>
<category> 分布式 </category>
</categories>
<tags>
<tag> 分布式 </tag>
</tags>
</entry>
<entry>
<title>Rainbow</title>
<link href="/about/index.html"/>
<url>/about/index.html</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><h2 id="个人介绍"><a href="#个人介绍" class="headerlink" title="个人介绍"></a>个人介绍</h2><p><code>Design, to build a simpler world</code></p><blockquote><p> 工作刚满一年,Java开发。</p><p>对一切不了解的事物都感兴趣,做这个博客也是觉得好玩。</p><p>人生三大乐趣:音乐,电影,羽毛球</p></blockquote><h2 id="联系方式"><a href="#联系方式" class="headerlink" title="联系方式"></a>联系方式</h2><p>GitHub: <a href="https://github.com/liuHongJie1217" target="_blank" rel="noopener">rainbow</a></p><p>微博:<a href="https://weibo.com/7336185047/profile?topnav=1&wvr=6&is_all=1" target="_blank" rel="noopener">存在即合理云云</a></p><p>邮箱:<a href="mailto:[email protected]" target="_blank" rel="noopener">[email protected]</a></p>]]></content>
</entry>
<entry>
<title>友情链接</title>
<link href="/link/index.html"/>
<url>/link/index.html</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script>]]></content>
</entry>
<entry>
<title>分类</title>
<link href="/categories/index.html"/>
<url>/categories/index.html</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script>]]></content>
</entry>
<entry>
<title>音乐分享</title>
<link href="/music/index.html"/>
<url>/music/index.html</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><!-- <iframe align="middle" frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=450 src="www.xiami.com/collect/796705375"></iframe> --> <div id="aplayer-cTLrwlVY" class="aplayer aplayer-tag-marker meting-tag-marker" data-id="1183138835" data-server="xiami" data-type="playlist" data-mode="circulation" data-autoplay="false" data-mutex="true" data-listmaxheight="340px" data-preload="auto" data-theme="#FF4081" ></div>]]></content>
</entry>
<entry>
<title>标签</title>
<link href="/tags/index.html"/>
<url>/tags/index.html</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script>]]></content>
</entry>
<entry>
<title>视频分享</title>
<link href="/video/index.html"/>
<url>/video/index.html</url>
<content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><script class="meting-secondary-script-marker" src="/assets/js/Meting.min.js"></script><!-- <div id="dplayer0" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer0"),"theme":"#FADFA3","loop":true,"video":{"url":"https://www.xiami.com/mv/K6YhqC","pic":"https://i.loli.net/2019/11/13/LFB9uVNzpSbH82a.jpg"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script> --><div id="dplayer1" class="dplayer hexo-tag-dplayer-mark" style="margin-bottom: 20px;"></div><script>(function(){var player = new DPlayer({"container":document.getElementById("dplayer1"),"theme":"#FADFA3","loop":true,"video":{"url":"/img/video.mp4","pic":"https://i.loli.net/2019/11/13/LFB9uVNzpSbH82a.jpg"}});window.dplayers||(window.dplayers=[]);window.dplayers.push(player);})()</script>]]></content>
</entry>
</search>