zoukankan      html  css  js  c++  java
  • 由浅入深解析HashMap系列一---HashMap简单实现 增、删、查。

        如果让我们自己来设计HashMap,应该怎么做呢?数组。用数组存储节点,每个节点有key和value信息。那首先我们得要有节点存储key,value。

    节点设计

    在这个Node类中,有三个属性,hash值、key、value值。

     1 class Node<K,V> {
     2         final int hash; //hash值
     3         final K key;//key
     4         V value;//value
     5 //构造函数
     6         Node(int hash, K key, V value, Node<K,V> next) {
     7             this.hash = hash;
     8             this.key = key;
     9             this.value = value;
    10             this.next = next;
    11         }
    12 
    13         public final K getKey()        { return key; }
    14         public final V getValue()      { return value; }
    15         public final String toString() { return key + "=" + value; }
    16 
    17         public final boolean equals(Object o) {
    18            if (o == this)
    19              return true;          
    20                if (Objects.equals(key, o.getKey()) &&
    21                    Objects.equals(value, o.getValue()))
    22                    return true;
    23 
    24             return false;
    25         }
    26 }
    View Code

    注意:这里的构造函数中多了个next,大家忽略即可。奇怪,添加了这个代码块,怎么编辑的时候删除不了。哎。。。

    初始化数组

    定义一个容量为100的数组,用来存储node节点。 DEFAULT_INITIAL_CAPACITY ,它必须是必须是2的幂,在JDK提供的HashMap它的值为1 << 4,也就是16,这里直接填16不可以吗?为啥一定要这样写?

    1  static final int DEFAULT_INITIAL_CAPACITY=100;
    2  transient Node<K,V>[] table;
    3     /**
    4      * 构造函数
    5      */
    6     public SimpleHashMap()
    7   {
    8       table=(Node<K,V>[])new Node[DEFAULT_INITIAL_CAPACITY];
    9   }
    View Code

    增加操作-put

    对一个容量为100的数组中添加元素其实很简单,具体步骤如下:

    1)对key值进行hash,获取hash值。

    2)通过上面获取的hash值、key、value值,创建node节点。

    3)通过hash值和数组长度获取对应的下标。

    4)对获取的下标赋值。

    注意:这里对key进行hash调用的hash函数,具体我也不明白为啥要这样设计?

    1 static final int hash(Object key) {
    2       int h;
    3       return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    4   }
    View Code

    另外,根据hash值和容量(数组长度)获取对应的下标,具体我也不明白为啥要这样设计?

    1 i = (n - 1) & hash
    View Code

     具体的put代码如下:

     1  public V put(K key, V value)
     2     {
     3         //创建新节点
     4         Node<K,V> p; int n, i;
     5         //获取长度
     6         n=table.length;
     7         //对key值进行hash
     8         int hash=hash(key);
     9         //目前没有考虑hash冲突
    10         table[i = (n - 1) & hash]=newNode(hash,key,value);
    11         //返回原来的值
    12         return value;
    13         
    14     }
    15 //创建新节点
    16  Node<K,V> newNode(int hash, K key, V value) {
    17       return new Node<>(hash, key, value);
    18   }
    View Code

    删除操作-remove

    删除操作其实和添加差不多,首先要获取下标,然后再将下标的值赋值为null。

     1   public V remove(Object key) {
     2         Node<K,V> e;
     3         return (e = removeNode(hash(key), key)) == null ?
     4             null : e.value;        
     5     }
     6     /**
     7      * 删除节点。
     8      * 目前不考虑冲突,当对应的下标有值且不为null时,返回该值,并将对应的值设置为Null
     9      * 这里考虑不太完善,因为hashmap是允许value为null的,这样并没有真正删除这个元素。
    10      * @param hash
    11      * @param key
    12      * @return
    13      */
    14     final Node<K,V> removeNode(int hash, Object key) {
    15         Node<K,V> p;int index;
    16         p=table[index = (table.length - 1) & hash];
    17         if(p!=null)
    18         {
    19             table[index]=null;
    20         }
    21         return p;
    22     }
    View Code

    查询操作-get

    和remove、put操作一样,也是需要根据key值获取下标,然后再返回对应的value值。

     1  /**
     2      * 根据key值,获取value值
     3      * @param key
     4      * @return
     5      */
     6     public V get(Object key) {
     7         Node<K,V> e;
     8         return (e = getNode(hash(key), key)) == null ? null : e.value;
     9         
    10     }
    11     /**
    12      * 获取对应的值
    13      * @param hash
    14      * @param key
    15      * @return
    16      */
    17     final Node<K,V> getNode(int hash, Object key) {
    18        return  table[(table.length - 1) & hash];
    19     }
    View Code

    是否存在该元素--containskey

    根据key值判断是否存在该元素,其实和get操作一样,只是这里返回的是布尔值。

    1  /**
    2      * 是否存在对于的key值
    3      * @param key
    4      * @return
    5      */
    6     public boolean containsKey(Object key) {
    7         return getNode(hash(key), key) != null;
    8         
    9     }
    View Code

    哇!是不是很简单呢?这里有两个疑问,就是不知道那个下标的获取和hash值得获取为啥要那样设计?是因为这样是最优的吗?下标的获取这种方法,发生冲突的几率就少很多吗?在整个系列中,我并没有讨论HashMap继承AbstractMap,实现Map接口。

  • 相关阅读:
    NYOJ The Triangle
    max()和数组里面的max
    sizeof和strlen的区别和联系总结
    继BAT之后 第四大巨头是谁
    专注做好一件事
    编程技术面试的五大要点
    IBM面试记
    创业者,你为什么这么着急?
    硅谷创业教父Paul Graham:如何获得创业idea
    17家中国初创公司的失败史
  • 原文地址:https://www.cnblogs.com/icbcfang/p/5414795.html
Copyright © 2011-2022 走看看