【Java八股面试系列】Arraylist和HashMap的底层原理

文章目录

    • ArrayList源码
      • 总:
      • 构造方法
      • 扩容机制
      • remove
    • HashMap
      • 总:
        • 构造方法
        • 细节问题
        • putVal()方法
        • resize()方法
        • Hash值
      • HashMap常见问题
    • ConcurrentHashMap
      • 总:
        • putVal()方法
        • 自己的测试
    • 为什么重写HashCode和equals

ArrayList源码

总:

**ArrayList**底层是使用名为 **elementDataObject动态数组进行实现,与Java中的数组相比,她的容量能够动态的进行增长。在我们更新元素的时候,我们会通过ensureCapacityInternal()方法来确保我们的容量够用,如果容量不够则调用grow()**方法对我们的数组进行扩容为1.5倍,然后将我们原来的的数组复制过去。

ArrayList 和 Vector 的区别?

  • ArrayListList 的主要实现类,底层使用 Object[]存储,适用于频繁的查找工作,线程不安全 。
  • VectorList 的古老实现类,底层使用Object[] 存储,线程安全。

ArrayList 可以添加 null 值吗?

ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。

Arraylist 与 LinkedList 区别?

Arraylist 和 LinkedList都是实现了 List接口,都是线程不安全的。

不同点:

  • Arraylist底层是通过object数组进行实现,而LinkedList底层是通过双向链表进行实现
  • AL实现了随机读取接口,能够随机进行读取,用来查询的效率高,LL不能进行随机读取,只能遍历进行读取,但是她的插入效率高
  • 内存空间的占用上,AL的预留空间会占用一定的位置,而LL会占用更多的空间,因为要保存其他的一些位置信息

构造方法

Arraylist有有参构造方法也有无参构造方法,有参的构造方法会将我们的容器大小设置为设置的大小

无参构造方法先是将提前创建好的空数组给他,后续使用的时候在进行扩容操作

扩容机制

这里每次add操作的时候都会使用ensure CapacityInternal()方法进行容量判断,如果不足则会使用grow进行扩容

在这里插入图片描述

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

这里的calculate Capacity方法主要是判断容器是否是已经初始化过

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

这里计算所需的最小容量是否大于当前的容器容量

