热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java集合框架——HashMap与Hashtable

Java集合框架——HashMap与Hashtable先来看看两个类的重点方法,再来比较。(这是我看源代码的思路,你也先去看看吧)集合最主要的操作:写入,读取,移除。Java代码***

Java集合框架——HashMap与Hashtable 



先来看看两个类的重点方法,再来比较。(这是我看源代码的思路,你也先去看看吧) 

集合最主要的操作:写入,读取,移除。 

Java代码  收藏代码
  1. /** 
  2.  * Associates the specified value with the specified key in this map. 
  3.  * If the map previously contained a mapping for the key, the old 
  4.  * value is replaced. 
  5.  *  接合指定的value与指定的key在这个map中,假如以前在这个map中包含这个key,以前   
  6.  *  对应的value将会被替换 
  7.  * @param key key with which the specified value is to be associated 
  8.  * @param value value to be associated with the specified key 
  9.  * @return the previous value associated with key, or 
  10.  *         null if there was no mapping for key. 
  11.  *         (A null return can also indicate that the map 
  12.  *         previously associated null with key.) 
  13.  */  
  14. public V put(K key, V value) {  
  15.     if (key == null)  
  16.         return putForNullKey(value);  
  17.     int hash = hash(key.hashCode());  
  18.     int i = indexFor(hash, table.length);  
  19.     for (Entry e = table[i]; e != null; e = e.next) {  
  20.         Object k;  
  21.         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  22.             V oldValue = e.value;  
  23.             e.value = value;  
  24.             e.recordAccess(this);  
  25.             return oldValue;  
  26.         }  
  27.     }  
  28.   
  29.     modCount++;  
  30.     addEntry(hash, key, value, i);  
  31.     return null;  
  32. }  

读源代码时,记得把上面的注释读一下,这是最快的理解方法的方式,不要一看源代码,就想着那几个for。

 

里面有几个关键点:

1:int hash = hash(key.hashCode());
2:int i = indexFor(hash, table.length);
3:addEntry(hash, key, value, i);

先说第1点:

Java代码  收藏代码
  1. /** 
  2.      * Applies a supplemental hash function to a given hashCode, which 
  3.      * defends against poor quality hash functions.  This is critical 
  4.      * because HashMap uses power-of-two length hash tables, that 
  5.      * otherwise encounter collisions for hashCodes that do not differ 
  6.      * in lower bits. Note: Null keys always map to hash 0, thus index 0. 
  7.      */  
  8.     static int hash(int h) {  
  9.         // This function ensures that hashCodes that differ only by  
  10.         // constant multiples at each bit position have a bounded  
  11.         // number of collisions (approximately 8 at default load factor).  
  12.         h ^= (h >>> 20) ^ (h >>> 12);  
  13.         return h ^ (h >>> 7) ^ (h >>> 4);  
  14.     }  

 

这个是HashMap的哈唏值的算法:(正好之前看过一篇博文,给出分析图,论坛地址是:http://www.iteye.com/topic/709945


画的很好,所以就引用一下啊,至于分析的话,还是看原地址中的吧。(我说过,让你与我一起来这个过程,所以自己去看)。

 

第2点:

    /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }

 

这么少?那分析什么啊。这里真看不出来有什么好分析的,可是我运气比较好,看过一篇博文。这个地址忘记了,我就把内容写一下吧。

 

这里的关键点在于length的长度。

它是Entry[] table的长度,这个与HashMap的初始化时指定的。

先看一下HashMap的三个构造函数:

HashMap(int initialCapacity, float loadFactor)

HashMap(int initialCapacity)

HashMap()

 

从最后一个说起:

public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }

 

其中:DEFAULT_LOAD_FACTOR=0.75;DEFAULT_INITIAL_CAPACITY=16;

这是默认值,平时用的是不是这个呢?(今天看了之后,以后可以不用这个了,因为你要知道它的用意。)

第二个就不说了,直接第一个构造函数吧。(记得看一下注释)

