HashTable是什么

  1. HashTable是基于哈希表的Map接口的同步实现
  2. HashTable中元素的key是唯一的,value值可重复
  3. HashTable中元素的key和value不允许为null,如果遇到null,则返回NullPointerException
  4. HashTable中的元素是无序的

  

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable{}

HashTable跟HashMap一样,同样是链表散列的数据结构,从源码中我们可以看出,Hashtable 继承于Dictionary类,实现了Map, Cloneable,Serializable接口

  1. Dictionary类是任何可将键映射到相应值的类的抽象父类,每个键和值都是对象
  2. Dictionary源码注释指出 Dictionary 这个类过时了,新的实现类应该实现Map接口

Hashtable成员变量

  1. table:一个Entry[]数组类型,而Entry(在 HashMap 中有讲解过)就是一个单向链表。哈希表的”key-value键值对”都是存储在Entry数组中的
  2. count:Hashtable的大小,它是Hashtable保存的键值对的数量
  3. threshold:Hashtable的阈值,用于判断是否需要调整Hashtable的容量,threshold的值 = (容量 * 负载因子)
  4. loadFactor:负载因子
  5. modCount:用来实现fail-fast机制的

Hashtable构造方法

  Hashtable 一共提供了 4 个构造方法

  1. public Hashtable(int initialCapacity, float loadFactor): 用指定初始容量和指定加载因子构造一个新的空哈希表
  2. public Hashtable(int initialCapacity):用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表
  3. public Hashtable():默认构造函数,容量为 11,加载因子为 0.75
  4. public Hashtable(Map< ? extends K, ? extends V> t):构造一个与给定的Map具有相同映射关系的新哈希表

  

public synchronized V put(K key, V value) {
//确保value不为null
if (value == null) {
throw new NullPointerException();
} //确保key不在hashtable中
//首先,通过hash方法计算key的哈希值,并计算得出index值,确定其在table[]中的位置
//其次,迭代index索引位置的链表,如果该位置处的链表存在相同的key,则替换value,返回旧的value
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
} modCount++;
if (count >= threshold) {
//如果超过阀值,就进行rehash操作
rehash(); tab = table;
hash = hash(key);
index = (hash & 0x7FFFFFFF) % tab.length;
} //将值插入,返回的为null
Entry<K,V> e = tab[index];
// 创建新的Entry节点,并将新的Entry插入Hashtable的index位置,并设置e为新的Entry的下一个元素
tab[index] = new Entry<>(hash, key, value, e);
count++;
return null;
}

  存储的流程如下:

  1. 判断value是否为空,为空则抛出异常
  2. 计算key的hash值,并根据hash值获得key在table数组中的位置index

    如果table[index]元素为空,将元素插入到table[index]位置

如果table[index]元素不为空,则进行遍历链表,如果遇到相同的key,则新的value替代旧的value,并返回旧 value,否则将元素插入到链头,返回null

    

Hashtable的获取

  

public synchronized V get(Object key) {
Entry tab[] = table;
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}

  获取的流程如下:

  1、通过 hash()方法求得key的哈希值

  2、根据hash值得到index索引

  3、迭代链表,返回匹配的key的对应的value,找不到则返回null

  

Hashtable遍历方式

  Hashtable有4种遍历方式:

  

//1、使用keys()
Enumeration<String> en1 = table.keys();
while(en1.hasMoreElements()) {
en1.nextElement();
} //2、使用elements()
Enumeration<String> en2 = table.elements();
while(en2.hasMoreElements()) {
en2.nextElement();
} //3、使用keySet()
Iterator<String> it1 = table.keySet().iterator();
while(it1.hasNext()) {
it1.next();
} //4、使用entrySet()
Iterator<Entry<String, String>> it2 = table.entrySet().iterator();
while(it2.hasNext()) {
it2.next();
}

Hashtable与HashMap的区别

  

Hashtable HashMap
方法是同步的 方法是非同步的
基于Dictionary类 基于AbstractMap,而AbstractMap基于Map接口的实现
key和value都不允许为null,遇到null,直接返回 NullPointerException key和value都允许为null,遇到key为null的时候,调用putForNullKey方法进行处理,而对value没有处理
hash数组默认大小是11,扩充方式是old*2+1 hash数组的默认大小是16,而且一定是2的指数

多线程存在的问题

  1、如果涉及到多线程同步时,建议采用HashTable

  2、没有涉及到多线程同步时,建议采用HashMap

  3、Collections 类中存在一个静态方法:synchronizedMap(),该方法创建了一个线程安全的 Map 对象,并把它作为一个封装的对象来返回

  synchronizedMap()其实就是对Map的方法加层同步锁,从源码中可以看出

  

