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()方法去找到链表中正确的节点,最终找到要找的值对象。

### JavaHashMap 的使用方法 #### 1. **简介** `HashMap` 是 Java 集合框架中的一个重要类,用于存储键值对(key-value pair)。它的底层基于哈希表实现,提供了快速的查找、插入和删除操作。`HashMap` 不保证元素的顺序,并允许一个 `null` 键和多个 `null` 值。 #### 2. **基本操作** 以下是 `HashMap` 的一些常用方法及其功能: - **put(key, value)**: 将指定的键值对存入 `HashMap`。 - **get(key)**: 返回与指定键关联的值。 - **remove(key)**: 移除指定键对应的映射关系。 - **size()**: 返回 `HashMap` 中键值对的数量。 - **clone()**: 创建并返回该 `HashMap` 对象的一个副本。 - **isEmpty()**: 如果此 `HashMap` 映射不包含任何键值对,则返回 true。 这些方法的具体用法可以通过下面的例子来说明。 --- #### 3. **示例代码** ##### 示例 1: 添加键值对并获取大小 ```java // Java program to demonstrate the use of size() method in HashMap import java.util.*; public class SizeExample { public static void main(String[] args) { // Create an empty HashMap Map<Integer, String> map = new HashMap<>(); // Add key-value pairs using put() map.put(10, "C"); map.put(20, "C++"); map.put(50, "JAVA"); map.put(40, "PHP"); map.put(30, "SFDC"); // Print the HashMap content System.out.println("HashMap Content: " + map); // Get the number of entries in the HashMap int size = map.size(); System.out.println("Size of HashMap: " + size); } } ``` 这段代码展示了如何向 `HashMap` 插入数据以及计算其大小[^1]。 --- ##### 示例 2: 删除特定键值对 ```java // Java program to demonstrate the removal operation in HashMap import java.util.*; public class RemoveExample { public static void main(String[] args) { // Initialize a HashMap with some data Map<Integer, String> map = new HashMap<>(); map.put(10, "C"); map.put(20, "C++"); map.put(50, "JAVA"); map.put(40, "PHP"); map.put(30, "SFDC"); // Display initial state System.out.println("Initial HashMap: " + map); // Remove entry associated with key '50' map.remove(50); // Show updated HashMap after deletion System.out.println("Updated HashMap after removing key '50': " + map); } } ``` 这里演示了如何移除某个键所对应的数据项[^2]。 --- ##### 示例 3: 复制一份新的 HashMap 实例 ```java // Example demonstrating cloning functionality within HashMaps. import java.util.*; public class CloneExample { public static void main(String[] args) { // Original HashMap creation and population HashMap<Integer, String> originalMap = new HashMap<>(); originalMap.put(10, "C"); originalMap.put(20, "C++"); originalMap.put(50, "JAVA"); originalMap.put(40, "PHP"); originalMap.put(30, "SFDC"); // Cloning process begins here HashMap<Integer, String> clonedMap = (HashMap<Integer, String>)originalMap.clone(); // Output both maps post-cloning action System.out.println("Original HashMap Contents: " + originalMap); System.out.println("Cloned HashMap Contents: " + clonedMap); } } ``` 本部分解释了复制现有 `HashMap` 的过程[^3]。 --- #### 4. **性能特点与其他注意事项** 由于 `HashSet` 内部依赖于 `HashMap` 来管理其成员集合,因此它们共享相似的时间复杂度特性——平均情况下 O(1),最坏情况取决于冲突处理机制[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值