集合面试题笔记

6319 HashMap 原理 数据结构:数组 + 链表 (JAVA8之后:数组+链表+红黑树) 数据结构 jdk1.7:数组+链表,数组每个元素是一个链表的表头,当发生冲突,将新元素添加在头部(头插法) hash冲突:头插法,❗扩容时可能造成环形链表 jdk1.8: 引入红黑树,当链表节点超过8个,那么这个链表会转换为红黑树。查询时间由 O(n) 优化为 O(logn)。当节点小于6个,再转换为链表。 hash冲突:尾插法,避免环形链表❗ 扩容机制 jdk1.7 扩容,元素会重新计算hash值,并分配到新的扩容数组中。⭐比较耗时 扩容时,头插法,在多线程情况下,可能造成环形链表 jdk1.8 扩容时,利用了元素哈希值和旧数组容量关系,减少了重新计算的哈希次数 扩容时,尾插法,避免环形链表 使用键的hashcode()计算hash值,然后(n-1) & hash确定数组中的位置。 n-1 => 10000 - 1 = 01111 HashMap的初始默认容量为16,负载因子为0.75。当存储的元素达到75%时,进行扩容,扩容为原来的2倍空间 扩展知识: hashmap的红黑树优化: JAVA8开始,为了优化hash冲突时的查找性能。但链表的长度超过8时,链表会转变为红黑树。红黑树是一种自平衡的二叉搜索树 查找时间 O(n) => O(logn)。 当元素少于6个,切换为链表 hashCode() 和 equals()的重要性: hashCode计算hash值,决定键的存储位置。 而hashCode相同=>冲突。 equals()比较的值 1// HashMap Node class 2static class Entry<K,V> { 3 final K key; // 键 4 V value; // 值 5 final int hash; // 计算后的哈希值 6 Entry<K,V> next; // 指向下一个元素的指针(形成链表) 7} 为什么头插法&多线程&扩容会造成环形链表? ...

March 13, 2025 · 3 min · 608 words · LongWei

JVM面试题笔记

521. Java如何实现跨平台的? Java编译生成的是字节码文件.class文件,而不是特定于某个操作系统的机器码。 不同操作系统上都有各自实现的JVM,负责将字节码翻译成特定平台的机器代码并执行。使得JAVA文件可以被不同操作系统上的JVM运行。包装了一层。 9807. JVM的组成部分 主要组成部分: 编译好的JAVA字节码(.class文件)准备就绪。 类加载器子系统:将class文件加载到内存中(运行时的数据区)。 运行时数据区。 执行引擎(命令解释器):将字节码文件翻译成机器码,并交给CPU执行; 本地方法接口:过程中会调用不同语言提供的接口,比如驱动和..,调用本地方法接口,例如操作系统级别的功能或者高性能库。 522. 编译执行和解释执行 编译执行:先将源代码编译为机器代码,再在CPU上运行。例如:C,C++; ​ 啊 优点:编译后运行速度快,并且运行时,不需要再进行翻译。 解释执行:运行时,解释器逐行翻译并执行例如Python。 跨平台性好, 每个代码都是在每个平台上通过相应平台的解释器运行。 速度慢,每次执行都需要动态翻译。 => Java采样编译执行和解释执行相结合的方式: 解释执行:JVM将.JAVA文件=>.class字节码。 有助于程序的跨平台性; 即时编译:将经常执行的代码编译为本地机器码,避免反复解释 523. JVM的内存区域如何划分的❗? JVM运行时的数据区分为:1. 方法区 2. 堆 3. 虚拟机栈 4. 本地方法栈 5. 程序计数器。 方法区 - 存储类&共享信息 存储类信息、常量、静态变量 这些信息属于线程共享区域 Java堆 - 与JVM共存亡 存放所有线程共享的对象实例 和 数组 (垃圾回收主要战地) 虚拟机栈 每个线程创建一个栈:用来保存局部变量、操作数栈、方法出口信息。 局部变量:基本数据类型;以及对象引用; 栈与线程共存亡 本地方法栈 为本地方法服务。。。 程序计数器 保存当前线程执行的字节码指令地址或行号。 总结:Java程序与线程在JVM内存中的流程 程序启动:JVM初始化内存区域,加载类信息到方法区。 线程创建:为线程分配程序计数器、Java虚拟机栈和本地方法栈。 方法调用:线程执行方法时,创建栈帧并压入Java虚拟机栈。 对象创建:对象实例存储在Java堆中,元数据存储在方法区。 垃圾回收:JVM清理不再使用的对象和类信息。 线程结束:线程的栈和程序计数器被销毁。 程序结束:JVM释放所有内存区域并退出。 524. JVM中堆和栈的区别是什么? 栈:主要用于存储局部变量(基本类型+对象引用)和方法的调用信息(返回地址、参数等)。线程执行时,会创建该线程的栈帧,被压入Java虚拟机栈中。 执行结束,线程栈帧被弹出(销毁) ...

March 8, 2025 · 1 min · 163 words · LongWei

并发面试题笔记

