自己动手实现java数据结构(五)哈希表
发布时间:2021-04-01 08:21:45 所属栏目:安全 来源:网络整理
导读:1.哈希表介绍 前面我们已经介绍了许多类型的数据结构。在想要查询容器内特定元素时,有序向量使得我们能使用二分查找法进行精确的查询(( O(logN)对数复杂度,很高效 )。 可人类总是不知满足,依然在寻求一种更高效的特定元素查询的数据结构, 哈希表/散列表(
|
哈希表ADT接口:
1 {
2 /**
3 * 存入键值对
4 * key key值
5 value value
6 被覆盖的的value值
7 */
8 V put(K key,V value);
9
10 11 * 移除键值对
12 13 被删除的value的值
14 15 V remove(K key);
16
17 18 * 获取key对应的value值
19 20 对应的value值
21 22 V get(K key);
23
24 25 * 是否包含当前key值
26 27 true:包含 false:不包含
28 29 containsKey(K key);
30
31 32 * 是否包含当前value值
33 value value值
34 true:包含 false:不包含
35 36 containsValue(V value);
37
38 39 * 获得当前map存储的键值对数量
40 键值对数量
41 * 42 size();
43
44 45 * 当前map是否为空
46 true:为空 false:不为空
47 48 isEmpty();
49
50 51 * 清空当前map
52 53 clear();
54
55 56 * 获得迭代器
57 迭代器对象
58 59 Iterator<EntryNode<K,1)"> iterator();
60
61 62 * 键值对节点 内部类
63 64 65 K key;
66 V value;
67 EntryNode<K,1)"> next;
68
69 EntryNode(K key,V value) {
70 key;
71 value;
72 }
73
74 keyIsEquals(K key){
75 key){
76 ;
77 }
78
79 ){
80 :::如果走到这步,this.key不等于null,不匹配
81 82 } 83 .key);
84 85 86
87 EntryNode<K,1)"> getNext() {
88 89 90
91 next) {
92 93 94
95 K getKey() {
96 97 98
99 V getValue() {
100 101 102
103 setValue(V value) {
104 105 106
107 @Override
108 String toString() {
109 110 111 }
112 }
View Code
哈希表实现:
2
3 ===========================================成员属性================================================
4 * 内部数组
7 [] elements;
8
9 10 * 当前哈希表的大小
12 size;
13
14 * 负载因子
16 loadFactor;
18
19 * 默认的哈希表容量
21 22 * 扩容翻倍的基数 两倍
27 28
30 * 默认的负载因子
31 32 33
34 ========================================构造方法===================================================
35 36 * 默认构造方法
37 38 @SuppressWarnings("unchecked")
39 HashMap() {
40 41 DEFAULT_LOAD_FACTOR;
42 elements = EntryNode[DEFAULT_CAPACITY];
43 44
45 * 指定初始容量的构造方法
47 capacity 指定的初始容量
48 49 @SuppressWarnings("unchecked" capacity) {
51 52 53 elements = EntryNode[capacity];
54 55
56 * 指定初始容量和负载因子的构造方法
58 59 loadFactor 指定的负载因子
60 61 @SuppressWarnings("unchecked" 62 loadFactor) {
63 64 65 elements = 67
68 ==========================================内部辅助方法=============================================
69 70 * 通过key的hashCode获得对应的内部数组下标
71 key 传入的键值key
对应的内部数组下标
73 74 getIndex(K key){
75 .elements);
76 77
78 79 * 通过key的hashCode获得对应的内部数组插槽slot下标
80 81 elements 内部数组
82 83 84 [] elements){
85 86 ::: null 默认存储在第0个桶内
87 88 } 89 key.hashCode();
91 :::通过 高位和低位的异或运算,获得最终的hash映射,减少碰撞的几率
);
93 finalHashCode;
94 95 96
97 98 * 获得目标节点的前一个节点
99 currentNode 当前桶链表节点
100 key 对应的key
返回当前桶链表中"匹配key的目标节点"的"前一个节点"
102 * 注意:当桶链表中不存在匹配节点时,返回桶链表的最后一个节点
103 104 105 :::不匹配
106 EntryNode<K,1)"> currentNode.next;
107 :::遍历当前桶后面的所有节点
:::如果下一个节点的key匹配
110 (nextNode.keyIsEquals(key)){
111 currentNode;
112 }113 :::不断指向下一个节点
114 currentNode = nextNode;
115 nextNode = nextNode.next;
116 117 118
119 :::到达了桶链表的末尾,返回最后一个节点
120 121 122
123 124 * 哈希表扩容
125 126 @SuppressWarnings("unchecked"127 reHash(){
128 :::扩容两倍
129 EntryNode<K,1)"> REHASH_BASE];
130
131 :::遍历所有的插槽
132 ) {
133 134 135 136
137 :::内部数组 ---> 扩容之后的新数组
138 newElements;
139 140
141 142 * 单个插槽内的数据进行rehash
143 144 [] newElements){
145 :::获得当前插槽第一个元素
146 EntryNode<K,1)">.elements[index];
147 148 :::当前插槽为空,直接返回
149 150 151
152 :::低位桶链表 头部节点、尾部节点
153 EntryNode<K,1)">154 EntryNode<K,1)">155 :::高位桶链表 头部节点、尾部节点
156 EntryNode<K,1)">157 EntryNode<K,1)">158
159 160 :::获得当前节点 在新数组中映射的插槽下标
161 162 :::是否和当前插槽下标相等
163 index){
164 :::和当前插槽下标相等
165 166 :::初始化低位链表
167 lowListHead = currentEntryNode;
168 lowListTail =169 }170 :::在低位链表尾部拓展新的节点
171 lowListTail.next =172 lowListTail = lowListTail.next;
173 }
174 }175 :::和当前插槽下标不相等
176 177 :::初始化高位链表
178 highListHead =179 highListTail =180 }181 :::在高位链表尾部拓展新的节点
182 highListTail.next =183 highListTail = highListTail.next;
184 185 186 :::指向当前插槽的下一个节点
187 currentEntryNode = currentEntryNode.next;
188 189
190 :::新扩容elements(index)插槽 存放lowList
191 newElements[index] = lowListHead;
192 :::lowList末尾截断
193 194 lowListTail.next = 195 196
197 :::新扩容elements(index + this.elements.length)插槽 存放highList
198 newElements[index + highListHead;
199 :::highList末尾截断
200 201 highListTail.next = 202 203 204
205 206 * 判断是否需要 扩容
207 208 needReHash(){
209 .loadFactor);
210 211
212 ============================================外部接口================================================
213
214 @Override
215 216 (needReHash()){
217 reHash();
218 219
220 :::获得对应的内部数组下标
221 getIndex(key);
222 :::获得对应桶内的第一个节点
223 EntryNode<K,1)">224
225 :::如果当前桶内不存在任何节点
226 227 :::创建一个新的节点
228 229 :::创建了新节点,size加1
230 231 232 233
234 (firstEntryNode.keyIsEquals(key)){
235 :::当前第一个节点的key与之匹配
236 V oldValue = firstEntryNode.value;
237 firstEntryNode.value =238 oldValue;
239 }240 :::不匹配
241
242 :::获得匹配的目标节点的前一个节点
243 EntryNode<K,key);
244 :::获得匹配的目标节点
245 EntryNode<K,1)"> targetPreviousNode.next;
246 247 :::更新value的值
248 V oldValue = targetNode.value;
249 targetNode.value =250 251 }252 :::在桶链表的末尾 新增一个节点
253 targetPreviousNode.next = 254 255 256 257 258 259 260
261 262 V remove(K key) {
263 264 265 266 EntryNode<K,1)">267
268 269 270 271 272
273 274 :::当前第一个节点的key与之匹配
275
276 :::将桶链表的第一个节点指向后一个节点(兼容next为null的情况)
277 firstEntryNode.next;
278 :::移除了一个节点 size减一
279 280 :::返回之前的value值
281 282 }283 284
285 286 EntryNode<K,1)">287 288 EntryNode<K,1)">289
290 291 :::将"前一个节点的next" 指向 "目标节点的next" ---> 相当于将目标节点从桶链表移除
292 targetPreviousNode.next = targetNode.next;
293 294 295 296 }297 :::如果目标节点为空,说明key并不存在于哈希表中
298 299 300 301 302
303 304 V get(K key) {
305 306 307 308 EntryNode<K,1)">309
310 311 312 313 314
315 316 317 318 }319 320 EntryNode<K,1)">321 322 EntryNode<K,1)">323
324 325 326 }327 328 329 330 331 332
333 334 containsKey(K key) {
335 V value = get(key);
336 337 338
339 340 containsValue(V value) {
341 :::遍历全部桶链表
342 .elements) {
343 :::获得当前桶链表第一个节点
344 EntryNode<K,1)"> element;
345
346 :::遍历当前桶链表
347 348 :::如果value匹配
349 (entryNode.value.equals(value)) {
350 :::返回true
351 352 } {
353 :::不匹配,指向下一个节点
354 entryNode = entryNode.next;
355 356 357 358
359 :::所有的节点都遍历了,没有匹配的value
360 361 362
363 364 size() {
365 .size;
366 367
368 369 isEmpty() {
370 371 372
373 374 clear() {
375 :::遍历内部数组,将所有桶链表全部清空
376 377 378 379
380 :::size设置为0
381 382 383
384 385 iterator() {
386 Itr();
387 388
389 390 391 Iterator<EntryNode<K,1)">.iterator();
392
393 :::空容器
394 iterator.hasNext()){
395 396 397
398 :::容器起始使用"["
399 StringBuilder s = 400
401 :::反复迭代
402 403 :::获得迭代的当前元素
404 EntryNode<K,1)"> iterator.next();
405
406 :::判断当前元素是否是最后一个元素
407 408 :::是最后一个元素,用"]"收尾
409 s.append(data).append("]"410 :::返回 拼接完毕的字符串
411 s.toString();
412 }413 :::不是最后一个元素
414
415 s.append(data).append(",1)">416 417 418 419
420 421 * 哈希表 迭代器实现
422 423 424 425 * 迭代器 当前节点
426 * 427 428
429 430 * 迭代器 下一个节点
431 432 433
434 435 * 迭代器 当前内部数组的下标
436 437 currentIndex;
438
439 440 * 默认构造方法
441 442 Itr(){
443 :::如果当前哈希表为空,直接返回
444 .isEmpty()){
445 446 447 :::在构造方法中,将迭代器下标移动到第一个有效的节点上
448
449 :::遍历内部数组,找到第一个不为空的数组插槽slot
450 451 :::设置当前index
452 i;
453
454 EntryNode<K,1)">.elements[i];
455 :::找到了第一个不为空的插槽slot
456 457 :::nextNode = 当前插槽第一个节点
458 firstEntryNode;
459
460 :::构造方法立即结束
461 462 463 464 465
466 467 hasNext() {
468 469 470
471 472 next() {
473 .nextNode;
474 :::暂存需要返回的节点
475 EntryNode<K,1)">476
477 :::nextNode指向自己的next
478 .nextNode.next;
479 :::判断当前nextNode是否为null
480 481 :::说明当前所在的桶链表已经遍历完毕
482
483 :::寻找下一个非空的插槽
484 485 486 487
488 EntryNode<K,1)">489 :::找到了后续不为空的插槽slot
490 491 492 493 :::跳出循环
494 495 }
496 497 498 needReturn;
499 500
501 502 remove() {
503 504 505 506
507 :::获得需要被移除的节点的key
508 K currentKey = .currentNode.key;
509 :::将其从哈希表中移除
510 HashMap..remove(currentKey);
511
512 :::currentNode设置为null,防止反复调用remove方法
513 514 515 516 }
View Code (编辑:清远站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |





