HashMap
HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
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。 |
修饰符和类型 | 方法签名和方法描述 |
---|---|
void | clear () 从该映射中删除所有映射。 |
object | clone() 返回此HashMap实例的浅拷贝:键和值本身没有克隆。 |
V | compute(K key, BiFunction super K,? super V,? extends V> 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 extends K,? extends V> 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 < 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大致相同。构造方法只是方法名不同,参数与实现的功能相同。
修饰符和类型 | 方法签名和方法描述 |
---|---|
void | clear () 清除此散列表,使其不包含键 |
*Object | clone() 创建此散列表的浅拷贝 |
V | compute(K key, BiFunction super K,? super V,? extends V> remappingFunction) 尝试为指定的键及其当前映射值计算映射(如果没有当前映射,则为null) |
*V | computeIfAbsent(K key, Function super K,? extends V> mappingFunction) 如果指定的键尚未与值关联(或映射为null),则尝试使用给定的映射函数计算其值,并将其输入到此映射中,除非为null |
V | computeIfPresent(K key, BiFunction super K,? super V,? extends V> 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字符“,”(逗号和空格)分隔 |
Hashtable继承(extends)ditionary类,HashMap继承(extends) AbstractMap类,但是都实现(implements)Map接口。
Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是非Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
其中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() 方法来判断。
哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
hashCode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值。
Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值;Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的 :为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16。
Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。