2024.04.20华为技术一面+技术二面
2024.04.20华为技术一面+技术二面
华为技术一面
主要内容:
Java的⼀些基础知识
项⽬问题
⼿撕代码
Java基础知识
- 哪些存储容器是线程安全的?
同步容器类:使⽤了synchronized
- Vector
- HashTable
并发容器类:
- ConcurrentHashMap:分段
- CopyOnWriteArrayList:写时复制
- CopyOnWriteArraySet:写时复制
Queue
- ConcurrentLinkedQueue:使⽤⾮阻塞式的⽅式实现的基于链接节点的⽆界的线程安全队列,性
- 能⾮常好。(java.util.concurrent.BlockingQueue 接⼝代表了线程安全的队列)
- ArrayBlockingQueue:基于数组的有界阻塞队列
- LinkedBlockingQueue:基于链表的有界阻塞队列
- PriorityBolckingQueue:表⽰优先级的⽆界阻塞队列,即该阻塞队列中的元素可⾃动排序。默认
- 情况下,元素采⽤⾃然升序
- DelayQueue:⼀种延时获取元素的⽆界阻塞队列
- SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待⼀个take操作,否则不能继
- 续添加元素;内部起始没有任何⼀个元素,容量是0
Deque接⼝定义了双向队列,双向队列允许在队列头和尾部继续⼊队出队操作
- ArrayDeque:基于数组的双向⾮阻塞队列
- LinkedBlockingDeque:基于链表的双向阻塞队列
- Sorted容器
- dakkk(仅供参考)ConcurrentSkipListMap:是TreeMap的线程安全版本
- ConcurrentSkipListSet:是TreeSet的线程安全版本
- StringBuffer 和 StringBuilder 有线程安全的吗 (没听清,以为是多线程的东西,能回答上来的)
- String 中的对象是不可变的,也就可以理解为常量,线程安全。
- StringBuffer 对⽅法加了同步锁或者对调⽤的⽅法加了同步锁,所以是线程安全的。
- StringBuilder 并没有对⽅法进⾏加同步锁,所以是⾮线程安全的。
- 项⽬中使⽤多线程的场景 (目前写过的项目没有使用过多线程!!!)
- 多线程是⼀种允许多个任务同时执⾏的技术。它通过将任务分解为更⼩的部分并在不同的线程上
- 运⾏这些部分来实现。
- 这可以提⾼程序的性能,尤其是当任务是CPU密集型或涉及⼤量IO时
- 在⼯作中,有多种场景可以使⽤多线程,⼀下是⼀些最常⻅的例⼦:
- 后台任务:后台任务是通常在⽤⼾不知不觉中运⾏的任务,例如:⽂件上传或数据处理,使⽤
- 多线程可以将这些任务与主应⽤程序分开,这样他们就不会阻塞主应⽤程序并导致其⽆响应
- ⽹络请求:当⽤⼾从⽹站或应⽤程序请求数据时,会发出⽹络请求,使⽤多线程可以同时处理
- 多个⽹络请求,从⽽提⾼响应速度
- 多线程使⽤过程中,需要注意的点是什么? (不熟)
- 死锁
- 线程安全
- 并发
- 多线程中的各项资源这么处理呢? (不熟)
- 同步关键字:synchronized关键字是 Java 中最常⽤的同步机制,它通过 ⼀次只有⼀个线程执⾏代
- 码块或⽅法来保护共享资源
- 锁:java 提供了显⽰锁机制,例如:ReentrantLock 和 ReadWriteLock,这些锁⽐ synchronized
- 关键字更灵活,但也更复杂
- 原⼦操作:Java 提供了原⼦操作,例如 AtomicInteger 和 AtomicLong,原⼦操作是不可分割的操
- 作,可以确保共享资源的更新不会出现数据竞争
- 线程安全类:Java 提供了许多线程安全类,例如 Vector 和 HashMap,这些类内部使⽤了同步机
- 制来保护共享资源,因此⽆需显⽰同步
- Synchronized中 类锁 和 对象锁的区别? (以前学习过,忘记了)
dakkk(仅供参考)类锁和对象锁都是 Java 中的同步机制,⽤于控制对共享资源的访问,但是,他们之间还存在⼀定
的区别
获取⽅式
类锁是通过 synchronized 关键字 修饰静态⽅法或代码块来获取的
对象锁是通过 synchronized 关键字修饰实⼒⽅法或代码块来获取的,也可以通过显⽰锁(例如
ReentrantLock)来获取
作⽤范围
类锁是整个类,意味着同⼀时刻只能有⼀个线程持有类锁
对象锁的作⽤范围是单个对象,意味着同⼀时刻可以有多线线程持有不同的对象锁
同步效果
类锁⽤于同步对静态⽅法和静态变量的访问。静态⽅法和静态变量是和类关联的,⽽不是与特
定对象关联的。因此,类锁可以确保同⼀时刻只能有⼀个线程执⾏静态⽅法或访问静态变量
对象锁⽤于同步对实例⽅法和实例变量的访问。实例⽅法和实例变量是与特定对象关联的,因
此,对象锁可以确保同⼀时刻只能有⼀个线程执⾏同⼀个对象的实例⽅法或访问同⼀个对象的实例变量
- maven⼯程和java⼯程的区别? (用自己的语言说出来了)
项⽬结构
maven
- 具有标准化的项⽬结构,所有项⽬⽂件都位于特定的⽬录中,这使得maven⼯程易于理解和维护
- 使⽤基于 XML 的配置⽂件(pom.xml)来定义项⽬的构建过程。可以⾃动下载依赖项,编译代码,打包应⽤程序等
- 具有强⼤的依赖管理功能,可以⾃动下载、安装和管理项⽬所需的依赖项
java
- 可以任意定义,这可能会导致难以理解和维护
- 通常需要使⽤其他的构建根据,例如 Ant 或 Gradle 来定义构建过程,这些根据可能⽐maven 更复杂
- ⼿动管理依赖,可能会导致依赖项冲突和其他问题
可移植性
maven⼯程可移植,可以在任何具有 maven安装的环境下运⾏
java⼯程的可移植性可能取决于所使⽤的构建⼯具和其他依赖项
- 类的加载机制清楚吗? (背过,说不清楚!!!)
dakkk(仅供参考)类从加载到虚拟机中开始,直到卸载为⽌,它的整个⽣命周期包括了:加载、验证、准备、解
析、初始化、使⽤和卸载这7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)
- 1.加载:查找和导⼊class⽂件
- 2.验证:保证加载类的准确性
- 3.准备:为类变量分配内存并设置类变量初始值
- 4.解析:把类中的符号引⽤转换为直接引⽤
- 5.初始化:对类的静态变量,静态代码块执⾏初始化操作
- 6.使⽤:JVM 开始从⼊⼝(main)⽅法开始执⾏⽤⼾的程序代码
- 7.卸载:当⽤⼾程序代码执⾏完毕后,JVM 便开始销毁创建的 Class 对象,最后负责运⾏的 JVM 也退出内存
9.什么是双亲委派模型?及其好处 (背过,说不清楚!!!)
如果⼀个类加载器收到了类加载的请求,它⾸先不会⾃⼰尝试加载这个类,⽽是把这请求委
派给⽗类加载器去完成,每⼀个层次的类加载器都是如此
因此所有的加载请求最终都应该委派到顶层的启动类加载器中,只有当⽗类加载器返回⾃⼰⽆法
完成这个加载请求(它的搜索返回中没有找到所需的类)时,⼦类加载器才会尝试⾃⼰去加载
好处:
第⼀、通过双亲委派机制可以避免某⼀个类被重复加载,当⽗类已经加载后则⽆需重复
加载,保证唯⼀性。
第⼆、为了安全,保证类库API不会被修改
10.什么是反射,其优缺点?应⽤场景 (看过,说不清楚!!!)
通过反射你可以获取任意⼀个类的所有属性和⽅法,你还可以调⽤这些⽅法和属性。
优点:反射可以让我们的代码更加灵活、为各种框架提供开箱即⽤的功能提供了便利;
缺点:
性能开销:反射操作⽐直接访问类、字段和⽅法的性能要低。这是因为反射需要在运⾏时进⾏
额外的解析和检查
安全隐患:反射允许代码执⾏⼀些在正常情况下不允许的操作。例如访问私有成员、修改类定
义等。这可能会导致安全漏洞,例如未经授权地访问或修改数据
代码可读性:会使代码更加复杂和难以理解,因为它增加了额外的抽象层
编译时检查缺失:由于反射是在运⾏时进⾏的,编译器⽆法检查许多反射相关的错误,例如类
名拼写错误、⽅法名错误等,只能在运⾏时发现这些错误,有可能会导致程序崩溃
- 像 Spring/Spring Boot、MyBatis 等等框架中都⼤量使⽤了反射机制
dakkk(仅供参考)
且这些框架使⽤了⼤量的AOP(动态代理),AOP的实现也依赖反射
提⾼代码的重⽤性,允许程序以通⽤的⽅式处理不同的类、字段和⽅法。例如,假设⼀个程序需
要验证多个对象的属性值是否为空,使⽤反射,程序可以编写⼀个通⽤⽅法来验证任意对象的任意属性
- 开发中⽤到了那些设计模式? (不熟)
设计模式(design pattern)是解决软件设计中常⻅问题的通⽤解决⽅案,它为开发⼈员提供了⼀套结果验证的⽅案,⽤于解决常⻅的软件设计问题,提⾼代码的可复⽤性、可维护性和可拓展性
根据功能和⽤途、设计模式可以分为以下三⼤类:
- 创建型模式(creational Patterns):⽤于常⻅对象的模式
- 结构性模式(Structural Patterns):⽤于描述如何将类或对象组合成更⼤的结构的模式
- ⾏为型模式(Behavioral Patterns):⽤于描述对象之间如何通信和交互的模式
开发中常⽤的设计模式有:
- 创建型模式
- 单例模式(Singleton Pattern):确保⼀个类只有⼀个实例,并提供⼀个全局访问点
- ⼯⼚⽅法模式(Factory Method Pattern):定义⼀个创建对象的接⼝,让⼦类决定实例化哪个类
- 抽象⼯⼚模式(Abstract Factory Pattern):提供⼀个创建多个相关或依赖对象的接⼝
- 建造者模式(BUilder Pattern):将⼀个复杂对象的创建分为多个步骤,并⽤不同的对象封装这些步骤
- 结构型模式
- 适配器模式(Adapter Pattern):将⼀个类的接⼝转换为另⼀个类所需的接⼝
- 桥接模式(Bridge Pattern):将⼀个对象的接⼝和实现解耦,使得⼆者可以独⽴变化
- 组合模式(Composite Pattern):将多个对象组合成树形结构,并向客⼾提供统⼀的接⼝
- 装饰者模式(Decorator Pattern):为⼀个对象添加新的功能,保持原有功能不变。
12.JVM的垃圾回收算法知道吗?
标签-清除算法:最早使⽤的垃圾回收⽅法之⼀,该⽅法⾸先标记所有可达的对象,然后回收所有
未标记的对象,效率⽐较低,需要扫描整个堆空间,内存碎⽚⽐较⼤
复制算法:将所有可达的对象复制到⼀个新的内存区域,然后回收旧的内存区域。效率⽐较⾼,但需要⼤量的内存空间
标签-整理算法:结合标签-清除算法和压缩法的有点,先标记所有可达的对象,然后将这些对象
移动到堆空间的⼀端,并回收剩余的内存空间。⽬前最常⽤的垃圾回收⽅法之⼀。
dakkk(仅供参考)分代收集算法:将堆空间划分为年轻代和⽼年代,年轻代包含新创建的对象,⽼年代包含存活时
间较⻓的⼤型对象。年轻代的回收频率⽐极⾼(java8使⽤复制算法),⽼年代的垃圾回收频率⽐较低,可以提⾼垃圾回收的效率
- ⼀些算法的原理
动态规划算法(Dynamic Programming Algorithm):⼀种通过将问题分解为⼦问题,并重复利
⽤⼦问题的解决来解决问题的算法。动态规划算法常⽤于解决具有重叠⼦问题的问题。
贪⼼算法(Greedy Algorithm):⼀种通过在每⼀步做出局部最优选择的算法,通常⽤于解决 NP难问题
并查集:⼀种⽤于处理集合的数据结构,并查集⽀持两种操作,查找和合并,通常⽤于解决连通性问题
快排基本原理:通过分治法将⼀个数组划分成两个数组,然后递归地对⼦数组进⾏排序
- 优化mysql的⽅法
使⽤分布式数据库
使⽤负载均衡
使⽤监控⼯具
- Redis缓存击穿、穿透、雪崩 (击穿和穿透区分了好久)
缓存穿透是指 缓存和数据库中都没有的数据 ,⽽⽤⼾不断发起请求。
接⼝层增加校验,如⽤⼾鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对 写为 key-null,缓存有
效时间可以设置短点,如30s(设置太⻓会导致正常情况也没法使⽤)。这样可以防⽌攻击⽤⼾反
复⽤同⼀个id暴⼒攻击;
- 布隆过滤器。bloomfilter就类似于⼀个HashSet,⽤于快速判断某个元素是否存在于集合中,其
典型的应⽤场景就是快速判断⼀个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键
就在于hash算法和容器⼤⼩
缓存击穿是指 缓存中没有但数据库中有的数据 (⼀般是缓存时间到期),这时由于并发⽤⼾特别多,
同时读缓存没有读到数据,⼜同时去数据库读取数据,引起数据库压⼒瞬间增⼤,造成过⼤压⼒。
热点数据⽀持续期,持续访问的数据可以不断续期,避免因为过期失效⽽被击穿
(互斥锁)发现缓存失效,重建缓存加互斥锁,当线程查询缓存发现缓存不存在就会尝试加锁,
线程争抢锁,拿到锁的线程就会查询数据库,然后重建缓存,争抢锁失败的线程,可以加⼀个睡
眠然后循环重试。
- (逻辑过期)把过期时间设置在value中,查询缓存中的数据时,如果发现逻辑时间已过期,尝试
获取锁,开启⼀个异步的⽅法进⾏缓存的更新操作,先返回过期的数据。
dakkk(仅供参考)缓存雪崩,是指⼤量的应⽤请求因为异常⽆法在Redis缓存中进⾏处理,像雪崩⼀样,直接打到数据
库。
缓存中 数据大批量过期,而查询数据量巨大,引起数据库压力过大甚至宕机
缓存数据的过期时间设置随机,分⽀同⼀时间⼤量数据过期现象发⽣
重建缓存加互斥锁,当线程拿到缓存发现缓存不存在就会尝试加锁,线程争抢锁,拿到锁的线程
就会进⾏查询数据库,然后重建缓存,争抢锁失败的线程,可以加⼀个睡眠然后循环重试
- Vue常⻅的钩⼦函数,以及作⽤?`(就说了onMounted和onUnmounted)``
onMounted() 注册⼀个回调函数,在组件挂载完成后执⾏。
onUpdated() 注册⼀个回调函数,在组件因为响应式状态变更⽽更新其 DOM 树之后调⽤。
onUnmounted() 注册⼀个回调函数,在组件实例被卸载之后调⽤。
onBeforeMount() 注册⼀个钩⼦,在组件被挂载之前被调⽤。
onBeforeUpdate() 注册⼀个钩⼦,在组件即将因为响应式状态变更⽽更新其 DOM 树之前调
⽤。
onBeforeUnmount() 注册⼀个钩⼦,在组件实例被卸载之前调⽤。
项⽬拷打(伙伴匹配和API项⽬)
伙伴匹配项⽬的规模有多⼤,有⼏个⼈开发,都分别是什么⻆⾊
按照开发的流程,说⾃⼰之前的公司⽐较⼩,就只有 前端、后端、运维这些⼈,⼀共⼗⼏个⼈
这两个项⽬你做了那些优化呢?遇到的难题有吗?
这⾥乱答的,背了⼀些相关的⾯试题
难题因为没做这两个项⽬,就没怎么回答上来
优化就说了这些
伙伴匹配
使⽤knife4j+swagger,优化接⼝调试
使⽤Stream API + Lambda ,简化集合处理
⾃主编写Dockerfile,实现⾃动化镜像构建及容器部署
API项⽬
使⽤Spring Cloud Gateway 作为API⽹关
使⽤Spring Boot Starter 开发客⼾端SDK
balabala
dakkk(仅供参考)⼿撕算法
因为说了⾃⼰只对数据结构相关的算法⽐较熟悉,就出了⼀个⼆叉树的题⽬
\104. ⼆叉树的最⼤深度 - ⼒扣(LeetCode)
搞清楚什么是深度和⾼度
深度:任意⼀个节点到root节点的距离
⾼度:任意⼀个节点到叶⼦节点的距离
后序遍历(左右中):适⽤于求树的⾼度,因为通过左右节点,可以将结果返回给当前的⽗节点,实
现了从底部往上的⼀个计数过程
前序遍历(中左右):适⽤于求树的深度,往下遍历⼀次,就深度+1,从⽽不断遍历,向下探索
明⾯上来说是求最⼤深度,本质上是求根节点的⾼度,所以我们使⽤后序遍历
递归三部曲:
返回值int,⼊参节点
终⽌条件,⼊参的节点为null
遍历顺序,左右中,左右为递归,中为取左右中的最⼤⾼度+1
class Solution {
public int maxDepth(TreeNode root) {
if(root = null) return 0;
int leftHeight = maxDepth(root.left);
int rightHeight = maxDepth(root.right);
return Math.max(leftHeight,rightHeight) + 1;
}
}
华为技术二面
主要内容:
- ⾃我介绍
- 介绍⼯作项⽬(简历上的项⽬)
- 常规⼋股
- ⼿撕算法
⾃我介绍
之前参考(隔壁)星球⼤佬 我就是贺生啊 的⽂章)写过⼀个模版,主要有以下内容
个⼈介绍(学历、性格、爱好、突出喜欢专研编程相关的技术)
个⼈成⻓(校招说学习经历,社招说⼯作经历,还是要突出编程相关的经历)
补充社招的内容:通过⾃⼰的实际项⽬来说成⻓!!
1 介绍⼯作项⽬(主要)
社招的兄弟,脸⽪厚点,把⾃⼰写的项⽬当作公司的项⽬来说就好了
主要问的内容如下,问的⽐较多就不说了:
先说⼀遍项⽬的的流程(⾃⼰做的流程肯定知道吧,不过专注于后端/前端,别前后端都说)
再说项⽬中遇到的问题,以及如何解决的(不细说了)
最后说项⽬⾃⼰做了那些优化(不细说了)
个⼈思考/建议:
做项⽬的时候,思考⼀下别⼈为什么⽤这个东西,有什么好处,可以⽤其他⾃⼰知道的吗?
遇到bug的时候,记录下来,回顾⼀下
也是在做项⽬的时候,记录⾃⼰感觉可以优化的点,然后在做项⽬的时候进⾏优化,或者做完项
⽬的时候再优化
关键点: 上述的过程都要做⽂档沉淀!!!以后问到了就会回答了
dakkk(仅供参考)常规⼋股(这次问的少,不过⽐较深⼊)
- 存储容器是线程安全的? (上次问过了,这次准确答出)
同步容器类:使⽤了synchronized
Vector
HashTable
并发容器类:
ConcurrentHashMap:分段
CopyOnWriteArrayList:写时复制
CopyOnWriteArraySet:写时复制
Queue
ConcurrentLinkedQueue:使⽤⾮阻塞式的⽅式实现的基于链接节点的⽆界的线程安全队列,性
能⾮常好。(java.util.concurrent.BlockingQueue 接⼝代表了线程安全的队列)
ArrayBlockingQueue:基于数组的有界阻塞队列
LinkedBlockingQueue:基于链表的有界阻塞队列
PriorityBolckingQueue:表⽰优先级的⽆界阻塞队列,即该阻塞队列中的元素可⾃动排序。默认
情况下,元素采⽤⾃然升序
DelayQueue:⼀种延时获取元素的⽆界阻塞队列
SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待⼀个take操作,否则不能继
续添加元素;内部起始没有任何⼀个元素,容量是0
Deque接⼝定义了双向队列,双向队列允许在队列头和尾部继续⼊队出队操作
ArrayDeque:基于数组的双向⾮阻塞队列
LinkedBlockingDeque:基于链表的双向阻塞队列
Sorted容器
ConcurrentSkipListMap:是TreeMap的线程安全版本
ConcurrentSkipListSet:是TreeSet的线程安全版本
- Springboot你了解吗?经常⽤哪些注解呢? 按照自己的理解说了
先回答了什么是Springboot
dakkk(仅供参考)Spring Boot 已经建⽴在现有 spring 框架之上。使⽤ spring 启动,我们避免了之前我们必须做
的所有样板代码和配置。因此,Spring Boot 可以 帮助我们以最少的⼯作量,更加健壮地使⽤
现有的 Spring 功能。
⼜回答了Springboot的优点
减少开发,测试时间和努⼒。
使⽤ JavaConfig 有助于避免使⽤ XML。
避免⼤量的 Maven 导⼊和各种版本冲突。
通过提供默认值快速开始开发。
没有单独的 Web 服务器需要。这意味着你不再需要启动 Tomcat,Glassfish 或其他任何东
西。
需要更少的配置 因为没有 web.xml ⽂件
最后回答了⼀些常⽤注解
⼤家都⽤过的,就不多说了!
- Mybatis的⼀⼆级缓存知道吗? 按照自己的理解说了
⼀ 级 缓 存 : 基 于 PerpetualCache的 HashMap 本 地 缓 存 , 其 存 储 作 ⽤ 域 为 SqlSession, 各 个
SqlSession之间的缓存相互隔离,当Session flush或close之后,该SqlSession中的所有Cache就将
清空,MyBatis默认打开⼀级缓存
⼆级缓存与⼀级缓存其机制相同,默认也是采⽤PerpetualCache,HashMap存储,不同之处在于其
存储作⽤域为Mapper(Namespace),可以在多个SqlSession之间共享,并且可⾃定义存储源,如
Ehcache。默认不打开⼆级缓存,要开启⼆级缓存,使⽤⼆级缓存属性类需要实现Serializable序
列化接⼝(可⽤来保存对象的状态),可在它的映射⽂件中配置
当开启⼆级缓存后,数据的查询执⾏的流程就是⼆级缓存->⼀级缓存->数据库。
缓存更新机制:当某⼀个作⽤域(⼀级缓存Session/⼆级缓存Mapper)进⾏了C/U/D操作(创建、更
新、删除)后,默认该作⽤域下所有select中的缓存将被clear。
- Mybatis你是如何理解的呢?`按照⾃⼰的理解说了
先说了什么是Mybatis
MyBatis是⼀个ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本
⾝,不需要花费精⼒去处理加载驱动,创建连接,创建statement:等复杂的过程。开发⼈员不
需要编写原⽣态sql,可以严格控制Sq执⾏性能,灵活度⾼。
MyBatis可以使⽤ml或者注解来配置映射原⽣信息,将PO]O映射成数据库中的记录,避免了⼏
乎所有的]DBC代码和⼿动设置的参数以及获取结果集。
⼜说了Mybatis 的优点
dakkk(仅供参考)基于SQL语句编程,⽐较灵活,不会对应⽤程序或数据库现有设计造成影响,SQL写在XML
⾥,解除sql语句和业务代码的耦合,便于统⼀管理,⽽且语句在XML⾥,可以复⽤
与传统JDBC相⽐,减少了很多代码量,消除了⼤量代码冗余,不需要⼿动开关SqlSession的连
接
使⽤JDBC驱动连接数据库,所以JDBC⽀持的数据库,Mybatis都⽀持
与Spring完美集成
提供映射标签,⽀持对象与数据库ORM字段关系映射;对象关系也可以映射
Mybatis的缺点
SQL语句依赖,需要开发⼈员具备⼀定的SQL知识,另外,如果数据库模式发⽣变化,还需要
⼿动修改SQL语句
XML配置⽂件冗⻓,会导致⼀些维护问题,如果还使⽤注解配置,代码可能会混乱
缺乏⾃动化创建,相⽐其他ORM框架,Mybatis不⽀持⾃动创建表和字段
最后⼜聊了聊Mybatis Plus
主要是什么是MP
MP有什么优点和缺点
具体的应⽤场景就不多说了,懂得,哈哈哈哈
- Redis中的list和map数据类型了解吗?说说应⽤场景 按照自己的理解说了
因为伙伴匹配项⽬⽤redis作为旁路缓存了,主要⽤的是String类型的数据,就问了问其他数据结构有
⽤过吗?
我说没⽤过,不过了解他的底层结构,也知道应⽤的场景
List
底层结构:
3.2版本之前,List对象有两种编码⽅式,⼀种是ZIPLIST,另⼀种是LINKEDLIST
列表对象保存的所有字符串对象 长度都小于64字节 或者 列表对象元素 个数少于512个 ,则
使⽤ZIPLIST,否则使⽤LINKEDLIST
ZIPLIST(压缩列表)没有细说了
3.2版本就引⼊了QUICKLIST。QUICKLIST其实就是ZIPLIST和LINKEDLIST的结合体
当数据较少的时候,QUICKLIST的节点就只有⼀个,此时其实相当于就是⼀个ZIPLIST
当数据很多的时候,则同时利⽤了ZIPLIST和LINKEDLIST的优势
使⽤场景
dakkk(仅供参考)朋友圈点赞:发朋友圈的⼈⽤key表⽰,点赞的⼈为value,点赞操作对应rpush,取消点赞操作可
以对应lrem。评论信息可以通过list去查询关系型数据库。
消息队列:list类型的lpop和rpush(或者反过来,lpush和rpop)能实现队列的功能,故⽽可
以⽤Redis的list类型实现简单的点对点的消息队列。不推荐在实战中这么使⽤,因为现在已经
有Kafka、NSQ、RabbitMQ等成熟的消息队列了,它们的功能已经很完善了。
排⾏榜:list类型的lrange命令可以分⻚查看队列中的数据。可每隔⼀段时间计算⼀次的排⾏榜
存储在list类型中的数据;
⼀般要求顺序的业务中,都⽤list来实现;
Map/Hash
底层结构
压缩列表:
Hash对象保存的所有值和键的⻓度都⼩于64字节;
Hash对象元素个数少于512个。
上述两个条件任何⼀条都不满⾜,编码结构就⽤HASHTABLE
HASHTABLE没有细说了,底层结构(字段),渐进式扩容,缩容没提了
应⽤场景:
购物⻋:⽤⼾的id为key,商品id为field,商品数量为value
存储对象:
因为有些使⽤使⽤String(key-value=json)的时候,对象的某个属性频繁修改,每次修改
都需要将整个对象重新JSON序列化,不够灵活;
但是我们使⽤HASH,将经常发⽣变化的属性,存放在value⾥,json对象放在field中,就可
以灵活的修改属性了,如:商品的价格、销量、关注数、评价数
3 ⼿撕算法
- 最⻓回⽂⼦串 - ⼒扣(LeetCode)
这次是直接给了个leetcode原题的链接了
分析了⼀下这个代码的实现思路
还有其他⽅式解决这个问题吗? 没答出来
直接贴代码
class Solution {
public String longestPalindrome(String s) {
/ 思路:中心拓展法
/ > 中心向两边拓展,分奇数和偶数的情况,只要相同就继续拓展
/ > 直到不同为止
for(int i=0;i<s.length();i + ){
/ max记录最长回文串的长度,start记录最长回文串的起始位置
int max = 0,start = 0;
/ 分奇数和偶数的情况
for(int j;j<2;j + ){
/ 左指针
int left = i;
/ 右指针
int right = i+j;
/ 循环从中心向两边拓展
while(left = 0 & right<s.length() &
s.charAt(left) = s.charAt(right)){
left - ;
right + ;
}
/ 更新最长回文串的长度和起始位置
if(right-left-1>max){
/ 长度
max = right - left - 1;
/ 起始位置
start = left + 1;
}
}
}
return s.substring(start,start+max);
}
}