1 如何创建线程 1) 实现Runnable接口 实现run()方法 1new Thread(MyRunnable()).start(); 2)继承Thread类 重写run()方法 3)Callable接口&&FutureTask 实现Callable call()方法,使用FutureTask包装Callable对象,通过Thread启动 1FutrueTask<ReturnType> task = new FutrueTask<>(MyCallable()); 2Thread(task).start 3 4ResultType res = task.get(); // 这里阻塞 4)使用线程池 通过ExecutorService提交Runnable或者Callable任务 不同方法对比 1Runnable vs Callable 2Callable:可以返回结果,可以抛出异常 3 4线程池的优势:避免重复创建和销毁线程,减少这部分重复带来的开销; 5ThreadPool => (FixedThreadPool, CachedThreadPool, ScheduledThreadPool) 6 7虚拟线程:虚拟线程创建和切换开销更低 8Thread.startVirtualThread() ThreadPool实践 XXXThreadPool.submit(task…) 1FixedThreadPool: 固定池中线程数量 => 适合1.执行较长任务;2.控制并发度 2 3CachedThreadPool:池中线程数量不固定,根据需要动态创建线程,空的被回收,少了多创建; 4⭐适用于任务执行时间短且任务数量不确定的场景; 5 6ScheduledThreadPool:适用于需要定时执行或周期性执行任务的场景。 1// 定时任务线程池 2ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 3// For 延迟任务 4scheduledThreadPool.schedule(() -> { 5 // 任务逻辑 6}, 10, TimeUnit.SECONDS); // 延迟10秒执行 7 8// For周期执行任务 9scheduledThreadPool.scheduleAtFixedRate(() -> { 10 // 任务逻辑 11}, 0, 1, TimeUnit.SECONDS); // 每隔1秒执行一次 Thread.sleep(0) <= 主动让出CPU控制权 ...

March 5, 2025 · 11 min · 2162 words · LongWei

MySQL面试题笔记

617 MySQL 数据排序 实现? Order By 命中索引(包括索引字段),使用索引排序(⭐有序⭐),效率最高效 否则使用文件排序,文件少=> 内存排序 sort_buffer 文件大=>外部排序,归并排序 内部排序细节: 双路排序(待排序的列数据太大了):使用row_id(回查表) + sort_field ​ 排好序后,使用row_id将完整的记录取出来 单路排序(待排序数据大小能接受) ​ 直接拍,不会查表,直接把拍好的结果返回 外部排序: 拆分小的,外部多路归并排序,小=>大 外部归并排序 => 先分段排序,每一段调入内存执行快排 ​ => 归并阶段,因为每子段都是有序的 => 多路归并排序 589 一条SQL的执行过程 先通过连接器校验权限 利用分析器进行SQL语句词法分析和语法分析,构建解析树 利用优化器选择合适的索引和表连接顺序,最终选择一条最佳的执行计划 利用执行器,调用引擎层查询数据,返回结果集 具体:Select * from user where id = 1; SQL => Server层连接器,权限校验,账号是否有资格获取。无=> Access denied for user。 连接成功后,空闲一段时间会断开 分析器(查询解析) => 语法分析:SQL : Select类型✔️ user表✔️ id列 ✔️拆分成词,再组装为解析树。 语义分析:语法是否有误 => you have an error in your SQL syntax (字段、表|存在?) 分析解析树语法正确性 优化器(查询优化)=> ...

March 4, 2025 · 7 min · 1346 words · LongWei

Redis面试题笔记

637 常见的数据类型 String Set Hash List Zset (Sorted Set) BitMap => 位图,考勤,或者xxx分配情况 HyperLogLog => 用户访问的独立用户数量 GEO => 地理 应用场景: 缓存 实时系统 消息队列 分布式锁 计数器:页面访问量、点赞数、评论数 651 Redis 主从复制的实现原理 为什么需要主从复制 **提供故障恢复:**数据冗余&故障恢复,某个节点宕机了,但是其他节点还活着; **负载均衡:**提供负载均衡,配合读写分离策略,主节点写操作,从节点提供读操作; ⭐ **高可用:**主从复制是Redis的高可用的基础,也是哨兵和集群实施的基础。 两种同步复制方式: 全量 & 增量 全量复制:分三阶段 主节点发送SYNC命令与从节点进行连接,开始同步, 主从之间建立联系; 主节点将当前数据生成RDB文件,发送给从节点; 发送的期间,主节点将期间的所有写命令缓存到Replication buffer。最后发送Replication buffer的写命令给从节点。 增量复制 短暂失联后同步数据 主要有三个步骤: 从服务器在恢复网络后,会发送 psync 命令给主服务器,此时的 psync 命令里的 offset 参数不是 -1; 主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据; 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。 那么关键的问题来了,主服务器怎么知道要将哪些增量数据发送给从服务器呢? 答案藏在这两个东西里: repl_backlog_buffer,是一个「环形」缓冲区,用于主从服务器断连后,从中找到差异的数据; (环形堆积缓冲区) replication offset,标记上面那个缓冲区的同步进度,主从服务器都有各自的偏移量,主服务器使用master_repl_offset 来记录自己,「写」到的位置,从服务器使用 slave_repl_offset 来记录自己「读」到的位置。 ·repl_backlog_buffer,是一个「环形」缓冲区,用于主从服务器断连后,从中找到差异的数据; 保持连接与断线重连 Redis中,主节点会和从节点保持长连接,以确保数据的持续同步; ...

