zoukankan      html  css  js  c++  java
  • Java Se :Map 系列

     之前对Java Se中的线性表作了简单的说明。这一篇就来看看Map。

    Map系列的类,并不是说所有的类都继承了Map接口,而是说他们的元素都是以<Key, Value>形式设计的。

    Dictionary

    这个类现在不推荐使用了,但也有必要说一下,在它的描述中,有这么一句:Any non-null object can be used as a key and as a value。

    现在都改用Map接口了。

    Map

    这个接口用于替换Dictionary的。这种集合提供了三种视图方式:

    1)a set of keys

    2)collection of Values

    3)set of Entry<key, value>

    Hashtable

    这个类继承了Dictionary实现了Map。下面就重点看看这个类如何实现。

     

    从这里看Hashtable是一个数组,数组的元素是Entry。

    在看看Entry的设计:

     

    这个Entry是一个单链表。

    所以Hashtable的数据结构就可以认为是这样的:

     

    为什么会这样设计?

    以为硬件的原因,支持数组和链表两种形式,所以其他的数据结构(集合)都是基于这两类基础结构实现的。

    Hashtable的设计,关键一点是hash,它计算的是key的hash,通过key的hashcode来计算这个元素所在的单链表在数组中的索引,然后根据索引取到单链表,然后进行其他的操作。

    接下来看看它的几个重要方法如何实现:

    put

    public synchronized V put(K key, V value) {
    
       // Make sure the value is not null
    
       if (value == null) {
    
           throw new NullPointerException();
    
       }
    
     
    
       // 如果key已经在这个hashtable中存在,就覆盖原有的value,并返回原有的value。
    
       // 从这一小段代码中,我们就可以看出hashtable的结构确实如上面所想的那样。
    
    Entry tab[] = table;
    
       int hash = key.hashCode();
    
       int index = (hash & 0x7FFFFFFF) % tab.length;
    
       for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
    
           if ((e.hash == hash) && e.key.equals(key)) {
    
          V old = e.value;
    
          e.value = value;
    
          return old;
    
           }
    
       }
    
     
    
       modCount++;
    
       if (count >= threshold) {
    
           // Rehash the table if the threshold is exceeded
    
           rehash();
    
     
    
                tab = table;
    
                index = (hash & 0x7FFFFFFF) % tab.length;
    
       }
    
     
    
       // 如果不存在,就把key value 封装成一个Entry,然后插入到相应位置
    
    // 在之前的代码中已经计算出它的索引了,接下来的任务就是把entry插入到数组索引位置处的单链表里。插入的时候是从头部插入的。
    
      Entry<K,V> e = tab[index];
    
       tab[index] = new Entry<K,V>(hash, key, value, e);
    
       count++;
    
       return null;
    
        }
    
     
    Hashtable#put(key, value)

      

    常用的方法get(key),remove(key)的方法就不分析了,因为没有必要,只要你了解了这个数据结构,自己就可以实现它的几个常用的方法。

    keys

     

     

    拿到的结果其实是一个枚举器(不过这个枚举器比较特殊,实现了Enumeration接口和Iterable接口),并不是一个集合。

    同样的道理,elements也是如此,拿到的结果是一个遍历value的枚举器。

    HashMap

    HashMap的存储结构如下图所示,因为源码部分太多,所以就截了这么一张图,但也足够说明问题了:

     

    从图上很容易看出,HashMap也是使用了数组实现的,数组中的元素是Node,再看看Node的设计:

     

    Node也是一个单链表,看来HashMap与Hashtable在结构上是基本一致的。

    也就是说他的模型也是下面图中所示:

     

    如果说HashMap的结构只是这个样子的话,就没有必要存在了。为什么这么说呢,关键点还是在Node上,看看他的两个子类:

     

    Entry:

    每个节点又添加了一两个引用:before,after,这样一来结构就可以猜想为:

    但其实并不是这个样子的,会根据hashcode进行一些算法处理,形成链式结构,也就是LinkedHashMap了。

    如果有兴趣可以了解一些LinkedHashMap是如何实现的。

    TreeNode:

     

    每个节点都可以是一个树。就让结构变得更为复杂了。

  • 相关阅读:
    台州 OJ 3847 Mowing the Lawn 线性DP 单调队列
    洛谷 OJ P1417 烹调方案 01背包
    快速幂取模
    台州 OJ 2649 More is better 并查集
    UVa 1640
    UVa 11971
    UVa 10900
    UVa 11346
    UVa 10288
    UVa 1639
  • 原文地址:https://www.cnblogs.com/f1194361820/p/3994625.html
Copyright © 2011-2022 走看看