hashMap深入了解

平时一直在用hashMap但是从未去了解底层源码。
昨天的面试恰好提到这个问题,除了能回答是通过hash表去解决hash冲突外,则一无所知。今天就对hashMap的底层源码一探究竟。

1.继承和实现关系

  • Map map = new hashMap()
    一直以为是hashMap实现的map接口,现在才发现是hashMap不仅仅是实现了Map接口,还是继承了AbstractMap,而AbstractMap也实现了Map接口。

首先来观察顶层接口Map

  • Map 接口
public  interface Map<K,V>{
	// 内部有一个Entry接口
	// 类似于链表的节点,所以对map的遍历,一般是对Entry的变量
	interface Entry<K,V>{
		//Entry 的 set/ get V 方法 
		……
	}
	// 其余就是增删改查方法,没什么可看
	……
}
  • hashMap的结构
  • 下图是hashMap的内部类结构,一般常用的是KeySet和EntrySet
  • 惊奇的发现居然也有树的节点TreeNode(因为认为这是hash结构,不会用树结构)
    在这里插入图片描述
  • 对keySet进行观察
    通过keySet()方法可以获得KeySet结构
    在这里插入图片描述
  • 对EntrySet进行观察
    -在这里插入图片描述
    方法都相同,也应该相同,因为只是map不同形式的节点表达方式。
    主要都用来遍历Map集合。
    了解到HashMap的基本结构之后,那么就要开始对构造方法进行,了解
  • 构造方法
    加粗样式
    有四个构造方法。看注释文档,神器的发现有两个参数因子对其性能有影响(原来所不知道的)
  1. 初始容量(the initial capacity)
  2. 加载因子(loadFactory)

API文档说明:HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。

通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
【简单 说,加载因子高,开辟空间小,容易rehash,加载因子小,开辟空间多,但是不容易rehash,空间换时间】(开辟空间 = 初始容量 / 加载因子)

线程安全问题

首先明确 hashMap是线程不安全的,因为没有做任何线程处理(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:

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

猜你喜欢

转载自blog.csdn.net/weixin_41263632/article/details/83863203