Hashmap

Hashmap是java面试中经常被问的问题,其重要性不言而喻。这不禁想起HashMap和Hashtable的比较:

  1. HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
  2. HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
  3. HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
  4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。HashMap不能保证随着时间的推移Map中的元素次序是不变的。
  5. HashMap几乎可以等价于Hashtable,那为什么用Hashmap呢?因为它快啊。Haspmap实现了Map接口,说起Map,我们就想起了put和get方法,那我们就讲一下Hashmap怎么在get方法上成为快男的。

性能是映射表的一个重要问,当我们应用get()方法,如果使用线性搜索的话,执行速度会很慢,这正是hashmap提高速度的地方。Hashmap使用了特殊的值,称作散列码,来取代对键的缓慢搜索。散列码是相对唯一的,用以代表对象的int值。hashcode()是根类Object的方法,因此所有Java对象都能产生散列码,Hashmap就是使用对象的hashcode进行快速查询,因此hashmap能显著提升性能。


数据结构的物理存储结构只有两种:顺序存储结构和链式存储结构(像栈,队列,树,图等是从逻辑结构去抽象的,映射到内存中,也这两种物理组织形式),而HashMap采用了链地址法,也就是数组+链表的方式。

既然我们了解了hashmap的基本数据结构,那hashmap的实现原理是怎样的呢?
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对,Entry是HashMap中的一个静态内部类。 所以,HashMap的整体结构如下
HashMap结构图
HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。

HashMap由数组+链表组成的,这样的数据结构有什么用呢?
HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为O(1),因为最新的Entry会插入链表头部,急需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

当两个对象就算hashcode相同,但是它们可能并不相等,怎么解决呢?
这就是HashMap中的碰撞问题,了解了hashmap特殊的数据结构是不是恍然大悟啊。因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。

hashmap这样的数据结构,怎么使用get方法来获取值对象呢?
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后获取值对象。当hashcode相同,它们的bucket位置可能有两个对象,我们就需要找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HashMapJava中的一种数据结构,它提供了一种键值对的映射关系。它基于哈希表实现,可以快速地插入、删除和查找元素。HashMap中的键是唯一的,而值可以重复。 HashMap的循环可以通过以下几种方式实现: 1. 使用迭代器循环:可以通过调用HashMap的`entrySet()`方法获取键值对的集合,然后使用迭代器遍历集合中的元素。示例代码如下: ```java HashMap<String, Integer> hashMap = new HashMap<>(); // 添加键值对 hashMap.put("A", 1); hashMap.put("B", 2); hashMap.put("C", 3); // 使用迭代器循环 Iterator<Map.Entry<String, Integer>> iterator = hashMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); String key = entry.getKey(); Integer value = entry.getValue(); // 处理键值对 System.out.println("Key: " + key + ", Value: " + value); } ``` 2. 使用for-each循环:可以直接使用for-each循环遍历HashMap中的键值对。示例代码如下: ```java HashMap<String, Integer> hashMap = new HashMap<>(); // 添加键值对 hashMap.put("A", 1); hashMap.put("B", 2); hashMap.put("C", 3); // 使用for-each循环 for (Map.Entry<String, Integer> entry : hashMap.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); // 处理键值对 System.out.println("Key: " + key + ", Value: " + value); } ``` 3. 只循环键或值:如果只需要循环HashMap中的键或值,可以使用`keySet()`方法获取键的集合或使用`values()`方法获取值的集合,然后进行循环遍历。示例代码如下: ```java HashMap<String, Integer> hashMap = new HashMap<>(); // 添加键值对 hashMap.put("A", 1); hashMap.put("B", 2); hashMap.put("C", 3); // 循环键 for (String key : hashMap.keySet()) { // 处理键 System.out.println("Key: " + key); } // 循环值 for (Integer value : hashMap.values()) { // 处理值 System.out.println("Value: " + value); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值