当前位置: 首页 > 后端技术 > Java

在线面试官-字节跳动

时间:2023-04-01 14:33:36 Java

朋友们,大家新年快乐,今天给大家分享一下字节跳动抖音电商面签经验,希望对大家有所帮助~面试官:你好,我是xxx,面试官字节跳动的,这是大斌?大斌:面试官你好,我是大斌面试官:现在方便面试吗?大斌:对对对。采访者:现在开始采访吧。面试官:我看你简历上对收藏很熟悉。你了解哈希表吗?说说HashMap的put方法?独白:果然是HashMap……大斌:HashMap实现了Map接口,用来存储键值对映射。其底层采用数组+链表+红黑树实现(JDK1.8增加了红黑树的部分)。大斌:它的put方法的过程是这样的:如果表没有初始化,就先初始化。使用哈希算法计算key的索引,判断索引处是否有元素。如果索引处没有元素,则直接插入。一种情况,一种是链表的形式,直接遍历到插入的末尾,另一种是红黑树的形式,根据红色插入链表的数-black树结构大于阈值8,将转为红黑树结构。添加成功后会勾选是否需要扩容?面试官:嗯,刚才你提到了HashMap的扩展,请详细说说?独白:emm,给自己挖坑了。。。大斌:以JDK1.8为例,在向HashMap添加元素时,如果元素个数大于阈值,则扩容,一个数组将使用两倍容量代替原来的阵列。大斌:由于数组的容量扩大了2的次方,所以当一个Entity扩大时,新的位置要么在原来的位置,要么在原来长度+原来位置的位置。大斌:原因是数组的长度增加了一倍,也就是说在二进制中,多了一个高位参与数组下标的计算。大斌:也就是说,在元素复制的过程中,不需要重新计算元素在数组中的位置。只需要查看原哈希值新增的位是1还是0,如果是0,则索引没有变化,如果是1,则索引变为“原索引+oldCap”(基于e.散列&(oldCap-1)==0)。Dabin:这样可以节省重新计算hash值的时间,而且由于新加入的1bit是0或者1,可以认为是随机的,所以resize过程会将之前冲突的节点平均分散到新的bucket中。面试官:小伙子,基础还不错。看你简历上写着精通MySQL,那我们来说说MySQL的索引结构?独白:妈的,以后再也不敢写熟练了……还好昨天背了大厂面试手册,现在一点都不慌了。需要的话可以到公众号【程序员大斌】后台回复【手册】从大厂大斌获取面试资料:MySQL数据库中使用最多的索引类型是BTREE索引,底层是基于BTREE索引实现的B+树数据结构。大斌:B+树是基于B树和叶子节点顺序访问指针实现的。具有B树的平衡性,通过顺序访问指针提高区间查询的性能。大斌:在进行查找操作时,先对根节点进行二分查找,找到key所在的指针,然后在该指针指向的节点上递归查找。直到找到叶子节点,再对叶子节点进行二分查找,找出key对应的数据项。面试官:为什么索引用B+树而不是二叉树来实现?大斌:B+树有一个特点,就是够短够肥,可以有效减少节点访问次数,提高性能。大斌:虽然二叉树也有很好的搜索性能log2N,但是当N比较大的时候,树的深度也比较高。数据查询时间主要取决于磁盘IO次数。二叉树的深度越大,搜索次数越多,性能越差。在最坏的情况下,它会退化为链表。因此,B+树更适合作为MySQL的索引结构。面试官:那为什么不用B-tree呢?独白:现在面试太复杂了。这是造火箭……大斌:因为B树的分支节点存放数据,所以我们需要进行中序遍历,对数据进行扫描,才能找到具体的数据。.并且由于B+树的数据存储在叶子节点中,叶子节点都是索引,方便扫描数据库,只需要扫描一次叶子节点即可。所以B+树更适合做区间查询,而基于范围的查询在数据库中非常频繁,所以B+树更适合做数据库索引。面试官:你知道聚簇索引吗?大斌:严格来说聚簇索引不是一种索引类型,而是一种数据存储方式。具体细节取决于其实现。例如,InnoDB聚集索引的叶子节点存储了整张表的行记录。Dabin:类似于字典的拼音目录的聚集索引。表中的数据是按照聚簇索引的规则存储的。就像新华字典一样,整本字典都是按照A-Z的顺序排列的。这就是一张表只能有一个聚簇索引的原因。面试官:聚簇索引相对于非聚簇索引有什么优势?大斌:1.数据访问速度更快,因为聚簇索引将索引和数据存储在同一棵B+树中,所以从聚簇索引中获取数据比非聚簇索引更快。大斌:2、聚簇索引的叶子节点的存储在逻辑上是连续的,所以对于主键的排序查找和范围查找会更快一些。面试官:嗯,问个别的,线程池知道吗?大斌:线程池,顾名思义,就是管理线程的池。面试官:那为什么要用线程池呢?大斌:使用线程池主要有3个原因:减少资源消耗。通过重用已创建的线程来降低线程创建和销毁的成本。提高响应能力。当任务到达时,可以立即执行,无需等待创建线程。提高线程可管理性。统一管理线程,防止系统创建大量同类型的线程,消耗内存。面试官:那你能说说线程池的参数吗?独白:老八卦呵呵~大斌:先看看ThreadPoolExecutor的通用构造函数:bigBin:里面有7个参数。它们是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handlerDabin:corePoolSize。当有新的任务时,如果线程池中的线程数没有达到线程池的基本大小,就会创建一个新的线程来执行任务,否则任务会被放入阻塞队列。当线程池中存活的线程数总是大于corePoolSize时,就应该考虑增大corePoolSize。大斌:maximumPoolSize。当阻塞队列满时,如果线程池中的线程数没有超过最大线程数,就会创建一个新的线程来运行任务。否则根据拒绝策略处理新任务。非核心线程类似于临时借用的资源。这些线程应该在空闲时间超过keepAliveTime后退出,以避免资源浪费。大斌:BlockingQueue。存储等待运行的任务。大斌:keepAliveTime。非核心线程空闲后,保持存活时间,该参数只对非核心线程有效。设置为0,表示多余的空闲线程将被立即终止。大斌:TimeUnit。时间单位,如下:TimeUnit.DAYSTimeUnit.HOURSTimeUnit.MINUTESTimeUnit.SECONDSTimeUnit.MILLISECONDSTimeUnit.MICROSECONDSTimeUnit.NANOSECONDS大斌:ThreadFactory。每当线程池创建一个新线程时,都是通过线程工厂方法完成的。ThreadFactory中只定义了一个方法newThread,每当线程池需要创建新线程时调用该方法。公共类MyThreadFactory实现ThreadFactory{privatefinalStringpoolName;publicMyThreadFactory(StringpoolName){this.poolName=poolName;}publicThreadnewThread(Runnablerunnable){returnnewMyAppThread(runnable,poolName);//将线程池名称传递给Constructor,用于区分线程来自不同的线程池}}Dabin:RejectedExecutionHandler.当队列和线程池都已满时,根据拒绝策略处理新任务。AbortPolicy:默认策略,直接抛RejectedExecutionExceptionDiscardPolicy:不处理,直接丢弃DiscardOldestPolicy:丢弃等待队头的任务,执行当前任务CallerRunsPolicy:调用线程处理任务Interviewer:OK。你了解SpringAOP吗?大彬:AOP其实就是面向切面的编程,它封装了一些通用的逻辑(事务管理,日志,缓存等),切面就是那些和业务无关,所有业务模块都会调用的通用逻辑。大斌:SpringAOP是通过动态代理技术实现的。面试官:哦,动态代理有哪些实现方式?大斌:动态代理技术的实现方式有两种:基于接口的JDK动态代理。基于继承的CGLib动态代理。在Spring中,如果目标类没有实现接口,那么SpringAOP会选择使用CGLIB来动态代理目标类。面试官:您刚才提到了CGlib动态代理,能详细介绍一下吗?大斌:CGLIB就是代码生成器库。它是一个强大的高性能代码生成库,被广泛应用于AOP框架中,提供方法拦截操作。大斌:CGLIB代理主要是通过字节码的运行,为对象引入一个间接层来控制对象的访问。大斌:相对于JDK动态代理,CGLib动态代理的局限性要小很多。目标对象不需要实现接口,底层是通过继承目标对象生成代理子对象。面试官:对,好好准备二面~coding不容易,如果觉得对你有帮助,可以点个赞鼓励一下!