java集合之HashMap与Hashtable

HashMap

HashMap<K,V> 扩展(extends) AbstractMap<K,V>类和实现(implements)Map<K,V>, 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<? extends K,? extends V> m)使用与指定映射相同的映射构造新的HashMap。
HashMap类实现的方法
修饰符和类型方法签名和方法描述
voidclear ()
从该映射中删除所有映射。
objectclone()
返回此HashMap实例的浅拷贝:键和值本身没有克隆。
Vcompute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
尝试为指定的键及其当前映射值计算映射(如果没有当前映射,则为null)。
*booleancontainsKey(Object key)
如果此映射包含指定键的映射,则返回true。
*booleancontainsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回true。
*Vget(Object key)
返回指定键映射到的值,如果该映射不包含键的映射,则返回null。
VgetOrDefault(Object key, V defaultValue)
返回指定键映射到的值,如果此映射不包含键的映射,则返回defaultValue。
*booleanisEmpty()
如果此映射不包含键值映射,则返回true。
SetkeySet()
返回此映射中包含的键的集合视图。
*Vput(K key, V value)
将指定值与此映射中的指定键关联
voidputAll(Map<? extends K,? extends V> m)
将指定映射的所有映射复制到此映射。
VputIfAbsent(K key, V value)
如果指定的键尚未与值关联(或映射为null),则将其与给定值关联并返回null,否则将返回当前值。
*Vremove(Object key)
如果存在,则从此映射中删除指定键的映射。
*Vreplace(K key, V value)
仅当指定键的项当前映射到某个值时,才替换该项。
booleanreplace(K key, V oldValue, V newValue)
仅当当前映射到指定值时,才替换指定键的项。
*booleanremove(Object key, Object value)
仅当当前映射到指定值时,才删除指定键的项。
*intsize()
返回此映射中键值映射的数目。

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类的方法
修饰符和类型方法签名和方法描述
voidclear ()
清除此散列表,使其不包含键
*Objectclone()
创建此散列表的浅拷贝
Vcompute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
尝试为指定的键及其当前映射值计算映射(如果没有当前映射,则为null)
*VcomputeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
如果指定的键尚未与值关联(或映射为null),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非为null
VcomputeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)
如果指定键的值为present且非null,则尝试计算给定键及其当前映射值的新映射
*booleancontains(Object value)
测试某些键是否映射到此散列表中的指定值
*booleancontainsKey(Object key)
测试指定的对象是否是这个散列表中的键
*booleancontainsValue(Object value)
如果此散列表将一个或多个键映射到此值,则返回true
Enumerationelements()
返回此散列表中值的枚举
*booleanequals(Object o)
根据Map接口中的定义,将指定的对象与此映射进行相等性比较
*Vget(Object key)
返回指定键映射到的值,如果该映射不包含键的映射,则返回null
VgetOrDefault(Object key, V defaultValue)
返回指定键映射到的值,如果此映射不包含键的映射,则返回defaultValue
*inthashCode()
根据映射接口中的定义返回此映射的哈希码值
*booleanisEmpty()
测试这个散列表是否没有将键映射到值
*Vput(K key, V value)
将指定的键映射到此散列表中的指定值
VputIfAbsent(K key, V value)
如果指定的键尚未与值关联(或映射为null),则将其与给定值关联并返回null,否则将返回当前值
*protected voidrehash()
增加此散列表的容量并在内部重新组织此散列表,以便更有效地容纳和访问其条目
*Vremove(Object key)
从这个散列表中删除键(及其对应的值)
*booleanremove(Object key, Object value)
仅当当前映射到指定值时,才删除指定键的项
*Vreplace(K key, V value)
仅当指定键的项当前映射到某个值时,才替换该。
*booleanreplace(K key, V oldValue, V newValue)
仅当当前映射到指定值时,才替换指定键的项
*intsize()
返回此散列表中的键数
StringString()
以一组条目的形式返回这个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倍。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值