java集合之HashMap与Hashtable

HashMap

HashMap 扩展(extends) AbstractMap类和实现(implements)Map, Cloneable, Serializable这三个接口。HashMap是基于哈希表实现的映射接口,此实现提供所有可选映射操作,并允许空值和空键,且是不同步的。
HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。

HashMap非线程安全

javadoc中关于hashmap的一段描述如下:此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。

HashMap中的方法在缺省情况下是非Synchronize的,而Hashtable的方法是Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用Collections.synchronizedMap 方法来“包装”该映射。

HashMap类的所有“collection视图方法”返回的迭代器都是快速失败的:如果在创建迭代器之后对映射进行结构上的修改(结构上修改: 添加或删除一个或多个映射的任何操作;仅更改与实例已经包含的键关联的值不是结构修改。),除了通过迭代器自己的remove方法之外,迭代器将抛出ConcurrentModificationException。

影响性能的因素:初始容量和负载因子

HashMap实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的桶数,初始容量就是创建哈希表时的容量。负载因子是衡量在哈希表的容量被自动增加之前,哈希表被允许获得多少满的度量。自动增加 :当哈希表中的条目数超过负载因子和当前容量的乘积时,哈希表将被重新哈希(即重新构建内部数据结构),这样哈希表的桶数大约是原桶数的两倍。
HashMap默认的初始容量是16和负载因子是0.75
在HashMap的构造方法中无论我们指定的容量为多少,构造方法都会将实际容量设为不小于指定容量的2的次方的一个数,且最大值不能超过2的30次方

HashMap类的四个构造方法:
构造方法中的两个参数正是:初始容量和加载因子

方法签名 构造的HashMap类型
HashMap() 构造一个具有默认初始容量(16)和默认负载因子(0.75)的空HashMap。
HashMap(int initialCapacity) 构造一个具有指定初始容量和默认负载因子(0.75)的空HashMap。
HashMap(int initialCapacity, float loadFactor) 构造一个具有指定初始容量和指定负载因子的空HashMap
HashMap(Map m) 使用与指定映射相同的映射构造新的HashMap。

HashMap类实现的方法

修饰符和类型 方法签名和方法描述
void clear ()
从该映射中删除所有映射。
object clone()
返回此HashMap实例的浅拷贝:键和值本身没有克隆。
V compute(K key, BiFunction remappingFunction)
尝试为指定的键及其当前映射值计算映射(如果没有当前映射,则为null)。
*boolean containsKey(Object key)
如果此映射包含指定键的映射,则返回true。
*boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回true。
*V get(Object key)
返回指定键映射到的值,如果该映射不包含键的映射,则返回null。
V getOrDefault(Object key, V defaultValue)
返回指定键映射到的值,如果此映射不包含键的映射,则返回defaultValue。
*boolean isEmpty()
如果此映射不包含键值映射,则返回true。
Set keySet()
返回此映射中包含的键的集合视图。
*V put(K key, V value)
将指定值与此映射中的指定键关联
void putAll(Map m)
将指定映射的所有映射复制到此映射。
V putIfAbsent(K key, V value)
如果指定的键尚未与值关联(或映射为null),则将其与给定值关联并返回null,否则将返回当前值。
*V remove(Object key)
如果存在,则从此映射中删除指定键的映射。
*V replace(K key, V value)
仅当指定键的项当前映射到某个值时,才替换该项。
boolean replace(K key, V oldValue, V newValue)
仅当当前映射到指定值时,才替换指定键的项。
*boolean remove(Object key, Object value)
仅当当前映射到指定值时,才删除指定键的项。
*int size()
返回此映射中键值映射的数目。

Hashtable

Hashtable < K、V > 扩展(extends) ditionary类< K、V > 和实现(implements)Map, Cloneable, Serializable三个接口。
这个类实现了一个哈希表,它将键映射到值。任何非空对象都可以用作键或值。
要成功地从散列表中存储和检索对象,用作键的对象必须实现hashCode方法和equals方法。

Hashtable默认的初始容量是11和负载因子是0.75

从Java 2平台v1.2开始,对该类进行了改进以实现Map接口。与新的集合实现不同,Hashtable是同步的。如果不需要线程安全的实现,建议使用HashMap代替Hashtable。如果需要线程安全的高并发实现,那么建议使用ConcurrentHashMap代替Hashtable。

Hashtable与HashMap大致相同。构造方法只是方法名不同,参数与实现的功能相同。

Hashtable类的方法

修饰符和类型 方法签名和方法描述
void clear ()
清除此散列表,使其不包含键
*Object clone()
创建此散列表的浅拷贝
V compute(K key, BiFunction remappingFunction)
尝试为指定的键及其当前映射值计算映射(如果没有当前映射,则为null)
*V computeIfAbsent(K key, Function mappingFunction)
如果指定的键尚未与值关联(或映射为null),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非为null
V computeIfPresent(K key, BiFunction remappingFunction)
如果指定键的值为present且非null,则尝试计算给定键及其当前映射值的新映射
*boolean contains(Object value)
测试某些键是否映射到此散列表中的指定值
*boolean containsKey(Object key)
测试指定的对象是否是这个散列表中的键
*boolean containsValue(Object value)
如果此散列表将一个或多个键映射到此值,则返回true
Enumeration elements()
返回此散列表中值的枚举
*boolean equals(Object o)
根据Map接口中的定义,将指定的对象与此映射进行相等性比较
*V get(Object key)
返回指定键映射到的值,如果该映射不包含键的映射,则返回null
V getOrDefault(Object key, V defaultValue)
返回指定键映射到的值,如果此映射不包含键的映射,则返回defaultValue
*int hashCode()
根据映射接口中的定义返回此映射的哈希码值
*boolean isEmpty()
测试这个散列表是否没有将键映射到值
*V put(K key, V value)
将指定的键映射到此散列表中的指定值
V putIfAbsent(K key, V value)
如果指定的键尚未与值关联(或映射为null),则将其与给定值关联并返回null,否则将返回当前值
*protected void rehash()
增加此散列表的容量并在内部重新组织此散列表,以便更有效地容纳和访问其条目
*V remove(Object key)
从这个散列表中删除键(及其对应的值)
*boolean remove(Object key, Object value)
仅当当前映射到指定值时,才删除指定键的项
*V replace(K key, V value)
仅当指定键的项当前映射到某个值时,才替换该。
*boolean replace(K key, V oldValue, V newValue)
仅当当前映射到指定值时,才替换指定键的项
*int size()
返回此散列表中的键数
String String()
以一组条目的形式返回这个Hashtable对象的字符串表示形式,用大括号括起来,并用ASCII字符“,”(逗号和空格)分隔

HashMap与Hashtablede的不同

1、继承的父类不同

Hashtable继承(extends)ditionary类,HashMap继承(extends) AbstractMap类,但是都实现(implements)Map接口。

2、线程安全性不同

Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:

  Map m = Collections.synchronizedMap(new HashMap(...));

3、key和value是否允许null值

其中key和value都是对象,并且不能包含重复key,但可以包含重复的value。

Hashtable中,key和value都不允许出现null值。但是如果在Hashtable中有类似put(null,null)的操作,编译同样可以通过,因为key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey() 方法来判断。

4、hash值不同

哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。

Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值;Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模&0x7FFFFFFF的目的 :为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。

5、内部哈希初始化容量和扩容方式不同

HashTable在不指定容量的情况下的默认容量为11,而HashMap为16

Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

你可能感兴趣的:(java,java集合类)