[Redis数据结构|Java实现] 二:字典(Map)

[Redis数据结构|Java实现] 二:字典(Map)

概念:Redis中的字典,底层是使用hashtable实现,一个哈希表里面可以有多个 哈希表节点,每个哈希表节点都保存着一个键值对。

实现思路:

主要考虑以下几个问题:

  • 哈希表怎么实现?

  • 哈希表节点怎么定义?

  • Map的常规操作怎么实现?

    • put实现
    • get实现
    • replace实现
  • 怎么实现扩容?

哈希表实现:数组+链表

哈希表底层直接用数组+链表的经典实现方式即可。

为什么这样实现呢?

哈希表的实现主要要考虑以下两个方面?
  1. 计算好哈希值之后,就可以知道需要存储到对应桶的位置,那么如何快速定位到需要存储到的桶的位置。
  2. 对于哈希值同样的数据,怎么存储?(怎么解决哈希冲突)
解决方案:

针对第一个问题:

使用数组来存储桶,则计算好索引的位置之后,直接获取数组指定的位置即可,时间复杂度为O(1)。

第二个问题:

哈希值同样的数据,直接用链表存储即可。

在这里插入图片描述

哈希表节点定义

首先哈希表节点需要存储键值对,所以肯定需要保存key,value

其次还需要保存它的下一个节点,所以肯定需要一个 next指针。(c里面叫指针)

所以可以定义成以下结构:

private static class Node{
   
    String key;
    Node next;
    String val;

    public Node(String key,String val){
   
        this.key = key;
        this.next = null;
        this.val = val;
    }
}

Map的常规操作实现

put实现

put的实现思路:put(key,val),首先计算key的hashcode,然后利用hashcode对数组的长度取模,得到桶的下标,然后去遍历这个桶里面的元素,如果没有同样的(equal判断),就加到这个桶保存的链表的尾部即可。

/**
 * 这里设置,如果已经存在同样的键,则添加时直接失败,需要替换的话,直接调用replace函数
 * @param key
 * @param val
 * @return
 */
public boolean put(String key,String val){
   

    int index = key.hashCode()%buf.length;
     Node node = new  Node(key,val);
    if(buf[index] == null){
   
        buf[index] = node;
        used++;
    }else {
   //使用链表存储
        if(isContain(buf[index],key))
            return false;
        addOnLast(buf[index], node);
    }
    keyNumber++;
    return true;
}
get实现(不要去遍历所有节点)

get的实现思路:get(key),首先计算key的hashcode,然后利用hashcode对数组的长度取模,得到桶的下标,然后去遍历这个桶里面的元素,如果没有同样的(equal判断),直接返回null,否则返回对应节点的val即可。

public String get(String key){
   
    int index = key.hashCode()%buf.length;
    if(buf[index] == null)
        return null;
    Node root = buf[index];
    while (root != null){
   
        if(root.key.equals(key))
            return root.val;
        root = root.next;
    }
    return null;
}

扩容实现

首先,为什么需要扩容?

因为单哈希冲突比较多时,一个桶中存储的链表可能会很长,这时候即使定位到了桶的位置,也需要遍历这个很长的链表,这会造成比较大的开销。

Redis里面扩容实现

Redis里面字典的扩容实现是比较独特的,它通过使用两个hashtable(buf,buf2),然后进行扩容。

具体操作:先计算负载因子,当负载因子满足一定值时,则执行扩容操作。

具体扩容:给buf2分配内存,将buf2当成buf,然后将原先buf中的数据重新添加即可,最后把buf设置为buf2,方便下一次扩容。

/**
 *获取负载因子
 */
private int getLoadFactor(){
   
   return used/buf.length;
}

/**
 * 扩容操作
 */
public void rehash(){
   
    if(getLoadFactor() == 0){
   
        return;
    }

    buf2 = new Node[buf.length*2];//将其容量加倍

    Node[] temp = buf;
    buf = buf2;
    this.used = 0;
    this.keyNumber = 0;
    buf2 = null;

    for(int i=0;i< temp.length;i++){
   
        Node root = temp[i];
        if(root != null){
   //统计
            while (root != null){
   
                put(root.key,root.val);
                root = root.next;
            }
        }
    }

}

完整代码:

package redis.Map;


/**
 * Redis中字典的实现
 */
public class RedisMap {
   

    private static final int DEFAULT_SIZE = 5;
    
    private int used;

    private int keyNumber;
    
    //buf2用来做扩容操作
    private Node[] buf,buf2;
    
    
    public RedisMap(){
   
        buf = new Node[DEFAULT_SIZE];
    }
    public RedisMap(int capacity
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值