数据结构之哈希表-JAVA代码实现

数据结构之哈希表-JAVA代码实现原文地址,点击访问

哈希表(散列表)

key value 形式的数据结构,其基础是一个数组。哈希表在特定元素的插入,删除和查询时都能够达到O(1)常数的时间复杂度,除非发生hash碰撞

  • 哈希算法

    把任意长度的输入通过哈希算法转换映射为固定长度的输出,所得到的输出被称为哈希值

  • 哈希冲突解决

    • 开放地址法
    • 重新哈希法
    • 拉链法
  • 负载因子

    负载因子代表着存储的总数据量内部数组大小比值。插入新数据时,判断哈希表当前的存储量和内部数组的比值是否超过了负载因子。当比值超过了负载因子时,哈希表认为内部过于拥挤,查询效率太低,会触发一次扩容操作。

    指定的负载因子越大,哈希表越拥挤(负载高,紧凑),查询效率越低,空间效率越高。

    指定的负载因子越小,哈希表越稀疏(负载小,松散),查询效率越高,空间效率越低。

  • 代码(使用了拉链法解决hash冲突)

    • 示例

      public static void main(String[] args) {
      
              MyMap<String, String> map = new MyHashMap<>(10);
      
              map.put("zlz","zlz");
              map.put("zlz","zlz-zlz");
              map.put("zlz1","zlz1");
              map.put("zlz2","zlz2");
              map.put("zlz3","zlz3");
              map.put("zlz4","zlz4");
      
              System.out.println("-----------remove-test-------------");
              System.out.println("Key-zlz4存在:" + map.containsKey("zlz4"));
              map.remove("zlz4");
              System.out.println("Key-zlz4存在:" + map.containsKey("zlz4"));
              System.out.println("-----------containsValue-test-----------");
      
              System.out.println("Value-zlz存在:" + map.containsValue("zlz"));
              System.out.println("Value-zlz-zlz存在:" + map.containsValue("zlz-zlz"));
      }
      
      out:
          -----------remove-test-------------
          Key-zlz4存在:true
          移除了元素:zlz4
          Key-zlz4存在:false
          -----------containsValue-test-----------
          Value-zlz存在:false
          Value-zlz-zlz存在:true
      
      
    • MyMap(接口+内部类EntryNode(键值对))

      package hash;
      
      /**
       * @author zlz
       * @version 1.0 CreateTime:2020-11-11 16:45
       * @description hash表,java中的Map结构
       */
      public interface MyMap<K, V> {
      
          /**
           * 存入元素
           * @param k key
           * @param v value
           * @return key
           */
          V put(K k, V v);
      
          /**
           * 通过key移除元素
           * @param k key
           * @return value
           */
          V remove(K k);
      
          /**
           * 通过key获取value
           * @param k key
           * @return value
           */
          V get(K k);
      
          /**
           * 是否包含该key
           * @param k key
           * @return boolean
           */
          boolean containsKey(K k);
      
          /**
           * 是否包含该value
           * @param v value
           * @return boolean
           */
          boolean containsValue(V v);
      
          /**
           * 元素个数
           * @return 元素个数
           */
          int size();
      
          /**
           * hash表是否为空
           * @return boolean
           */
          boolean isEmpty();
      
          /**
           * 清空hash表
           * @return 成功或者失败
           */
          void clear();
      
          /**
           * 键值对类型,表示一个节点
           * @param <K>
           * @param <V>
           */
          class EntryNode<K, V>{
              K key;
              V value;
              EntryNode<K,V> next;
      
              EntryNode(K key, V value) {
                  assert key != null;
                  this.key = key;
                  this.value = value;
              }
      
              /**
               * 判断传入的key与对象的key是否相等
               * @param k key
               * @return boolean
               */
              boolean equalsKey(K k){
                  return this.key.equals(k);
              }
      
              public K getKey() {
                  return key;
              }
      
              public void setKey(K key) {
                  this.key = key;
              }
      
              public V getValue() {
                  return value;
              }
      
              public void setValue(V value) {
                  this.value = value;
              }
      
              public EntryNode<K, V> getNext() {
                  return next;
              }
      
              public void setNext(EntryNode<K, V> next) {
                  this.next = next;
              }
      
              @Override
              public String toString() {
                  return "{" +
                          "key=" + key +
                          ", value=" + value +
                          '}';
              }
          }
      }
      
      
    • MyHashMap(实现类)

      /**
       * @author zlz
       * @version 1.0 CreateTime:2020-11-11 17:00
       * @description hashmap
       */
      public class MyHashMap<K,V> implements MyMap<K, V>{
      
          /**
           * 存储所有的键值对
           */
          private EntryNode<K, V>[] nodes;
      
          /**
           * 当前哈希表的大小
           * */
          private int size;
      
          /**
           * 负载因子,负载因子决定了hash表何时进行resize(扩容)操作
           */
          private final float loadFactor;
      
          /**
           * 默认的哈希表容量
           */
          private final static int DEFAULT_CAPACITY = 16;
      
          /**
           * 扩容翻倍的基数
           */
          private final static int REHASH_BASE = 2;
      
          /**
           * 默认的负载因子
           */
          private final static float DEFAULT_LOAD_FACTOR = 0.75f;
      
          /**
           * 使用默认容量初始化
           */
          public MyHashMap() {
              this.size = 0;
              this.loadFactor = DEFAULT_LOAD_FACTOR;
              nodes = new EntryNode[DEFAULT_CAPACITY];
          }
      
          /**
           * 指定初始化的容量大小
           * @param capacity 初始化的容量
           */
          public MyHashMap(int capacity) {
              this.size = 0;
              this.loadFactor = DEFAULT_LOAD_FACTOR;
              nodes = new EntryNode[capacity];
          }
      
          /**
           * 通过hash计算获取数组的下标
           * @param key key
           * @return hash桶坐标
           */
          private int getIndex(K key){
              return getIndex(key,this.nodes);
          }
      
          /**
           * 通过hash计算获取数组的下标
           * @param key key
           * @param nodes 节点
           * @return hash桶坐标
           */
          private int getIndex(K key,EntryNode<K,V>[] nodes){
              if(key == null){
                  return 0;
              }else{
                  int hashCode = key.hashCode();
                  // 异或运算,获得最终的hash映射,减少碰撞的几率
                  int finalHashCode = hashCode ^ (hashCode >>> 16);
                  return (nodes.length-1) & finalHashCode;
              }
          }
      
          /**
           * 试图获得目标节点,获取不到则返回链表的最后一个节点
           * @param currentNode 链表的第一个节点
           * @param key key
           * @return EntryNode
           */
          private EntryNode<K,V> getTargetEntryNode(EntryNode<K,V> currentNode, K key){
              // 不匹配
              EntryNode<K,V> nextNode = currentNode.next;
              // 遍历当前桶后面的所有节点
              while(true){
                  if(nextNode == null || currentNode.key.equals(key)){
                      return currentNode;
                  }
                  currentNode = nextNode;
                  nextNode = currentNode.next;
              }
          }
      
          @Override
          public V put(K k, V v) {
              //创建键值对
              EntryNode<K, V> entryNode = new EntryNode<>(k, v);
              //通过key获取数组的下标
              int index = getIndex(k);
              EntryNode<K, V> firstNode = this.nodes[index];
      
              //若是此节点不存在数据,则直接插入该数据作为第一个节点
              if(firstNode == null){
                  this.nodes[index] = entryNode;
                  this.size++;
                  if(this.size > this.nodes.length * this.loadFactor){
                      this.resize();
                  }
                  return v;
              }
      
              // 若是此桶的第一个节点就是当前的key,就将该key对应的value替换掉,并且返回此oldValue
              if(firstNode.equalsKey(k)){
                  V oldValue = firstNode.value;
                  firstNode.value = v;
                  return oldValue;
              //若是不匹配,则去链表的下一个节点查找,若是全部不匹配,则新增一个节点放在链表的末尾
              }else {
                  EntryNode<K, V> targetEntryNode = getTargetEntryNode(firstNode, k);
                  // 若是查找到了key相同的节点,则替换节点的value
                  if(targetEntryNode.key.equals(k)){
                      V oldValue = targetEntryNode.value;
                      targetEntryNode.value = v;
                      return oldValue;
                  //未查询到匹配的节点
                  }else {
                      targetEntryNode.next = entryNode;
                      this.size++;
                      if(this.size > this.nodes.length * this.loadFactor){
                          this.resize();
                      }
                      return v;
                  }
              }
          }
      
          @Override
          public V remove(K k) {
      
              int index = getIndex(k);
              EntryNode<K, V> firstNode = this.nodes[index];
      
              if(null == firstNode){
                  return null;
              }
      
              // 链表第一个节点即为要移除的节点
              if(firstNode.key.equals(k)){
                  this.nodes[index] = firstNode.next;
                  System.out.println("移除了元素:" + k);
                  this.size --;
                  return firstNode.value;
              // 链表第一个节点不是要移除的节点
              } else {
                  // 从第二个节点开始遍历
                  EntryNode<K, V> preEntryNode = firstNode;
                  EntryNode<K, V> curEntryNode = firstNode.next;
                  while (null != curEntryNode) {
                      if(curEntryNode.key.equals(k)){
                          break;
                      }
                      // 若是下一个节点为空,则说明链表遍历完成,但是也没有匹配到对应的节点
                      if(null == curEntryNode.next){
                          return null;
                      }
                      preEntryNode = curEntryNode;
                      curEntryNode = curEntryNode.next;
                  }
      
                  // 将其之前的节点连接到他的下一个节点上
                  if(null != curEntryNode){
                      System.out.println("移除了元素:" + k);
                      this.size --;
                      preEntryNode.next = curEntryNode.next;
                      return curEntryNode.value;
                  }
                  return null;
              }
          }
      
          @Override
          public V get(K k) {
              int index = getIndex(k);
              EntryNode<K, V> node = this.nodes[index];
              while (null != node){
                  if(node.key.equals(k)){
                      return node.value;
                  }
                  node = node.next;
              }
              return null;
          }
      
          @Override
          public boolean containsKey(K k) {
              int index = getIndex(k);
              EntryNode<K, V> node = this.nodes[index];
              while (null != node){
                  if(node.key.equals(k)){
                      return true;
                  }
                  node = node.next;
              }
              return false;
          }
      
          @Override
          public boolean containsValue(V v) {
              for (EntryNode<K, V> node : this.nodes) {
                  while (null != node){
                      if(node.value.equals(v)){
                          return true;
                      }
                      node = node.next;
                  }
              }
              return false;
          }
      
          @Override
          public int size() {
              return this.size;
          }
      
          @Override
          public boolean isEmpty() {
              for (EntryNode<K, V> node : this.nodes) {
                  if (null != node) {
                      return false;
                  }
              }
              return true;
          }
      
          @Override
          public void clear() {
              this.nodes = new EntryNode[this.nodes.length];
          }
      
          private void resize() {
              EntryNode<K, V>[] resizeArray = this.nodes;
              this.nodes = new EntryNode[this.nodes.length * REHASH_BASE];
              this.size = 0;
              for (EntryNode<K, V> node : resizeArray) {
                  while (null != node){
                      this.put(node.key, node.value);
                      node = node.next;
                  }
              }
              System.out.println("hash表已经扩容,目前容量:" + this.nodes.length);
          }
      }
      
      

      数据结构之哈希表-JAVA代码实现原文地址,点击访问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值