文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基于数组或链表实现Map

2024-12-03 08:56

关注

前言

JAVA中的Map主要就是将一个键和一个值联系起来。虽然JAVA中已经提供了很多Map的实现,为了学习并掌握常用的数据结构,从本篇开始我将自己实现Map的功能,本篇主要是通过数组和链表两种方式实现,之后提供二叉树,红黑树,散列表的版本实现。通过自己手写各个版本的Map实现,掌握每种数据结构的优缺点,可以在实际的工作中根据需要选择适合的Map。

Map API的定义

在开始之前,我们需要先定义出Map的接口定义,后续的版本都会基于此接口实现

  1. public interface Map { 
  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 keys(); 
  11.  
  12.     default boolean contains(K key) { 
  13.         return get(key) != null
  14.     } 
  15.  
  16.     default boolean isEmpty() { 
  17.         return size() == 0; 
  18.     } 

这个接口是最简单的一个Map定义,相信这些方法对于java程序员来说不会陌生;

基于链表实现Map

基于链表实现首先我们需要定义一个Node节点,表示我们需要存储的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方法的实现思路是遍历链表,然后比较每个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 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方法的实现思路也是遍历链表,然后比较每个Node的key值是否相等,如果相等那么覆盖掉value,如果未查找到有key相等的node,那么就新建一个Node放到链表的开头

  1. @Override 
  2. public void put(K key, V value) { 
  3.     Optional 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方法实现同样也需要遍历链表,因为我们的是单向链表,删除某个节点有两种思路,第一种,在遍历链表的时候记录下当前节点的上一个节点,把上一个节点的next指向当前节点next;第二种,当遍历到需要删除的节点时,把需要删除节点的next的key、value完全复制到需要删除的节点,把next指针指向next.next,比如:first - > A -> B -> C -> D -> E -> F -> G -> NULL,要删除 C 节点,就把D节点完全复制到c中,然后C -> E,变相删除了C

  1. @Override 
  2. public void delete(K key) { 
  3. // 第一种实现: 
  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. // 第二中实现: 
  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.     } 

分析上面基于链表实现的map,每次的put、get、delete都需要遍历整个链表,非常的低效,无法处理大量的数据,时间复杂度为O(N)

基于数组实现Map

基于链表的实现非常低效,因为每次操作都需要遍历链表,假如我们的数据是有序的,那么查找的时候我们可以使用二分查找法,那么get方法会加快很多

为了体现出我们的Map是有序的,我们需要重新定义一个有序的Map

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

该定义要求key必须实现接口Comparable,rank方法如果key值存在就返回对应在数组中的下标,如果不存在就返回小于key键的数量

  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方法实现:基于rank方法,判断返回的keys[index]与key进行比较,如果相等返回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方法实现:基于rank方法,判断返回的keys[index]与key进行比较,如果相等直接修改values[index]的值,如果不相等表示不存在该key,需要插入并且移动数组

  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方法实现:通过rank方法判断该key是否存在,如果不存在就直接返回,如果存在需要移动数组

  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--; 

基于数组实现的Map,虽然get方法采用的二分查找法,很快O(logN),但是在处理大量数据的情况下效率依然很低,因为put方法还是太慢;下篇我们将基于二叉树来实现Map,继续改进提升效率

 

文中所有源码已放入到了github仓库https://github.com/silently9527/JavaCore

 

来源:贝塔学JAVA内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