Hashtable 的实现原理

【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种方式


你可能感兴趣的:(Hashtable 的实现原理)