自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于數(shù)組或鏈表實(shí)現(xiàn)Map

開發(fā) 后端
本篇主要是通過數(shù)組和鏈表兩種方式實(shí)現(xiàn),之后提供二叉樹,紅黑樹,散列表的版本實(shí)現(xiàn)。通過自己手寫各個(gè)版本的Map實(shí)現(xiàn),掌握每種數(shù)據(jù)結(jié)構(gòu)的優(yōu)缺點(diǎn),可以在實(shí)際的工作中根據(jù)需要選擇適合的Map。

[[388024]]

本文轉(zhuǎn)載自微信公眾號(hào)「貝塔學(xué)JAVA」,作者Silently9527。轉(zhuǎn)載本文請(qǐng)聯(lián)系貝塔學(xué)JAVA公眾號(hào)。

前言

JAVA中的Map主要就是將一個(gè)鍵和一個(gè)值聯(lián)系起來。雖然JAVA中已經(jīng)提供了很多Map的實(shí)現(xiàn),為了學(xué)習(xí)并掌握常用的數(shù)據(jù)結(jié)構(gòu),從本篇開始我將自己實(shí)現(xiàn)Map的功能,本篇主要是通過數(shù)組和鏈表兩種方式實(shí)現(xiàn),之后提供二叉樹,紅黑樹,散列表的版本實(shí)現(xiàn)。通過自己手寫各個(gè)版本的Map實(shí)現(xiàn),掌握每種數(shù)據(jù)結(jié)構(gòu)的優(yōu)缺點(diǎn),可以在實(shí)際的工作中根據(jù)需要選擇適合的Map。

Map API的定義

在開始之前,我們需要先定義出Map的接口定義,后續(xù)的版本都會(huì)基于此接口實(shí)現(xiàn)

  1. public interface Map<K, V> { 
  2.     void put(K key, V value); 
  3.  
  4.     V get(K key); 
  5.  
  6.     void delete(K key); 
  7.      
  8.     int size(); 
  9.  
  10.     Iterable<K> keys(); 
  11.  
  12.     default boolean contains(K key) { 
  13.         return get(key) != null
  14.     } 
  15.  
  16.     default boolean isEmpty() { 
  17.         return size() == 0; 
  18.     } 

這個(gè)接口是最簡(jiǎn)單的一個(gè)Map定義,相信這些方法對(duì)于java程序員來說不會(huì)陌生;

基于鏈表實(shí)現(xiàn)Map

基于鏈表實(shí)現(xiàn)首先我們需要定義一個(gè)Node節(jié)點(diǎn),表示我們需要存儲(chǔ)的key、vlaue

  1. class Node { 
  2.     K key
  3.     V value; 
  4.     Node next
  5.  
  6.     public Node(K key, V value, Node next) { 
  7.         this.key = key
  8.         this.value = value; 
  9.         this.next = next
  10.     } 
  11.  

get方法的實(shí)現(xiàn)思路是遍歷鏈表,然后比較每個(gè)Node中的key是否相等,如果相等就返回value,否則返回null

  1. @Override 
  2. public V get(K key) { 
  3.     return searchNode(key).map(node -> node.value).orElse(null); 
  4.  
  5. public Optional<Node> searchNode(K key) { 
  6.     for (Node node = root; node != null; node = node.next) { 
  7.         if (node.key.equals(key)) { 
  8.             return Optional.of(node); 
  9.         } 
  10.     } 
  11.     return Optional.empty(); 

put方法的實(shí)現(xiàn)思路也是遍歷鏈表,然后比較每個(gè)Node的key值是否相等,如果相等那么覆蓋掉value,如果未查找到有key相等的node,那么就新建一個(gè)Node放到鏈表的開頭

  1. @Override 
  2. public void put(K key, V value) { 
  3.     Optional<Node> optionalNode = searchNode(key); 
  4.  
  5.     if (optionalNode.isPresent()) { 
  6.         optionalNode.get().value = value; 
  7.         return
  8.     } 
  9.     this.root = new Node(key, value, root); 

delete方法實(shí)現(xiàn)同樣也需要遍歷鏈表,因?yàn)槲覀兊氖菃蜗蜴湵?,刪除某個(gè)節(jié)點(diǎn)有兩種思路,第一種,在遍歷鏈表的時(shí)候記錄下當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn),把上一個(gè)節(jié)點(diǎn)的next指向當(dāng)前節(jié)點(diǎn)next;第二種,當(dāng)遍歷到需要?jiǎng)h除的節(jié)點(diǎn)時(shí),把需要?jiǎng)h除節(jié)點(diǎn)的next的key、value完全復(fù)制到需要?jiǎng)h除的節(jié)點(diǎn),把next指針指向next.next,比如:first - > A -> B -> C -> D -> E -> F -> G -> NULL,要?jiǎng)h除 C 節(jié)點(diǎn),就把D節(jié)點(diǎn)完全復(fù)制到c中,然后C -> E,變相刪除了C

  1. @Override 
  2. public void delete(K key) { 
  3. // 第一種實(shí)現(xiàn): 
  4. //        for (Node node = first, preNode = null; node != null; preNode = node, node = node.next) { 
  5. //            if (node.key.equals(key)) { 
  6. //                if (Objects.isNull(preNode)) { 
  7. //                    first = first.next
  8. //                } else { 
  9. //                    preNode.next = node.next
  10. //                } 
  11. //            } 
  12. //        } 
  13.  
  14. // 第二中實(shí)現(xiàn): 
  15.     for (Node node = first; node != null; node = node.next) { 
  16.         if (node.key.equals(key)) { 
  17.             Node next = node.next
  18.             node.key = next.key
  19.             node.value =next.value; 
  20.             node.next = next.next
  21.         } 
  22.     } 

分析上面基于鏈表實(shí)現(xiàn)的map,每次的put、get、delete都需要遍歷整個(gè)鏈表,非常的低效,無法處理大量的數(shù)據(jù),時(shí)間復(fù)雜度為O(N)

基于數(shù)組實(shí)現(xiàn)Map

基于鏈表的實(shí)現(xiàn)非常低效,因?yàn)槊看尾僮鞫夹枰闅v鏈表,假如我們的數(shù)據(jù)是有序的,那么查找的時(shí)候我們可以使用二分查找法,那么get方法會(huì)加快很多

