HashMap底层实现原理,以及和Hashtable的比较

本文详细介绍了HashMap的数据结构、工作原理及常用方法。探讨了HashMap与Hashtable的区别,包括线程安全性、null键值的支持等。

boolean	containsValue(Object value)
Returns true if this map maps one or more keys to the specified value.

首先,我们要知道HashMap底层实现是数组(Entry类型)加上链表的数据结构---拉链法实现哈希表

Entry 实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数

                        

    1.通过hash()函数,计算key对应的哈希值,找到数组中存储位置,在冲突(不同的关键字,对应相同的哈希值,在)较少的情况下,他的查询效率是很高的,如果发生冲突就在数组元素所在链表的表头插入该值,所以在查找HashMap时候有两个关键函数,一个是hash()函数找到key在数组中的位置,一个是equals()函数,在链表中找到key,返回value,通常返回某关键字时候用的判断方法:(key==null ? k==null :key.equlas(k)),可见关键字可以为null

   2.Hashtable 的实例有两个参数影响其性能:初始容量 和 加载因子。容量是哈希表中桶的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。通常,默认加载因子是 0.75(当数据元素个数达到数组总容量的75%时候,要对数组进行动态扩展), 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 get 和 put 操作,都反映了这一点)。

  3.public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>,HashMap继承于AbstractMap父类,并且是非线程同步的


Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

关于HashMap和Hashtable的区别:

1.由于历史原因,Hashtable是基于Dictionary类,HashMap是1.2之后实现Map接口的实现(从其命名的不规范就可以看出年代之久。正常应该是HashTable,如果修改的,大量程序需要修改

2.最重要的不同是HashTable是同步的(每个方法前面都加上了synchronized关键字),而HashMap不是线程同步的,可以利用Collections类的synchronizedMap()方法创建一个安全的Map对象,并把它作为一个封装对象返回,利用这个对象可以让你同步访问潜在的HashMap.

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

3.HashMap中key,value允许为空,即HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。或者如果发现了搜索键,但它是一个空的值,那么get(object key)将返回null。如果有必要,用containKey()方法来区别这两种情况。Hashtable的key,value不允许为空



下面列出来一些HashMap常用的方法:

boolean	containsKey(Object key)
Returns true if this map contains a mapping for the specified key.

boolean	containsValue(Object value)
Returns true if this map maps one or more keys to the specified value.

<span style="background-color: rgb(255, 204, 102);">Set<Map.Entry<K,V>>	entrySet()</span>
Returns a Set view of the mappings contained in this map.

<span style="background-color: rgb(255, 204, 102);"> V	get(Object key)</span>
Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.

boolean	isEmpty()
Returns true if this map contains no key-value mappings.
<span style="background-color: rgb(255, 204, 102);">
Set<K>	keySet()</span>
Returns a Set view of the keys contained in this map.

<span style="background-color: rgb(255, 204, 102);">V	put(K key, V value)</span>
Associates the specified value with the specified key in this map.

V	remove(Object key)
Removes the mapping for the specified key from this map if present.

boolean	remove(Object key, Object value)
Removes the entry for the specified key only if it is currently mapped to the specified value.

V	replace(K key, V value)
Replaces the entry for the specified key only if it is currently mapped to some value.

int	size()
Returns the number of key-value mappings in this map.

Collection<V>	values()
Returns a Collection view of the values contained in this map.




以下是一些关于HashMap底层实现原理的面试问题及答案: ### 基础原理类 - **问题**:简述HashMap的工作原理。 - **答案**:HashMap是基于hashing的原理,使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。给put()方法传递键值时,先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。HashMap是在bucket中储存键对象值对象,作为Map.Entry。获取对象时,通过计算key的hashCode找到对应的bucket,再在该bucket中比较key的hashCodeequals方法来确定具体的键值对[^1]。 - **问题**:如何从HashMap中获取对象? - **答案**:假如要取某个key的值,通过get(key)方法获取数据的模,然后根据key与hashCode的值去比较对应bucket位置的keyhashCode,查看是否相等,如果不相等则通过next方法比较下一个节点的数据,直到key与hashCode对比的值都相等,此时获取的value就是当前key所对应的value [^4]。 ### 哈希冲突类 - **问题**:什么是哈希冲突,HashMap如何解决哈希冲突? - **答案**:哈希冲突是指不同的键通过哈希函数计算得到了相同的哈希值。HashMap解决哈希冲突采用链地址法,即当多个键值对的键计算出的哈希值相同时,会在对应的bucket位置形成一个链表,将这些键值对依次存储在链表中。在JDK 1.8中,当链表长度大于8,并且表的长度大于64的时候,链表会转换成红黑树,以提高查找效率 [^2]。 ### 线程安全类 - **问题**:HashMap为什么线程不安全? - **答案**:在JDK 1.7中,多线程下扩容可能造成死循环数据丢失;在JDK 1.8中,会出现数据覆盖的问题 [^2]。 - **问题**:如何解决HashMap的线程不安全问题? - **答案**:可以使用HashTable(已弃用)、Collections.synchronizedMap(不常用)、ConcurrentHashMap(常用)来解决HashMap线程不安全的问题 [^2]。 ### 其他特性类 - **问题**:为什么HashMap的初始容量扩容都是2的次幂? - **答案**:在计算元素存放位置时,使用位运算代替取模运算,提高效率。而且能使元素在哈希表中分布更均匀,减少哈希冲突 [^2]。 - **问题**:为什么负载因子是0.75? - **答案**:这是时间空间成本上的一种折中选择。如果设置过大,虽然空间利用率提高了,但是会增加哈希冲突的概率,导致查询效率降低;如果设置过小,虽然减少了哈希冲突,但是会频繁扩容,增加空间开销 [^2]。 ### 代码示例 ```java import java.util.HashMap; public class HashMapExample { public static void main(String[] args) { HashMap<String, Integer> map = new HashMap<>(); map.put("key1", 1); map.put("key2", 2); Integer value = map.get("key1"); System.out.println(value); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值