字节面试官问我,HashMap 的源码看过吗?我???(1)

简介: 字节面试官问我,HashMap 的源码看过吗?我???

字节面试官问我,Java 的 HashMap 的源码看过吗?我???我花了十分钟给他解释的清清楚楚的。


先看再点赞,给自己一点思考的时间,微信搜索【沉默王二】关注这个有颜值却假装靠才华苟且的程序员。

本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章。

List 系列差不多写完了, 单线程环境下最重要的就是 ArrayList 和 LinkedList,多线程环境下最重要的就是 CopyOnWriteArrayList,新来的同学可以点击链接回顾一下 List 的知识点。接下来,我要带着 HashMap 去爬山了,注意不是六峰山,纯粹就是为了锻炼了一下身体,不不不,纯粹是为了和 HashMap 拉近关系,同学们注意不要掉队。




说一句很废的话,HashMap 是一个 Map,用来存储 key-value 的键值对,每个键都可以精确地映射到一个值,然后我们可以通过这个键快速地找到对应的值。


对于一个 List 来说,如果要找到一个值,时间复杂度为 O ( n ) O(n) O(n),如果 List 排序过的话,时间复杂度可以降低到 O ( l o g n ) O(log n) O(logn)(二分查找法),但如果是 Map 的话,大多数情况下,时间复杂度能够降低到 O ( 1 ) O(1) O(1)。


来看一下 HashMap 的特点:


HashMap 的键必须是唯一的,不能重复。


HashMap 的键允许为 null,但只能有一个这样的键;值可以有多个 null。


HashMap 是无序的,它不保证元素的任何特定顺序。


HashMap 不是线程安全的;多线程环境下,建议使用 ConcurrentHashMap,或者使用 Collections.synchronizedMap(hashMap) 将 HashMap 转成线程同步的。


只能使用关联的键来获取值。


HashMap 只能存储对象,所以基本数据类型应该使用其包装器类型,比如说 int 应该为 Integer。


HashMap 实现了 Cloneable 和 Serializable 接口,因此可以拷贝和序列化。


01、HashMap 的重要字段


HashMap 有 5 个非常重要的字段,我们来了解一下。(JDK 版本为 14)


transient Node<K,V>[] table;

transient int size;

transient int modCount;

int threshold;

final float loadFactor;



1)table 是一个 Node 类型的数组,默认长度为 16,在第一次执行 resize() 方法的时候初始化。


static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

final HashMap.Node<K,V>[] resize() {

   newCap = DEFAULT_INITIAL_CAPACITY;

   Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

}



Node 是 HashMap 的一个内部类,实现了 Map.Entry 接口,本质上是一个键值对。


static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    HashMap.Node<K,V> next;
    Node(int hash, K key, V value, HashMap.Node<K,V> next) {
        ...
    }
    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }
    public final int hashCode() {
        ...
    }
    public final V setValue(V newValue) {
        ...
    }
    public final boolean equals(Object o) {
        ...
    }
}



2)size 就是 HashMap 中实际存储的键值对数量,它和 table 的 length 是有区别的。


为了说明这一点,我们来看下面这段代码:


HashMap<String,Integer> map = new HashMap<>();

map.put("1", 1);

1

2

声明一个 HashMap,然后 put 一个键值对。在 put() 方法处打一个断点后进入,等到该方法临近结束的时候加一个 watch(table.length),然后就可以观察到如下结果。




也就是说,数组的大小为 16,但 HashMap 的大小为 1。


3)modCount 主要用来记录 HashMap 实际操作的次数,以便迭代器在执行 remove() 等操作的时候快速抛出 ConcurrentModificationException,因为 HashMap 和 ArrayList 一样,也是 fail-fast 的。


关于 ConcurrentModificationException 的更多信息,请点击下面的链接查看 03 小节的内容。


4)threshold 用来判断 HashMap 所能容纳的最大键值对数量,它的值等于数组大小 * 负载因子。默认情况下为 12(16 * 0.75),也就是第一次执行 resize() 方法的时候。


static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

static final float DEFAULT_LOAD_FACTOR = 0.75f;


final HashMap.Node<K,V>[] resize() {

   newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

}



5)loadFactor 为负载因子,默认的 0.75 是对空间和时间效率上的一个平衡选择,一般不建议修改,像我这种工作了十多年的老菜鸟,就从来没有修改过这个值。


相关文章
|
6天前
|
存储 安全 Java
一天十道Java面试题----第二天(HashMap和hashTable的区别--------》sleep、wait、join)
这篇文章是关于Java面试的第二天笔记,涵盖了HashMap与HashTable的区别、ConcurrentHashMap的实现原理、IOC容器的实现方法、字节码的概念和作用、Java类加载器的类型、双亲委派模型、Java异常体系、GC如何判断对象可回收、线程的生命周期及状态,以及sleep、wait、join、yield的区别等十道面试题。
一天十道Java面试题----第二天(HashMap和hashTable的区别--------》sleep、wait、join)
|
17天前
面试官: 请你手写一份 Call()源码,看完此篇不用担心!
面试官: 请你手写一份 Call()源码,看完此篇不用担心!
|
17天前
|
存储 JavaScript 前端开发
JS浅拷贝及面试时手写源码
JS浅拷贝及面试时手写源码
|
1月前
|
存储 安全 Java
Android面试题之ArrayList源码详解
ArrayList是Java中基于数组实现的列表,提供O(1)的索引访问,但插入和删除操作平均时间复杂度为O(n)。默认容量为10,当需要时会通过System.arraycopy扩容。允许存储null,非线程安全。面试常问:List是接口,ArrayList是其实现之一,推荐使用List接口编程以实现更好的灵活性。更多详情见[ArrayList源码](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList.Node)。
20 2
|
1月前
|
存储 安全 Java
Java面试题:请解释Java内存模型,并说明如何在多线程环境下使用synchronized关键字实现同步,阐述ConcurrentHashMap与HashMap的区别,以及它如何在并发环境中提高性能
Java面试题:请解释Java内存模型,并说明如何在多线程环境下使用synchronized关键字实现同步,阐述ConcurrentHashMap与HashMap的区别,以及它如何在并发环境中提高性能
21 0
|
1月前
|
前端开发 数据挖掘
字节面试:领域、子域、核心域、通用域和支撑域怎么划分?
领域驱动设计(DDD)通过划分业务领域和子域简化复杂性。领域是业务问题的范围,子域是更小的专业部分。核心域代表业务的核心竞争力,如电商中的商品、订单和支付;通用域提供跨领域服务,如用户管理;支撑域支持核心功能,如物流、客服和数据分析。这种划分帮助团队专注关键业务,提高开发效率和软件对业务需求的契合度。
|
2月前
|
存储 安全 Java
《ArrayList & HashMap 源码类基础面试题》面试官们最喜欢问的ArrayList & HashMap源码类初级问,你都会了?
《ArrayList & HashMap 源码类基础面试题》面试官们最喜欢问的ArrayList & HashMap源码类初级问,你都会了?
23 0
|
2月前
HashMap源码
HashMap源码
14 0
|
4天前
|
存储 缓存 网络协议
复盘女朋友面试4个月的Java基础题
这篇文章是关于Java基础面试题的复盘,涵盖了HashMap原理、对象序列化作用等高频面试问题,并强调了Java基础知识的重要性。
复盘女朋友面试4个月的Java基础题
|
6天前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)