【面经】蔚来后端一面

一 hashmap的put和get过程

put(K key, V value) 用于将键值对插入到 HashMap 中。如果该键已经存在,则更新对应的值。具体过程为:

计算哈希值HashMap 根据键对象的 hashCode() 方法计算出键的哈希值。哈希值是一个整数,用于确定键值对在 HashMap 中的存储位置。哈希值通常会经过一次二次哈希,以减少冲突并均匀分布。

确定存储位置(索引):通过哈希值和 HashMap 的容量(即桶数组的长度)计算出该键值对的存储索引。计算公式通常是 index = (n - 1) & hash,其中 n 是桶数组的长度。

处理哈希冲突:如果计算出的索引位置已经有元素存在,就发生了哈希冲突。HashMap 使用链地址法(即链表或红黑树)来解决冲突。

  • 链表:如果桶内的元素数量较少(默认阈值为8),HashMap 使用链表存储多个键值对。
  • 红黑树:如果桶内的元素数量较多(超过阈值8),HashMap 会将链表转化为红黑树,以提高查找和插入的效率。

插入键值对:如果在计算出的索引位置找不到相同哈希值和相同键的元素,HashMap 会将新键值对插入到对应的位置。如果找到了相同的键,则更新该键对应的值。

扩容检查:每次插入新元素时,HashMap 会检查当前的负载因子(Load Factor),如果超过了预设值(默认0.75),则会触发扩容操作。扩容时,HashMap 会将容量加倍,并将所有元素重新哈希分布到新的桶数组中。

get(Object key) 用于根据键从 HashMap 中检索对应的值。具体过程为:

计算哈希值:与 put 方法类似,HashMap 会根据传入的键对象计算出哈希值。

确定存储位置(索引):通过哈希值和桶数组的长度计算出该键的索引位置。

查找对应的键值对HashMap 会在计算出的索引位置开始查找,首先检查桶内第一个节点的哈希值和键是否匹配。如果匹配,则返回该节点对应的值。如果不匹配且存在链表或红黑树,则沿着链表或树结构继续查找,直到找到匹配的键或者查找结束(未找到则返回 null)。

二 介绍垃圾识别算法

引用计数法: 给每一个对象设置一个引用计数器,当有一个地方引用该对象的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用,也就是垃圾对象,等待回收; 缺点:无法解决循环引用的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法;

可达性分析法 从一个被称为GC Roots的对象向下搜索,如果一个对象到GC Roots没有任何引用链相连接时,说明此对象不可用,但一个对象满足上述条件的时候,不会马上被回收,还需要进行两次标记;第一次标记:判断当前对象是否有finalize()方法并且该方法没有被执行过,若不存在则标记为垃圾对象,等待回收;若有的话,则进行第二次标记;第二次标记将当前对象放入F-Queue队列,并生成一个finalize线程去执行该方法,虚拟机不保证该方法一定会被执行,这是因为如果线程执行缓慢或进入了死锁,会导致回收系统的崩溃;如果执行了finalize方法之后仍然没有与GC Roots有直接或者间接的引用,则该对象会被回收。

在java中可以作为GC Roots的对象有以下几种:

  • 虚拟机栈中引用的对象

  • 方法区类静态属性引用的变量

  • 方法区常量池引用的对象

  • 本地方法栈JNI引用的对象

三 写一个懒汉式单例

public class Singleton {
    // volatile 关键字确保 instance 在多线程环境下的可见性
    private static volatile Singleton instance;

    // 私有化构造方法,防止外部实例化
    private Singleton() {
    }

    // 提供获取实例的全局访问点
    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

四 讲讲java中的锁

ava 中的锁(Lock)是实现并发控制的基础工具,用于确保在多线程环境下共享资源的安全访问。锁的主要目的是在同一时间只允许一个线程访问共享资源,从而避免数据不一致的问题。常见的锁如:

  • synchronized:简单易用,适合小规模并发控制,自动管理锁的获取和释放。
  • ReentrantLock:更灵活的锁机制,提供了公平锁、可重入性、中断响应等特性。
  • ReadWriteLock:适合读多写少的场景,提高并发性。

五 Reentrantlock是如何实现可重入锁的

ReetrantLock是一个可重入的独占锁,主要有两个特性,一个是支持公平锁和非公平锁,一个是可重入。 ReetrantLock实现依赖于AQS(AbstractQueuedSynchronizer)。

ReetrantLock主要依靠AQS维护一个阻塞队列,多个线程对加锁时,失败则会进入阻塞队列。等待唤醒,重新尝试加锁。

ReentrantLock 的可重入性通过内部计数器和对当前持有锁线程的跟踪来实现。每次同一线程成功获取锁时,计数器增加;每次释放锁时,计数器减少。当计数器为 0 时,锁完全释放,其他线程可以获取锁。通过这种机制,ReentrantLock 可以允许同一线程多次进入受锁保护的代码块,而不导致死锁。

六 如果一张表中存在a,b字段,并且a,b单独建立索引,where a=x and b = y索引命中情况

MySQL 可能会选择使用其中一个单列索引,或者在某些情况下采用索引合并。

七 如果数据表中查询的数据访问是1万到1万零10,如何优化sql只查询10条数据

要优化查询指定范围内的数据,直接使用 LIMITOFFSET 是最简单的方法,但直接使用 OFFSET 可能会导致性能问题,因为数据库需要扫描和跳过大量数据。我们可以通过使用覆盖索引、避免深度分页、ID 范围查询或者将表进行分区的方法,进一步优化查询性能,减少数据扫描量和 I/O 操作。

八 如果存在一个字段为big_int, 大小为1kb,索引树的深度为3,问最多能存储多少条数据,提供计算思路即可

计算每个节点的容量:确定节点大小和键值对大小,计算每个节点可以容纳的键值对数。

计算各级节点的总数:从根节点到叶子节点,逐级计算各层节点的总数。

计算叶子节点的存储数据量:叶子节点存储实际数据,通过叶子节点总数计算出最大存储数据量。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值