Hashtable的实现原理
2014-03-25 09:29
295 查看
【IT168 技术文档】在仔细分析源代码之前,我们来看看Hashtable提供的一些接口方法。
public int size();
public boolean isEmpty() ;
public synchronized Enumeration keys();
public synchronized Enumeration elements();
public synchronized boolean contains(Object
value) ;
public synchronized boolean containsKey(Object
key);
public synchronized Object get(Object key);
public synchronized Object put(Object key, Object value) ;
public synchronized Object remove(Object key);
public synchronized void clear()
;
public synchronized void clear()
;
上面的方法我就不一一介绍了,具体的用法也是很简单,相对大家对此也比较熟悉了。
Hashtable的用法
Hashtable 有2个构造函数
public Hashtable(int initialCapacity); //指定容量大小
public Hashtable() {
this(11); //默认的容量是11,为什么是11,而不是10呢?
}
Demo1
Hashtable sTable = new Hashtable();
sTable.put("wuhua","wuhua");
sTable.remove("wuhua");
sTable.clear();
上面是简单的用法。
Hashtable源代码解读
在了解源代码之前,我们先来了解下一些java不常用的关键字。
transient
当串行化某个对象时,如果该对象的某个变量是transient,那么这个变量不会被串行化进去。也就是说,假设某个类的成员变量是transient,那么当通过ObjectOutputStream把这个类的某个实例保存到磁盘上时,实际上transient变量的值是不会保存的。因为当从磁盘中读出这个对象的时候,对象的该变量会没有被赋值。 另外这篇文章还提到,当从磁盘中读出某个类的实例时,实际上并不会执行这个类的构造函数,而是读取这个类的实例的状态,并且把这个状态付给这个类的对象。
引用地址:http://blog.chinaunix.net/u/22516/showart.php?id=380029
Transient 这个关键字很重要,来看下源代码里面有几处是用到这个关键字的。
private transient HashtableEntry table[];
private transient int count;
源代码中只有上面两个字段的定义是用到的,但是这两个字段是用于存储Hashtable的容器字段,因此可以说Hashtable是不允许序列化的。
内部类
Hashtable有2个内部类
HashtableEntry -- 用于存放key-value,nextElement的类。
class HashtableEntry {
int hash;
Object key;
Object value;
HashtableEntry next;
}
HashtableEnumerator 遍历的枚举类。
class HashtableEnumerator implements Enumeration {
boolean keys;
int index;
HashtableEntry table[];
HashtableEntry entry;
HashtableEnumerator(HashtableEntry table[], boolean keys) {
this.table = table;
this.keys = keys;
this.index = table.length;
}
public boolean hasMoreElements() {
if (entry != null)
{
return true;
}
while (index-- > 0)
{
if ((entry = table[index]) != null)
{
return true;
}
}
return false;
}
public Object nextElement() {
if (entry == null)
{
while ((index-- > 0) && ((entry = table[index]) == null));
}
if (entry != null)
{
HashtableEntry e = entry;
entry = e.next;
return keys ? e.key : e.value;
}
throw new NoSuchElementException(
/* #ifdef VERBOSE_EXCEPTIONS */
/// skipped "HashtableEnumerator"
/* #endif */
);
}
}
代码写的是相当的简介。有一些比较技巧性的用法也是相当的不错,比如:
if (entry == null) {
while ((index-- > 0) && ((entry = table[index]) == null));
} 这段写的是相当的好,可见作者的功力,循环变量table
while (index-- > 0)
//循环变量,查看是否有下一个元素,
if ((entry = table[index]) != null)
{
return true;
}
}
了解了Hashtable的数据存放格式,我们看看存放的关键逻辑
在put,remove,get方法中存在。
int index = (hash & 0x7FFFFFFF) % tab.length;
这样的函数,这个函数的意义上,根据散列值来获取对象的存储位置。
来欣赏下代码片段:
public synchronized Object get(Object key) {
HashtableEntry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (HashtableEntry e = tab[index] ; e != null ;
e = e.next) {
if ((e.hash == hash) && e.key.equals(key))
{
return e.value;
}
}
return null;
}
从上面的代码可以分析出。首先获取key的散列值,并且根据散列值进行key的Index定位
这里存在同一个index多个HashtableEntry 存在,所以才会有了next的变量,next就是存放相同位置不同key的实体。
下面再来看看Hashtable里面一个扩充容器的算法。
protected void rehash() {
int oldCapacity = table.length;
HashtableEntry oldTable[] = table;
int newCapacity = oldCapacity * 2 + 1;
HashtableEntry newTable[] = new HashtableEntry[newCapacity];
threshold = (int)((newCapacity * loadFactorPercent) / 100);
table = newTable;
for (int i = oldCapacity
; i-- > 0 ;) {
//循环遍历oldTable的对应的实体,并且遍历对应的实体的没一个对象,进行
//重新分配index,再进行保存
for (HashtableEntry old = oldTable[i] ; old != null ;
) {
HashtableEntry e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newTable[index]; //e 的next 指向当前索引
newTable[index] = e; //
}
}
}
for (HashtableEntry old = oldTable[i] ; old != null ;
)
这段的用法很奇怪,我以前没有使用过,上面的代码等同于
HashtableEntry old = oldTable[i];
Whild(old != null){
...............................
...............................
}
不过个人比较习惯使用第2种方式
相关文章推荐
- 智能指针 shared_ptr 的使用方法
- SQL连表查询
- locale的设定及其LANG、LC_ALL、LANGUAGE环境变量的区别
- Java Tomcat 中调用.net DLL的方法
- sql2005 用户 可能联接到数据库但 登录失败, 的因
- SMTP命令与ESMTP命令简介(附带命令通信)
- 告别充电、直接更换电池,只是电动车的一场梦?
- 在 SELECT 查询中使用分组集
- 在linux下php挂接mysql.so扩展的方法
- 这个项目要多久开发完成?
- HDU 2577 How to Type(dp)
- 正確讀書方法
- Linux内核中双向链表的经典实现
- base64编解码
- const char *' to 'LPCWSTR'
- 如何恢复Eclipse或者Myeclipse里面删除的文件?
- Win7+Ubuntu11.10(EasyBCD硬盘安装)
- error: No curses/termcap library found的解决办法 .
- 一步一步学SpringDataJpa——初识SpringDataJpa
- C++编程练习(1)----“实现简单的线性表的顺序存储结构“