`
wangyanlong0107
  • 浏览: 499834 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

【转】Java的Hashtable实现

 
阅读更多

最近做信息检索的VSM实验,字典生成这块用的是java自带的Hashtable数据结构,觉得效率还不错。后来有同学提到用词典树来保存字符串,可以用公共前缀来节约存储空间,最大限度的减少无谓的比较,查询效率要高于哈希表。(补充@2011.5.5 在数据较少的情况下,hash的查询效率应该是最高的,基本接近O(1),字典树的优势应该是在空间效率上)回头有时间研究下词典树的实现和分析,这里先分析一下Java的hashtable实现。

 


 

为了使用Eclipse去查看java本身的一些基础实现,我们需要先将java的源码加到Eclipse的jre路径中:

1.点 “window”-> "Preferences" -> "Java" -> "Installed JRES"

 

2.此时"Installed JRES"右边是列表窗格,列出了系统中的 JRE 环境,选择你的JRE,然后点边上的 "Edit..."

3.选中rt.jar文件,点右边的按钮“Source Attachment...”, 选择你的JDK目录下的“src.zip”文件即可

 


 

这样,在Eclipse中随便写一个Hashtable对象,然后ctrl单击就可以看到java的Hashtable类的实现了。下面这张是其总体的结构:

 

Java的Hashtable结构

总得来说就是每个哈希表都保存了一个Entry数组,然后每个Entry其实是存放碰撞的一个链,其中Entry类部分代码实现是:

 

  1. /** 
  2.      * Hashtable collision list. 
  3.      */  
  4.     private static class Entry<K,V> implements Map.Entry<K,V> {  
  5.     int hash;  
  6.     K key;  
  7.     V value;  
  8.     Entry<K,V> next;  
  9.     protected Entry(int hash, K key, V value, Entry<K,V> next) {  
  10.         this.hash = hash;  
  11.         this.key = key;  
  12.         this.value = value;  
  13.         this.next = next;  
  14.     }  

 

 

除了hash值和键值对,就是指向下一个Entry的“指针”了。哈希表还有两个主要的属性,一个是initialCapacity表示初始的大小,如果使用默认的构造函数,系统就设为11,注意这里容量不是可以存放字符串的个数,而是哈希的范围,设为11的话,所有的hash值都会映射到这11个位置上。另一个是loadFactor,表示存放元素的个数栈总的hash范围的比例,默认的是设为0.75,这是在空间和时间之间的一个权衡,如果过大,则会有很多的碰撞出现,搜索效率不高,而如果过低,则会占用很大的空间。还有一些其他的属性,比如总的元素个数,阈值等等,这里不再详述。

下面看下几个关键的函数实现,首先自然是put函数:

 

  1. public synchronized V put(K key, V value) {  
  2.     // Make sure the value is not null  
  3.     if (value == null) {  
  4.         throw new NullPointerException();  
  5.     }  
  6.     // Makes sure the key is not already in the hashtable.  
  7.     Entry tab[] = table;  
  8.     int hash = key.hashCode();  
  9.     int index = (hash & 0x7FFFFFFF) % tab.length;  
  10.     for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {  
  11.         if ((e.hash == hash) && e.key.equals(key)) {  
  12.         V old = e.value;  
  13.         e.value = value;  
  14.         return old;  
  15.         }  
  16.     }  
  17.     modCount++;  
  18.     if (count >= threshold) {  
  19.         // Rehash the table if the threshold is exceeded  
  20.         rehash();  
  21.             tab = table;  
  22.             index = (hash & 0x7FFFFFFF) % tab.length;  
  23.     }  
  24.     // Creates the new entry.  
  25.     Entry<K,V> e = tab[index];  
  26.     tab[index] = new Entry<K,V>(hash, key, value, e);  
  27.     count++;  
  28.     return null;  
  29.     }  

 

这里我们可以看到,对key的hash做了一个与操作,保证其是一个正整数,然后对数组的长度求余,得到索引,然后遍历这个索引位置的链表中的每一个元素,如果存在一个元素的key和插入的key相同,就修改其值。否则,就新建一个Entry放在index位置链表的最前面,其中用到了rehash函数,可以在当哈希表中的总个数超过当前容量乘以loadFactor(就是threshold)的时候,进行扩建和重排序:

 

  1. protected void rehash() {  
  2.     int oldCapacity = table.length;  
  3.     Entry[] oldMap = table;  
  4.     int newCapacity = oldCapacity * 2 + 1;  
  5.     Entry[] newMap = new Entry[newCapacity];  
  6.     modCount++;  
  7.     threshold = (int)(newCapacity * loadFactor);  
  8.     table = newMap;  
  9.     for (int i = oldCapacity ; i-- > 0 ;) {  
  10.         for (Entry<K,V> old = oldMap[i] ; old != null ; ) {  
  11.         Entry<K,V> e = old;  
  12.         old = old.next;  
  13.         int index = (e.hash & 0x7FFFFFFF) % newCapacity;  
  14.         e.next = newMap[index];  
  15.         newMap[index] = e;  
  16.         }  
  17.     }  
  18.     }  

 

 

容量扩大2倍加1,采用这个策略应该是有一定考虑的,我没有细究。在拷贝完之后,进行了一个重新的hash,因为容量已经变了,所以这个步骤是必须的。还有一些其他的函数,类似这里就不介绍了,最后我们来看下java的字符串hash是采用的什么算法:

 

  1. public int hashCode() {  
  2.     int h = hash;  
  3.     if (h == 0) {  
  4.         int off = offset;  
  5.         char val[] = value;  
  6.         int len = count;  
  7.             for (int i = 0; i < len; i++) {  
  8.                 h = 31*h + val[off++];  
  9.             }  
  10.             hash = h;  
  11.         }  
  12.         return h;  
  13.     }  

 

 

这个函数在String中,看上面非常简洁,就是对字符串中的每一个字符的ASCII码值进行的一个加和乘运算,乘数是31。这个算法是BKDR哈希算法,来自于Brian Kernighan 和 Dennis Ritchie的The C Programming Language一书,可以说是常用的hash算法中较为简洁的一个了,但是效率确实最好的之一,其中乘数的形式是31 131 1313 13131 131313...。关于常见的字符串hash算法,我会在以后的博客给予介绍,并用这次VSM的实验进行一个简单的测试。

分享到:
评论

相关推荐

    HashTable的java实现

    在Java中,除了`HashTable`,还有其他实现,如`HashMap`,它在单线程环境下提供了更好的性能,而`ConcurrentHashMap`则在多线程环境下提供高性能且线程安全的解决方案。学习和掌握这些不同的实现方式可以帮助开发者...

    java hashtable实现代码

    Java中的`Hashtable`类是Java集合框架的一部分,它是一个基于哈希表的Map接口实现。这个数据结构允许我们以键值对的形式存储数据,并且提供了快速的插入、删除和查找操作。`Hashtable`是线程安全的,因此可以在多...

    Json字符串转换Hashtable,DataTable,DataSet方法和反转换方法

    这些示例展示了如何在C#中实现JSON与常用数据结构之间的转换。了解并熟练掌握这些技巧,对于开发人员来说至关重要,特别是在处理Web服务响应或存储数据时。在实际项目中,确保已经添加了Newtonsoft.Json库(通常通过...

    用Session、Hashtable实现购物车功能

    总之,使用Session和Hashtable实现购物车功能是一种常见且实用的方法,它充分利用了Web服务器的存储能力,保证了用户在多个页面间跳转时购物车数据的一致性。通过不断实践和优化,我们可以构建出高效、稳定的购物车...

    浅谈Java web中基于Hashtable的数据库操作.zip

    首先,理解Hashtable是Java中的一个同步容器类,它继承自Dictionary类,实现了Map接口。Hashtable存储键值对,不允许存储null键和null值,且具有线程安全的特性。在Web应用中,开发者可以利用Hashtable存储和管理...

    java的hashtable的用法.docx

    在Java编程语言中,`Hashtable`是`Dictionary`类的一个具体实现,它提供了一个基于键值对的数据结构,允许程序员存储和检索对象。`Hashtable`类是线程安全的,因此可以在多线程环境中直接使用,而无需额外的同步控制...

    java软件技术文档-深入java8的集合5:Hashtable的实现原理.pdf

    【Java软件技术文档-深入Java8的集合5:Hashtable的实现原理】 在Java编程语言中,集合框架是不可或缺的一部分,而Hashtable是其中一种古老的、线程安全的哈希表实现。尽管现在更多地使用HashMap或...

    java的hashtable的用法.pdf

    在Java编程语言中,`Hashtable`是`Dictionary`类的一个具体实现,它是早期Java版本中的一个线程安全的键值对存储容器。`Hashtable`遵循了一些基本的映射概念,如存储键值对、根据键查找值以及添加、删除键值对等。...

    易语言哈希表例程 据java的HashTable编写

    通过阅读和分析这份代码,我们可以深入了解易语言如何借鉴Java的HashTable实现哈希表,以及易语言特有的编程风格和技巧。这有助于提升我们对易语言的理解,并且能够运用到自己的项目中,优化数据存储和检索的效率。

    在J2ME环境下把JSON解析为Hashtable

    import java.util.Hashtable; import java.util.Vector; import org.json.me.JSONArray; import org.json.me.JSONObject; public class JSONParser { public static Hashtable parse(String jsonObjStr) throws ...

    在Java中运用Hashtable.doc

    在Java中,`Hashtable`和`HashMap`都是基于哈希表实现的,但它们之间存在一些差异: 1. 线程安全性:`Hashtable`是线程安全的,这意味着在多线程环境下,它内部的同步机制保证了并发访问的正确性,而`HashMap`则...

    Java中HashTable和HashMap的区别_动力节点Java学院整理

    Java中的`HashTable`和`HashMap`都是实现`Map`接口的数据结构,用于存储键值对。两者虽然在功能上相似,但在实现细节和使用场景上有显著的区别。 首先,线程安全性是两者之间的一个关键差异。`HashTable`是线程安全...

    Java 实例 - 遍历 HashTable 的键值源代码+详细教程.zip

    `HashTable`是Java中的一个同步容器类,继承自`Dictionary`接口,实现了`Map`接口。它不允许`null`键和`null`值,并且提供了线程安全的访问,即在多线程环境中,对`HashTable`的操作不会引发数据不一致的问题。 ...

    Java中Hashtable类与HashMap类的区别详解

    Java中的`Hashtable`和`HashMap`都是用于存储键值对的数据结构,它们都实现了`Map`接口,但在一些关键特性上有所不同。以下是这两者的主要区别: 1. **线程安全性**: - `Hashtable`是线程安全的,这意味着在多...

    java集合-Hashtable的使用

    Hashtable是Java中的一种散列表实现,它可以存储键值对,并根据键的哈希值来快速查找和访问值。

    HashMap和HashTable底层原理以及常见面试题

    HashMap和HashTable是Java中两个常用的数据结构,都是基于哈希表实现的,但它们之间存在着一些关键的区别。本文将深入探讨HashMap和HashTable的底层原理,并总结常见的面试题。 HashMap的底层原理 HashMap是Java中...

Global site tag (gtag.js) - Google Analytics