Java代码  收藏代码
  1. /** 
  2.      * Constructs an empty HashMap with the specified initial 
  3.      * capacity and load factor. 
  4.      * 
  5.      * @param  initialCapacity the initial capacity 
  6.      * @param  loadFactor      the load factor 
  7.      * @throws IllegalArgumentException if the initial capacity is negative 
  8.      *         or the load factor is nonpositive 
  9.      */  
  10.     public HashMap(int initialCapacity, float loadFactor) {  
  11.         if (initialCapacity < 0)  
  12.             throw new IllegalArgumentException("Illegal initial capacity: " +  
  13.                                                initialCapacity);  
  14.   
  15.         //如果你给出的容量大小大于HashMap支持的最大值时,取最大值  
  16.         if (initialCapacity > MAXIMUM_CAPACITY)  
  17.             initialCapacity = MAXIMUM_CAPACITY;  
  18.         if (loadFactor <= 0 || Float.isNaN(loadFactor))  
  19.             throw new IllegalArgumentException("Illegal load factor: " +  
  20.                                                loadFactor);  
  21.   
  22.         //下面的是关键:获取大于或等于的initialCapacity值的最小的2的n次方。  
  23.         // Find a power of 2 >= initialCapacity  
  24.         int capacity = 1;  
  25.         while (capacity < initialCapacity)  
  26.             capacity <<= 1;  
  27.   
  28.         this.loadFactor = loadFactor;  
  29.         threshold = (int)(capacity * loadFactor);  
  30.         table = new Entry[capacity];  
  31.         init();  
  32.     }  

 

看到了吧,这里有个2的n次方。回到先前说的indexFor方法

h&(length-1),这下知道道理了吧,保证在数组之内循环。(只能设计者太邪恶了,因为你只有把代码全部看懂才知道这个用处,好在分享者众多。)

 

再回到上面说的第3重点:addEntry(hash, key, value, i);

Java代码  收藏代码
  1.    /** 
  2.     * Adds a new entry with the specified key, value and hash code to 
  3.     * the specified bucket.  It is the responsibility of this 
  4.     * method to resize the table if appropriate. 
  5.     * 
  6.     * Subclass overrides this to alter the behavior of put method. 
  7.     */  
  8.    void addEntry(int hash, K key, V value, int bucketIndex) {  
  9. Entry e = table[bucketIndex];  
  10.        table[bucketIndex] = new Entry(hash, key, value, e);  
  11.        if (size++ >= threshold)  
  12.            resize(2 * table.length);  
  13.    }  

 这里真没什么好说的,为了分析与Hashtable的不同,看看这个resize(2 * table.length);再到这个方法中的transfer(newTable);(其实这个在Hashtable与这没什么不同,引大家来是想学一下人家的设计思想,都是两个循环,因为HashMap可以有key为null的值,所以设计的时候,就得考虑进去。至于效率方面,两个没什么差别,但是可以看出在扩大容量时,是一个很耗时的工作。认清这点,虽然比较Hashtable没什么用,但是你在理解HashMap有用,与别的集合比较也有用,难道不是吗?)

Java代码  收藏代码
  1. /** 
  2.      * Transfers all entries from current table to newTable. 
  3.      */  
  4.     void transfer(Entry[] newTable) {  
  5.         Entry[] src = table;  
  6.         int newCapacity = newTable.length;  
  7.         for (int j = 0; j < src.length; j++) {  
  8.             Entry e = src[j];  
  9.             if (e != null) {  
  10.                 src[j] = null;  
  11.                 do {  
  12.                     Entry next = e.next;  
  13.                     int i = indexFor(e.hash, newCapacity);  
  14.                     e.next = newTable[i];  
  15.                     newTable[i] = e;  
  16.                     e = next;  
  17.                 } while (e != null);  
  18.             }  
  19.         }  
  20.     }  

 
到此put方法就结束了。(看客还有什么好的要说的,就回复吧,因为我分享的同时,也希望能再深入点。)

 

 

 

第二大重点,读取

