Java - 提高-源码(5) - HashTable

HashTable源码解析

源码对应JDK1.7

JDK1.7源码下载地址:JDK1.7下载地址


JDK 源码注释

This class implements a hash table, which maps keys to values. Any non-null object can be used as a key or as a value.

 

To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.

 

An instance of Hashtable has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. Note that the hash table is open: in the case of a "hash collision", a single bucket stores multiple entries, which must be searched sequentially. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. The initial capacity and load factor parameters are merely hints to the implementation. The exact details as to when and whether the rehash method is invoked are implementation-dependent.

 

Generally, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the time cost to look up an entry (which is reflected in most Hashtable operations, including get and put).

 

The initial capacity controls a tradeoff between wasted space and the need for rehash operations, which are time-consuming. No rehash operations will ever occur if the initial capacity is greater than the maximum number of entries the Hashtable will contain divided by its load factor. However, setting the initial capacity too high can waste space.

 

If many entries are to be made into a Hashtable, creating it with a sufficiently large capacity may allow the entries to be inserted more efficiently than letting it perform automatic rehashing as needed to grow the table.

 

This example creates a hashtable of numbers. It uses the names of the numbers as keys:

 

   Hashtable<String, Integer> numbers

     = new Hashtable<String, Integer>();

   numbers.put("one", 1);

   numbers.put("two", 2);

   numbers.put("three", 3);

To retrieve a number, use the following code:

 

   Integer n = numbers.get("two");

   if (n != null) {

     System.out.println("two = " + n);

   }}

The iterators returned by the iterator method of the collections returned by all of this class's "collection view methods" are fail-fast: if the Hashtable is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future. The Enumerations returned by Hashtable's keys and elements methods are not fail-fast.

 

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

 

As of the Java 2 platform v1.2, this class was retrofitted to implement the Map interface, making it a member of the Java Collections Framework. Unlike the new collection implementations, Hashtable is synchronized. If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use java.util.concurrent.ConcurrentHashMap in place of Hashtable.

 

 

这个类实现了一个哈希表,它将键映射到值。任何非空对象都可以用作键或值

 

要成功地从哈希表存储和检索对象,用作键的对象必须实现hashCode方法和equals方法

 

一个Hashtable的实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中桶的数量,初始容量就是哈希表创建时的容量。请注意,散列表是打开的:对于“散列冲突”,单个存储区存储多个条目,这些条目必须按顺序搜索。加载因子是散列表在其容量自动增加之前被允许获得的满量程的度量。初始容量和负载因数参数仅仅是实施的暗示。有关何时以及是否调用rehash方法的确切细节取决于实现。

 

通常,默认的加载因子(.75)在时间和空间成本之间提供了一个很好的折衷。较高的值会减少空间开销,但会增加查找条目的时间成本(这反映在大多数Hashtable操作中,包括get和put)。

 

初始容量控制了浪费的空间与需要重新运行操作之间的权衡,这是耗时的。如果初始容量大于Hashtable将包含的最大条目数除以其负载因子,则不会发生重新哈希操作。但是,将初始容量设置得太高会浪费空间。

 

如果要将许多条目制作为Hashtable,则使用足够大的容量创建条目可能会使条目插入的效率高于根据需要执行自动重新散列以增大表的容量。

 

这个例子创建了一个数字哈希表。它使用数字的名称作为关键字:

 

   散列表<String,Integer>数字

     = new Hashtable <String,Integer>();

   numbers.put(“one”,1);

   numbers.put(“two”,2);

   numbers.put(“three”,3);

要检索一个数字,请使用以下代码:

 

   整数n = numbers.get(“two”);

   if(n!= null){

     System.out.println(“two =”+ n);

   }}

由所有此类的“集合视图方法”返回的集合的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后随时对结构进行结构修改,除了通过迭代器自己的删除方法,迭代器将抛出ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在将来未定的时间冒着任意的,非确定性的行为冒险。 Hashtable的键和元素方法返回的枚举不是快速失败的。

 

请注意,迭代器的故障快速行为无法得到保证,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。失败快速迭代器尽最大努力抛出ConcurrentModificationException。因此,编写一个依赖于此异常的程序是正确的:迭代器的快速失败行为应仅用于检测错误。

 