為了體現(xiàn)出我們的Map是有序的,我們需要重新定義一個(gè)有序的Map

  1. public interface SortedMap<K extends Comparable<K>, V> extends Map<K, V> { 
  2.     int rank(K key); 

該定義要求key必須實(shí)現(xiàn)接口Comparable,rank方法如果key值存在就返回對(duì)應(yīng)在數(shù)組中的下標(biāo),如果不存在就返回小于key鍵的數(shù)量

  • 在基于數(shù)組的實(shí)現(xiàn)中,我們會(huì)定義兩個(gè)數(shù)組變量分部存放keys、values;
  • rank方法的實(shí)現(xiàn):由于我們整個(gè)數(shù)組都是有序的,我們可以二分查找法(可以查看《老哥是時(shí)候來復(fù)習(xí)下數(shù)據(jù)結(jié)構(gòu)與算法了》),如果存在就返回所在數(shù)組的下表,如果不存在就返回0
  1. @Override 
  2. public int rank(K key) { 
  3.     int lo = 0, hi = size - 1; 
  4.     while (lo <= hi) { 
  5.         int mid = (hi - lo) / 2 + lo; 
  6.         int compare = key.compareTo(keys[mid]); 
  7.         if (compare > 0) { 
  8.             lo = mid + 1; 
  9.         } else if (compare < 0) { 
  10.             hi = mid - 1; 
  11.         } else { 
  12.             return mid; 
  13.         } 
  14.     } 
  15.     return lo; 

get方法實(shí)現(xiàn):基于rank方法,判斷返回的keys[index]與key進(jìn)行比較,如果相等返回values[index],不相等就返回null

  1. @Override 
  2. public V get(K key) { 
  3.     int index = this.rank(key); 
  4.     if (index < size && key.compareTo(keys[index]) == 0) { 
  5.         return values[index]; 
  6.     } 
  7.     return null

put方法實(shí)現(xiàn):基于rank方法,判斷返回的keys[index]與key進(jìn)行比較,如果相等直接修改values[index]的值,如果不相等表示不存在該key,需要插入并且移動(dòng)數(shù)組

  1. @Override 
  2. public void put(K key, V value) { 
  3.     int index = this.rank(key); 
  4.     if (index < size && key.compareTo(keys[index]) == 0) { 
  5.         values[index] = value; 
  6.         return
  7.     } 
  8.  
  9.     for (int j = size; j > index; j--) { 
  10.         this.keys[j] = this.keys[j--]; 
  11.         this.values[j] = this.values[j--]; 
  12.     } 
  13.     keys[index] = key
  14.     values[index] = value; 
  15.     size++; 

delete方法實(shí)現(xiàn):通過rank方法判斷該key是否存在,如果不存在就直接返回,如果存在需要移動(dòng)數(shù)組

  1. @Override 
  2. public void delete(K key) { 
  3.     int index = this.rank(key); 
  4.     if (Objects.isNull(keys[index]) || key.compareTo(keys[index]) != 0) { 
  5.         return
  6.     } 
  7.     for (int j = index; j < size - 1; j++) { 
  8.         keys[j] = keys[j + 1]; 
  9.         values[j] = values[j + 1]; 
  10.     } 
  11.     keys[size - 1] = null
  12.     values[size - 1] = null
  13.     size--; 

基于數(shù)組實(shí)現(xiàn)的Map,雖然get方法采用的二分查找法,很快O(logN),但是在處理大量數(shù)據(jù)的情況下效率依然很低,因?yàn)閜ut方法還是太慢;下篇我們將基于二叉樹來實(shí)現(xiàn)Map,繼續(xù)改進(jìn)提升效率

 

文中所有源碼已放入到了github倉庫https://github.com/silently9527/JavaCore

 

責(zé)任編輯:武曉燕 來源: 貝塔學(xué)JAVA
相關(guān)推薦

2009-11-25 10:31:35

PHP數(shù)組實(shí)現(xiàn)單鏈表

2023-12-28 10:39:57

數(shù)組節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)

2021-04-13 08:12:33

拉鏈?zhǔn)?/a>Map探測(cè)式

2023-12-22 08:56:01

Java編程語言鏈表

2023-03-01 07:37:10

數(shù)組鏈表性能

2020-02-07 11:07:53

數(shù)組鏈表單鏈表

2022-05-07 09:47:07

Babylon.js微軟元宇宙

2020-12-22 14:11:45

JS forEach()map()

2022-04-20 11:14:55

Python列表數(shù)組

2022-03-10 17:02:51

Rust單鏈表數(shù)據(jù)結(jié)構(gòu)

2021-11-02 09:05:25

Redis

2022-06-16 07:50:35

數(shù)據(jù)結(jié)構(gòu)鏈表

2019-08-14 10:20:32

算法數(shù)組鏈表

2023-10-10 08:39:25

Java 7Java 8

2022-09-08 09:42:26

JavaScripMapObject

2023-11-23 06:50:08

括號(hào)

2023-12-22 13:58:00

C++鏈表開發(fā)

2010-02-06 09:46:46

C++單向鏈表

2024-11-22 15:00:00

開源Redis鏈表

2021-05-07 08:20:52

前端開發(fā)技術(shù)熱點(diǎn)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)