private void ensureExplicitCapacity(int minCapacity) {
    // 用来记录遍历的时候时候,集合有没有进行改变
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

这里先将数组扩容为原来的1.5倍,判断是否够用,或者有没有超过最大的容量

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

hugeCapacity()方法 当我们容量超过了当前容器设置的最大值的时候执行

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?	// 这里的MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }


如果不够的话,也只能最多再添加8个了

remove

Arraylist 集合移除元素时候执行的函数,然后使用 **System.arraycopy()**方法将数组进行复制再将原来位置设置为null方便进行 gc

public E remove(int index) {
    rangeCheck(index); // 判断移除的元素是否越界

    modCount++;  // 判断当前的集合是否被修改
    E oldValue = elementData(index);

    int numMoved = size - index - 1;	// 确定移除位置
    if (numMoved > 0)	
        System.arraycopy(elementData, index+1, elementData, index, numMoved);  
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}

HashMap

总:

HashMap在 jdk1.8之前是由Node数组+链表组成的,在jdk1.8以后更改了解决hash冲突的方式,是由Node数组+链表或者红黑树实现。元素添加是通过**putVal()方法添加,HashMap通过扰动函数处理得到Hash值,如果发生hash冲突的时候就会先判断当前节点的链表大小如果超过8,然后调用treeifBin()**如果hashMap总的容量大于64则会转为红黑树进行存储。

为什么不使用多路平衡二叉树?

  1. 红黑树是一种平衡二叉树,多路平衡二叉树需要存储更多的节点信息,空间上会使用更多的空间
  2. 操作的复杂性,红黑树不是严格的平衡二叉树,两边子节点的高度差没有完全的差1,插入的时候更好的处理
  3. 红黑树的查询表现已经可以了,不需要多路平衡二叉树的B+树的范围查询了
构造方法

构造方法有三个,关键就是携带参数的问题,有没有 **initialCapacity初始化容量**和 loadFactor扩容阈值

细节问题

默认的大小是16,AL是10,扩容每次为一倍,AL每次扩容1.5倍

putVal()方法

在这里插入图片描述

resize()方法

计算出新的**Capacity**和 threshold的值,然后创建一个新的数组,将我们原来的数组的值复制进去

**threshold**的值是通过 Capacity的值与设置的阈值0.75相乘得出来的

Hash值

hashCode()方法返回的是int整数类型,其范围为


-(2 ^ 31)~(2 ^ 31 - 1),而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置;

那怎么解决呢?

HashMap自己实现了自己的hash()方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均;
在保证数组长度为2的幂次方的时候,使用hash()运算之后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操作更加有效率,二来也是因为只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题;

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

HashMap常见问题

为什么线程不安全

因为可能会造成数据丢失的问题,假如两个线程同时进行插入,并且产生了Hash碰撞,那么线程1判断完成以后插入之前挂起,同时线程2进行插入,这时线程1插入的将会覆盖线程2

ConcurrentHashMap

总:

ConcurrentHashMap是一个并发容器,能够解决多个线程使用HashMap造成的线程安全问题,底层的**putVal函数**是通过 Cas操作和Synchronized操作来保证线程的安全性。其他的结构和HashMap一样。

putVal()方法

和HashMap的过程基本一致

只是如果计算出hash值原位置没有的话,直接使用cas操作进行插入

如果后续碰撞则使用synchronized进行插入

自己的测试

自己使用了四个线程,分别进行100万次随机位置的写入,查看每个线程的完成时间,平均每个线程的完成时间将会降低百分之30左右。

为什么重写HashCode和equals

因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

思考:重写 equals() 时没有重写 hashCode() 方法的话,使用 HashMap 可能会出现什么问题。

总结

  • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
  • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/508018.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营三刷day41 | 动态规划之 343. 整数拆分 96.不同的二叉搜索树

三刷day41 343. 整数拆分确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp的初始化确定遍历顺序举例推导dp数组 96.不同的二叉搜索树确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组…

2024年,我写了一个视频去水印的微信小程序

花了两天时间&#xff0c;写了一个简单的微信小程序&#xff0c;主要为了学习一下微信小程序相关的知识。 目录 一、功能介绍 二、页面展示 三、简单总结 四、在线体验 一、功能介绍 小程序的主要功能是对目前的主流视频平台的视频进行去水印处理。 项目难点在于收集各个平…

关于多物理场耦合仿真的相关思考

关于多物理场耦合仿真&#xff0c;写点自己的思考。 1 核心本质 多物理场耦合仿真&#xff0c;听起来是个挺高大上的名词。不少人被各种名词创新搞得云里雾里&#xff0c;不知所谓。 实际上&#xff0c;多物理场耦合仿真理解起来并不算复杂。搞清楚了本质&#xff0c;做多物理…

LeetCode-热题100:160. 相交链表

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&…

“美国债务螺旋上升,每百天膨胀万亿”!华尔街:投入比特币是明智之举,美元早晚垮台?

​ 前不久&#xff0c;黄金和比特币价格的双双逼近历史高位&#xff0c;再度吸引了不少金融市场参与者的关注。虽然这两类资产大涨的背后&#xff0c;有着诸如比特币减半临近、地缘局势引发避险等各自的原因&#xff0c;但也有一些业内人士提到了美国政府债务规模激增等无法回…

day_2FreeRTOS使用PWM+ADC光敏电阻完成光控灯实验

主要代码&#xff1a; int adc_val0;//保存ADC采集到的数值 float volt0;//保存电压值HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);//打开定时器的PWM通道3 TIM3->CCR30;//改变CCR的值&#xff0c;范围0——999&#xff0c;不能超过ARRwhile (1){ HAL_ADC_Start(&had…

小米SU7又“赢麻了”对标雷军的爽文人生:天选成功人士

会议之眼 快讯 2024年3月28日&#xff0c;小米SU7汽车盛大发布&#xff0c;吸引了众多关注者。SU7标准版售价21.59万元&#xff0c;Pro版24.59万元&#xff0c;Max版本29.99万元&#xff0c;全部控制在30万元以内。发布会场面火爆&#xff0c;各大车企领导齐聚&#xff0c;雷军…

OMP压缩感知仿真(MATLAB)

clc; clearvars; close all;% 读文件 Ximread(mandrill256.bmp); tic; Xdouble(X); [m,n]size(X);% % 小波变换矩阵生成 [LL1, LH1, HL1, HH1] dwt2(X, haar); [LL2, LH2, HL2, HH2] dwt2(LL1, haar); % [LL3, LH3, HL3, HH3] dwt2(LL2, haar); % [LL4, LH4, HL4, HH4] d…

ObjectiveC-07-OOP面向对象程序设计基础

OOP&#xff08;面向对象程序设计&#xff09;是一个简单又复杂的课题&#xff0c;之所以简单是因为其概念清晰&#xff0c;内容简单&#xff0c;之所以复杂是因为没有固定的模式可寻&#xff0c;正所谓千人千面。 从本节开始&#xff0c;笔者大概会用5篇左右不同的专题来讲解O…

虚拟机ip不停地变每次使用ssh不好登录?有手就行!

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 虚拟机ip不停地变每次使用ssh不好登录&#xff1f;有手就行&#xff01; 桥接模式下固定ip&#xff1f;NoAvahi服务&#xff0c;你值得拥有Avahi解决方案虚拟机中配置Avahi服务配置成功展示测试成功 桥…

MySQL故障排查与生产环境优化

一、MySQL逻辑架构图 客户端和连接服务核心服务功能存储引擎层数据存储层 二、MySQL故障排查 1、MySQL单实例故障排查 故障一 故障现象&#xff1a; ERROR 2002 (HY000): Cant connect to local MySQL server through socket /data/mysql/mysql.sock (2)问题分析&#xff…

使用Pollard_rho算法分解质因数

分解质因数的朴素算法 最简单的算法即为从 [2, sqrt&#xff08;N&#xff09;] 进行遍历。 vector<int> breakdown(int N) {vector<int> result;for (int i 2; i * i < N; i) {if (N % i 0) { // 如果 i 能够整除 N&#xff0c;说明 i 为 N 的一个质因子。…

求组合背包II(acwing)

题目描述&#xff1a; 给定n组循问&#xff0c;每组询问给定两个整数a&#xff0c;b&#xff0c;请你输出Ca^b mod (1e9 7)的值&#xff0c;。 输入格式&#xff1a; 第一行包含整数n。 接下来2行&#xff0c;每行包含一组a和b。 输出格式&#xff1a; …

Vscode下使用markdown入门

1.安装vscode插件 1. **Markdown All in One** ——提供丰富的Markdown相关的快捷键、自动补全功能&#xff0c;提高md文档编写生产力 2. **Markdown Preview Ehanced** ——用于渲染当前编写文档的效果同步预览 3. **Paste Image** ——用于快速引用图片至Markdown文…

视频素材库有哪些网站?八大平台视频素材库创作推荐

视频创作的小达人们&#xff0c;是不是经常在想&#xff0c;视频素材库有哪些网站能提供高质量的素材呢&#xff1f;别担心&#xff0c;今天我要为你们揭秘八个超棒的视频素材网站&#xff0c;让你的视频制作更加轻松在创作的路上如鱼得水&#xff01; 蛙学网&#xff1a;海量…

【tensorflow框架神经网络实现鸢尾花分类_Keras】

文章目录 1、前言2、鸢尾花分类3、结果打印 1、前言 【tensorflow框架神经网络实现鸢尾花分类】一文中使用自定义的方式&#xff0c;实现了鸢尾花数据集的分类工作。在这里使用tensorflow中的keras模块快速、极简实现鸢尾花分类任务。 2、鸢尾花分类 import tensorflow as t…

.DevicData-P-XXXXXXXX勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 随着信息技术的飞速发展&#xff0c;网络安全问题日益突出&#xff0c;其中勒索病毒成为了一种日益严重的威胁。.DevicData-P-XXXXXXXX勒索病毒就是其中一种典型的恶意软件&#xff0c;它通过加密用户文件并要求赎金来解锁的方式&#xff0c;给企业和个人带来…

【Java项目】基于SpringBoot的【心灵治愈交流平台】

目录 背景 技术简介 系统简介 界面预览 背景 随着网络不断的普及发展&#xff0c;心灵治愈交流平台依靠网络技术的支持得到了快速的发展&#xff0c;首先要从用户的实际需求出发&#xff0c;通过了解用户的需求开发出具有针对性的首页、系统公告、心理咨询师、心灵专栏、压…

基于springboot实现网上点餐系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现网上点餐系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于网上点餐系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了网上点餐系统…

【了解下Oracle】

&#x1f308;个人主页:程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…