从Java 2平台v1.2开始,这个类被改进以实现Map接口,使它成为Java Collections Framework的成员与新的集合实现不同,Hashtable是同步的。如果不需要线程安全的实现,建议使用HashMap来代替Hashtable。如果需要线程安全的高度并行实现,则建议使用java.util.concurrent.ConcurrentHashMap来代替Hashtable。

Java Collections Framework.
Unlike the new collection Implementations, {@code Hashtable} is synchronized. 
If a Thread-safe implementation is not needed, it is recommended to use {@link HashMap} in place of {@code Hashtable}. 
If a thread-safe Highly-concurrent implementation is desired, then it is recommended To use {@link java.util.concurrent.ConcurrentHashMap} in place of {@code Hashtable}.
大意是:
Java集合框架。
与新集合“实现”不同,{@code Hashtable}已同步。
如果不需要Thread-safe实现,建议使用{@link HashMap}代替{@code Hashtable}。

如果需要线程安全的高度并发实现,那么建议使用{@link java.util.concurrent.ConcurrentHashMap}代替{@code Hashtable}。


在Java中,有两个类提供了key-value机制;一个是HashaMap 另一个是HashTable

记住结论:

1. HashTable键值对不允许为null

2. HashTable线程安全因为主要的public方法都是synchronized



HashMap 和 HashTable的区别

HashMap允许keyvaluenull

HashTable不允许keyvaluenull

HashMap默认初始容量为16

HashTable默认初始容量为11.

HashMap非线程安全

HashTable线程安全(所有的public方法都是synchronized)

HashMap继承于AbstractMap

HashTable继承于Dictionary



基本属性

HashTable采用了"拉链法"实现哈希表,它定义了以下几个重要参数:

Entry[] table:Entry类型数组,Entry代表了"拉链"的节点,每一个Entry代表了一个键值对,哈希表的"key-value键值对"都是存储在Entry数组中的。

int count:HashTable的大小,注意这个大小并不是HashTable容器的大小,而是它所包含Entry键值对的数量。

int threshold:HashTable的阀值,用于判断是否需要调整HashTable的容量。threshold = 容量 * 加载因子

float loadFactor:加载因子

int modCount:用来实现“fail-fast”机制的(也就是快速失败),所谓快速失败就是在并发集合中,其进行迭代操作时,若有其他线程对其进行结构性的修改,这时迭代器会立马感知到,并且立即抛出ConcurrentModificationException异常,而不是等到迭代完成之后才告诉你(你已经出错了)。


构造方法

Hashtable():默认构造方法,默认初始容量11,加载因子0.75


Hashtable(int initialCapacity, float loadFactor):

构造一个HashTable,两个参数分别是hashTable初始容量,加载因子


Hashtable(int initialCapacity):

构造一个HashTable,传参为默认初始容量,加载因子则默认0.75


Hashtable(Map<? extends K, ? extends V> t):

构造一个包含指定Map(Collection)的HashTable。


HashTable方法

put方法

put方法的整个处理流程:

1. 计算key的hash值,根据hash值获得key在table数组中索引的位置

2. 迭代该key处的Entry链表

3. 若该链表中存在一个一样的key对象,那么直接替换其value值即可,否则将该key-value节点插入该index索引位置处。


首先我们假设一个容量为5的table,存在8、10、13、16、17、21。他们在table中位置如下:



然后我们插入一个数:put(16,22),key=16在table的索引位置为1,同时在1索引位置有两个数,程序对该“链表”进行迭代,发现存在一个key=16,这时要做的工作就是用newValue=22替换oldValue16,并将oldValue=16返回。

在put(33,33),key=33所在的索引位置为3,并且在该链表中也没有存在某个key=33的节点,所以就将该节点插入该链表的第一个位置。


在HashTable的put方法中有个地方需要注意:

HashTable的扩容操作,在put方法中,如果需要向table数组中添加元素,会首先进行容量校验,如果容量已经达到阀值,就会扩容

在这个rehash()方法中我们可以看到容量扩大了两倍+1,同时需要将原来HashTable中的元素一一复制到新的HashTable中,这个过程很耗时


阀值

比如初始值为11,加载因子默认为0.75,那么阀值threshold=8,当容器中元素数量达到8时,HashTable就会进行一次扩容,

容量:8*2+1=17,阀值thresold=17*0.75=13,当容量再次达到阀值,HashTable再次扩容。


get方法

get方法的处理过程:

1. 计算key的hash值,判断在table数组中的索引位置

2. 迭代该链表,找到匹配key对应的value值,没找到返回null



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值