March 1, 2025 · 8 min · 1645 words · LongWei

Spring笔记

1. 什么是循环依赖 两个及以上的类之间相互依赖。模块A依赖于模块B,模块B依赖于模块A,导致依赖链的循环,无法确定加载/初始化顺序。 => 多个Bean循环引用导致Spring容器无法正常初始化它们。 延迟某些Bean的初始化时间,使用@Lazy进行懒加载,只有当实际使用了该对象才创建。 2. Spring如何解决循环依赖 提前暴露未完全创建的Bean 三级缓存解决: 一级缓存(Single Objects Map):用于初始化单例Bean; (成品) 二级缓存(Early Singleton Objects Map): 用于存储尚未完全初始化,但实例化的Bean,用于提取暴露对象,避免循环依赖问题;(半成品,成员变量未初始化) 三级缓存(Singleton Factories Map): 用于存储对象工厂,可以通过工厂创建早期Bean **解决步骤:**例如AB两个相互依赖,三级缓存策略 创建A,查询一级缓存看看有没有完全体B,没有则看看二级缓存有没有半成品B,都没有则创建A的Bean,调用ceateBean方法(实例化,属性注入,初始化); 之后,A往三级缓存加入一个A的getObject方法 到了属性注入,因为A依赖B,那么需要创建B。同样的路线,B查询到二级缓存都没发现A,调用createBean创建B实例。到了B的属性注入,发现三级缓存有A工厂,调用getObject创建半成品A,放到二级缓存中,完成B的第二步属性注入。后面initializeBean完成B的创建,并放到一级缓存中。 回到A,A调用一级缓存的B完成注入。 未解决的问题: 而如果说 A 是构造器注入,B 是 set 注入。则说明 A 需要 B 的时刻提前了,在实例化 new A(B b)的时候就需要 B。此时 A 没有往三级缓存放getObject,因此到了创建依赖 B的时候,无法获取 A的 getObject 工厂方法,只能继续 new A,造成循环依赖的死循环。 4. Spring重要的模块组成 Core Container 核心容器: Spring Core:提供依赖注入DI和控制反转IOC的实现。 Spring Beans:负责管理Bean的定义和生命周期,通过IOC完成Bean的创建、依赖注入、初始化、销毁等操作; Spring Context:基于Core和Beans的高级容器,提供类似JNDI的上下文功能,包含国际化、事件传播、资源访问等功能; Spring Expression Language:用于运行时查询和操作对象的值。 AOP面向切面编程 Spring AOP:提供面向切面编程的功能,可以在方法执行前后或抛出异常时动态插入额外的业务逻辑。 Data Access 数据访问 ...

March 1, 2025 · 3 min · 516 words · LongWei

Java面试题笔记

415 序列化&反序列化 将Object转为字节流,或反之 普通:实现serializable接口 ˈsɪərɪəlaɪzəbl 使用Jackon,Obj => json格式 416 Java中的不可变类 final 修饰,例如String类 🎉优点: 线程安全 缓存友好 缺点 性能问题,因为不能修改,所有每次状态变化,都需要生成新的对象。 411 多态特性 继承 方法重载,函数名相同,但是函数签名需要有差异(参数类型&数量) 重写,子类重写父类方法,通过父类调用方法时,调用的是子类重写后的函数。 412 Java参数传值是副本还是引用呢? 基本类型是传值副本,int… 引用数据类型是传引用副本。 including:obj,array 425 Java中 包装类型和基本类型 🏷️基本类型 => int long float double … 位于栈上(局部变量的话) ,性能好,但不支持为null (局部变量在栈上,成员变量在堆上,静态字段在方法区) 🏷️包装类型 => 每一个基本类型都对应一个包装类型。包装类型是类,在堆中,支持null JVM内存模型 ❗❗❗内存堆和数据结构堆不是同一个东西(不是堆的结构) 1+--------------------------------------------+ 2| 方法区(Method Area) | ← 线程共享 3| - 类元数据(Class 信息) | 4| - 静态变量(static 变量) | 5| - 常量池(字符串常量等) | 6+--------------------------------------------+ 7 ↑ ↑ 8 | | 9+----------------+ +-----------------+ 10| 栈(Stack) | | 堆(Heap) | ← 线程共享 11| - 局部变量 | | - Java 对象实例 | 12| - 方法调用栈 | | - 数组 | 13+----------------+ +-----------------+ 413 interface & abstract class interface(自上而下) 知晓某一种行为,基于这些行为约束定义的接口, 一些类需要有这些行为的话,需要实现这些接口 abstract class(自下而上): 有许多类,它们有共同点,很多代码可以复用,因此将公共逻辑封装为抽象对象。 100 hashCode & equals & == hashCode用于散列表(hashMap)用于计算hash值,从而计算存储位置; ...

February 19, 2025 · 7 min · 1389 words · LongWei