//Collections.synchronizedMap(Map<K, V>)
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<K,V>(m);
} private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L; private final Map<K,V> m; // Backing Map
//同步锁
final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
//把this本身作为锁监视器, 这样任何线程访问他的方法都要获取该监视器.
mutex = this;
} SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
} public int size() {
synchronized(mutex) {return m.size();}
} //重写map的emty方法
public boolean isEmpty() {
synchronized(mutex) {return m.isEmpty();}
}
public boolean containsKey(Object key) {
synchronized(mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value) {
synchronized(mutex) {return m.containsValue(value);}
}
public V get(Object key) {
synchronized(mutex) {return m.get(key);}
} public V put(K key, V value) {
synchronized(mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized(mutex) {return m.remove(key);}
}
public void putAll(Map<? extends K, ? extends V> map) {
synchronized(mutex) {m.putAll(map);}
}
public void clear() {
synchronized(mutex) {m.clear();}
} private transient Set<K> keySet = null;
private transient Set<Map.Entry<K,V>> entrySet = null;
private transient Collection<V> values = null; //重写keySet方法
public Set<K> keySet() {
synchronized(mutex) {
if (keySet==null)
//mutex传给SynchronizedSet, 这样对于set内部操作也需要获取锁.
keySet = new SynchronizedSet<K>(m.keySet(), mutex);
return keySet;
}
} public Set<Map.Entry<K,V>> entrySet() {
synchronized(mutex) {
if (entrySet==null)
entrySet = new SynchronizedSet<Map.Entry<K,V>>(m.entrySet(), mutex);
return entrySet;
}
} public Collection<V> values() {
synchronized(mutex) {
if (values==null)
values = new SynchronizedCollection<V>(m.values(), mutex);
return values;
}
} public boolean equals(Object o) {
synchronized(mutex) {return m.equals(o);}
}
public int hashCode() {
synchronized(mutex) {return m.hashCode();}
}
public String toString() {
synchronized(mutex) {return m.toString();}
}
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized(mutex) {s.defaultWriteObject();}
}
}

  

深入理解java集合框架之---------HashTable集合的更多相关文章

  1. 牛客网Java刷题知识点之Java 集合框架的构成、集合框架中的迭代器Iterator、集合框架中的集合接口Collection(List和Set)、集合框架中的Map集合

    不多说,直接上干货! 集合框架中包含了大量集合接口.这些接口的实现类和操作它们的算法. 集合容器因为内部的数据结构不同,有多种具体容器. 不断的向上抽取,就形成了集合框架. Map是一次添加一对元素. ...

  2. Java集合框架概述和集合的遍历

    第三阶段 JAVA常见对象的学习 集合框架概述和集合的遍历 (一) 集合框架的概述 (1) 集合的由来 如果一个程序只包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序. 通常,程序 ...

  3. 第14章 集合框架(1)-List集合的各种类

    1.概述 1.1.Java集合框架的由来 1.2.什么是集合框架? 1.3.为什么需要集合框架 1.4.常用的框架接口规范 2.Vector类 2.1.存储原理 2.2.构造方法 2.3.常用方法 3 ...

  4. 深入理解Java并发框架AQS系列(一):线程

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...

  5. 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...

  6. 深入理解Java并发框架AQS系列(四):共享锁(Shared Lock)

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock) 深入 ...

  7. java集合框架容器 java框架层级 继承图结构 集合框架的抽象类 集合框架主要实现类

    本文关键词: java集合框架  框架设计理念  容器 继承层级结构 继承图 集合框架中的抽象类  主要的实现类 实现类特性   集合框架分类 集合框架并发包 并发实现类 什么是容器? 由一个或多个确 ...

  8. -1-3 java集合框架基础 java集合体系结构 Collection 常用java集合框架 如何选择集合 迭代器 泛型 通配符概念 Properties 集合 迭代器

    集合又称之为容器存储对象的一种方式 •数组虽然也可以存储对象,但长度是固定的:显然需要可变长度的容器 集合和数组的区别?                 A:长度区别                  ...

  9. (1)StringBuilder类和StringBuffer类 (2)日期相关的类 (3)集合框架 (4)List集合

    1.StringBuilder类和StringBuffer类(查手册会用即可)1.1 基本概念 由于String类描述的字符串内容无法更改,若程序中出现大量类似的字符串时需要申请独立的内存空间单独保存 ...

随机推荐

  1. 作业 c++编写

    1.第一版本程序Prog1:+ 给定一个数组,实现数组元素求和:,具体要求:实现对一维数组(a[100])的所有元素相加运算.+ 数据准备:a)数组长度:100:b)数组数据来源:实验数据A列:1~1 ...

  2. 构建命令maven install 打包不是最新的代码

    问题: 之前一直用的是mvn install 命令来构建项目,但是最近发现最新的代码没有在war包中.之前看的说 mvn install 命令会执行之前的所有阶段,会被编译,测试,打包. 经查最后采用 ...

  3. session喜欢丢值且占内存,Cookis不安全,用什么可以代替呢?

    localstorage sessionstorage 在线资料 webdb 这些都是基于HTML5的新特性! 此外还可以使用服务器文件.DB等.

  4. 在一般处理程序清理cookie

    清理cookie在ashx里面很奇怪,因为直接设置过期时间并不能成功,cookie还是会存在.所以需要添加一个同名的Cookie设置过期时间覆盖 HttpCookie cookie = null; / ...

  5. C# 加载配置文件

    //加载配置文件 var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .Add ...

  6. 开源应用框架BitAdminCore:更新日志20180903

    索引 NET Core应用框架之BitAdminCore框架应用篇系列 框架演示:https://www.bitadmincore.com 框架源码:https://github.com/chenyi ...

  7. UWP多线程枚举安全的List

    最近在做windows runtime下APP开发的工作.在Service层请求返回后,往往会通过回调的形式来通知UI更新数据.多个线程操作经常出现foreach抛出异常:System.Invalid ...

  8. jquery优化轮播图2

    继续优化 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  9. Django 项目拆分配置文件settings.py

    使用Django命令生成一个项目的基本结构时, 配置信息默认保存在和项目目录同名的目录下的settings.py文件里, 对于一个项目而言, 这样往往是不合适的, 在实际的开发中,需要将配置文件拆分为 ...

  10. 找到windows中的环境变量

    我的电脑,右击--->属性---->高级系统设置---->高级----->环境变量