Java代码  收藏代码
  1. /** 
  2.  * Returns the value to which the specified key is mapped, 
  3.  * or {@code null} if this map contains no mapping for the key. 
  4.  * 
  5.  * 

    More formally, if this map contains a mapping from a key 

  6.  * {@code k} to a value {@code v} such that {@code (key==null ? k==null : 
  7.  * key.equals(k))}, then this method returns {@code v}; otherwise 
  8.  * it returns {@code null}.  (There can be at most one such mapping.) 
  9.  * 
  10.  * 

    A return value of {@code null} does not necessarily 

  11.  * indicate that the map contains no mapping for the key; it's also 
  12.  * possible that the map explicitly maps the key to {@code null}. 
  13.  * The {@link #containsKey containsKey} operation may be used to 
  14.  * distinguish these two cases. 
  15.  * 
  16.  * @see #put(Object, Object) 
  17.  */  
  18. public V get(Object key) {  
  19.     if (key == null)  
  20.         return getForNullKey();  
  21.     int hash = hash(key.hashCode());  
  22.     for (Entry e = table[indexFor(hash, table.length)];  
  23.          e != null;  
  24.          e = e.next) {  
  25.         Object k;  
  26.         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
  27.             return e.value;  
  28.     }  
  29.     return null;  
  30. }  

 

代码本身没什么好说的,这里来看看构造函数对它的影响。

如果每个key的hash值都是不一样的,那就很快;反之,如果多个key的hash值一样,而table的深度就更深,获取的速度就有可能遍历到最后。

      如果开始就知道 HashMap 会保存多个 key-value 对,可以在创建时就使用较大的初始化容量,如果 HashMap 中 Entry 的数量一直不会超过极限容量(capacity * load factor),HashMap 就无需调用 resize() 方法重新分配 table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为 capacity 的 Entry 数组),因此创建 HashMap 时初始化容量设置也需要小心对待。 

 

再看移除:remove(Object key),与Hashtable没有什么区别的。

都是要遍历一次。并找到对应的key-value,将其移除。所以要它的时间复杂度o(n)。(等之后看了别的集合再说说。)

 

说完了。到Hashtable了。

构造函数差不多,不说。

不同点:

1,默认值不一样。0.75是一样的,initialCapacity为11。而且没有最大值,最小值是1。与2的30次方=1073741824

为什么没有最大值呢???(等写完这博文再去看看,这是突然想到的。)

 

2,hash的算法:直接用的int hash = key.hashCode();

    而int index = (hash & 0x7FFFFFFF) % tab.length;与HashMap区别不大,因为HashMap支持key为null,而且固定的把第0位给了它,所以通过h&(length-1)把0给去掉。而这里的这个就是直接循环,对于null的支持,总是要点代价的。

 

3,就是刚说的对null的支持不同,Hashtable是不支持的。如果从源代码中,可以看到

if (value == null) {
     throw new NullPointerException();
 }

不支持value为null,而通过

int hash = key.hashCode();可以知道不支持key不能为null。

而HashMap支持有一个key为null,而value为null的不限制。

 

4,就是看看Hashtable在操作方法前都是有个关键字synchronized,不懂的去看我的博客,地址:http://ciding.iteye.com/blog/1300110 (Java多线程及线程池专题

 

5,这个不是重点,也顺带说一下,Hashtable有一个contains(Object value)方法与containsValue(Object value)方法一样。而HashMap只有containsValue(Object value)方法。说这一点的原因是,在有的博文中乱写

写道 HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。 

 只是将两个方法合并而已,引起误解也是因为没有看源代码。

 

先这到了。。。


推荐阅读
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • Learning to Paint with Model-based Deep Reinforcement Learning
    本文介绍了一种基于模型的深度强化学习方法,通过结合神经渲染器,教机器像人类画家一样进行绘画。该方法能够生成笔画的坐标点、半径、透明度、颜色值等,以生成类似于给定目标图像的绘画。文章还讨论了该方法面临的挑战,包括绘制纹理丰富的图像等。通过对比实验的结果,作者证明了基于模型的深度强化学习方法相对于基于模型的DDPG和模型无关的DDPG方法的优势。该研究对于深度强化学习在绘画领域的应用具有重要意义。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
author-avatar
倪好蛋蛋小猪
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有