面试官:源码你完全搞懂了吗?用这20个问题检验你

【高能预警】:两万字长文,建议先收藏再看,深度源码分析,二十个问题带你一网打尽集合面试。由于文章篇幅过长,本文将持续更新中 添加小助手VX:xuanwo008即可领取全部面试题

问题一:看到这个图,你会想到什么?

「两万字」面试官:听说你很懂集合源码,接我二十道问题

 

(PS:截图自《编程思想》)

这个图由Map指向Collection的Produces并不是说Map是Collection的一个子类(子接口),这里的意思是指Map的KeySet获取到的一个视图是Collection的子接口。

我们可以看到集合有两个基本接口:Map和Collection。但是我个人认为Map并不能说是一个集合,称之为映射或许更为合适,因为它的KeySet视图是一个Set类型的键集,所以我们姑且把它也当做集合。

Collection继承了Iterator接口,而Iterator的作用是给我们提供一个只能向后遍历集合元素的迭代器,也就是说所有实现Collection的类都可以使用Iterator遍历器去遍历。

每种接口都有一个Abstract开头的抽象子类,这个子类中包括了一些默认的实现,我们在自定义类的时候都需要去继承这个抽象类,然后根据我们不同的需求,对于其中的方法进行重写。

从容器角度上来说,只有四种容器:Map,Queue,Set,List。

问题二:列出常见的集合,并进行简单的介绍

答:

  1. ArrayList: 一种可以动态增长和缩减的的索引序列
  2. LinkedList:一种可以在任何位置进行高效地插入和删除操作的有序序列
  3. ArrayDeque:一种用循环数组实现的双端队列
  4. HashSet:一种没有重复元素的无序集合
  5. TreeSet:一种有序集
  6. EnumSet:一种包含枚举类型值的集
  7. LinkedHashSet:一种可以记住元素插入次序的集
  8. PriorityQueue:一种允许高效删除最小元素的集合
  9. HashMap:一种存储键/值关联的数据结构
  10. TreeMap:一种键值有序排列的映射表
  11. EnumMap:一种键值属于枚举类型的映射表
  12. LinkedHashMap:一种可以记住键/值项添加次序的映射表
  13. WeakHashMap:一种其值无用武之地后可以被垃圾回收期回收的映射表
  14. IdentityHashMap:一种用==而不是用equals比较键值的映射表
  15. Vector:目前使用较少,因为设计理念的陈旧和性能的问题被ArrayList所取代
  16. Hashtable:线程非同步可以使用HashMap来替代,同步的话可以使用ConcurrentHashMap来替代

问题三:关于Iterator,聊聊你的看法

从鸟瞰图中我们可以看到,所有实现Collection的子类都继承了Iterable接口。这个接口提供了一个iterator()方法可以构造一个Iterator接口对象。然后我们可以使用这个迭代器对象依次访问集合中的元素。

迭代器一般使用方法是这样的:

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while (iter.hasNext()) {
    String s = iter.next();
    System.out.println(s);
}
复制代码

或者是这样的:

//适用于JDK1.8以后的版本
iter.forEachRemaining(element -> System.out.println(element));
复制代码

迭代器的next()工作原理是这样的:

迭代器是位于两个集合元素之间的位置,当我们调用next()方法的时候迭代器指针就会越过一个元素,并且返回刚刚越过的元素,所以,当我们迭代器的指针在最后一个元素的时候,就会抛出会抛出一个NoSuchElementException的异常。所以,在调用next()之前需要调用hasNext()去判断这个集合的迭代器是否走到了最后一个元素。

通过调用next()方法可以逐个的去访问集合中的每个元素,而访问元素的顺序跟该容器的数据结构有关,比如ArrayList就是按照索引值开始,每次迭代都会使索引值加1,而对于HashSet这种数据结构是散列表的集合,就会按照某种随机的次序出现。

Iterator的接口中还有一个remove()方法,这个方法实际上删除的是上次调用next()方法返回的元素,下面我来展示一下remove()方法的使用方法

Collection<String> c = ...;
Iterator<String> iter = c.iterator();
iter.next();
iter.remove();
复制代码

这样就可以删除该集合中的第一个元素,但是需要注意一点,如果我们需要删除两个元素,必须这样做:

iter.remove();
iter.next();
iter.remove();
复制代码

而不能这么做:

iter.remove();
iter.remove();
复制代码

因为next()方法和remove()方法之间是有依赖性的,如果调用remove之前没有调用next就会抛出一个IllegalStateException的异常。

问题四:对于Collection,你了解多少?

可以看出,作为顶级的框架,Collection仅仅是继承了Iterable接口,接下来,我们来看一下Iterable的源码,看看有什么收获。

public interface Iterable<T> {
   
    Iterator<T> iterator();
    
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

复制代码

可以看到这个接口中有三个方法,其中iterator()方法可以给我们提供一个迭代器,这个在之前的教程就已经说过了,而forEach()方法提供了一个函数式接口的参数,我们可以使用lambda表达式结合来使用:

Collection<String> collection = ...;
collection.forEach(String s -> System.out.println(s));
复制代码

这样就可以获取到每个值,它的底层实现是加强for循环,实际上也是迭代器去遍历,因为编译器会把加强for循环编译为迭代遍历。 Spliterator()是1.8新加的方法,字面意思可分割的迭代器,不同以往的iterator()需要顺序迭代,Spliterator()可以分割为若干个小的迭代器进行并行操作,既可以实现多线程操作提高效率,又可以避免普通迭代器的fail-fast(fail-fast机制是java集合中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件)机制所带来的异常。Spliterator()可以配合1.8新加的Stream()进行并行流的实现,大大提高处理效率。

「两万字」面试官:听说你很懂集合源码,接我二十道问题

 

Collection()中提供了17个接口方法(除去了继承自Object的方法)。接下来,我们来了解一下这些方法的作用:

  1. size(),返回当前存储在集合中的元素个数。
  2. isEmpty(),如果集合中没有元素,返回true。
  3. contains(Object obj),如果集合中包含了一个与obj相等的对象,返回true。
  4. iterator(),返回这个集合的迭代器。
  5. toArray(),返回这个集合的对象数组
  6. toArray(T[] arrayToFill),返回这个集合的对象数组,如果arrayToFill足够大,就将集合中的元素填入这个数组中。剩余空间填补null;否则,分配一个新数组,其成员类型与arrayToFill的成员类型相同,其长度等于集合的大小,并填充集合元素。
  7. add(Object element),将一个元素添加到集合中,如果由于这个调用改变了集合,返回true。
  8. remove(Object obj),从集合中删除等于obj的对象,如果有匹配的对象被删除,返回true。
  9. containsAll(Collection<?> other),如果这个集合包含other集合中的所有元素,返回true。
  10. addAll(Collection<? extends E> other),将other集合中的所有元素添加到这个集合,如果由于这个调用改变了集合,返回true。
  11. removeAll(Collection<?> other),从这个集合中删除other集合中存在的所有元素。如果由于这个调用改变了集合,返回true。
  12. removeIf(Predicate<? super E> filter),从这个集合删除filter返回true的所有元素,如果由于这个调用改变了集合,则返回true。
  13. retainAll(Collection<?> other),从这个集合中删除所有与other集合中的元素不同的元素。如果由于这个调用改变了集合,返回true。
  14. clear(),从这个集合中删除所有的元素。
  15. spliterator(),返回分割后的若干个小的迭代器。
  16. stream(),返回这个集合对于的流对象。
  17. parallelStream(),返回这个集合的并行流对象。

作为第一级的集合接口,Collection提供了一些基础操作的借口,并且可以通过实现Iterable接口获取一个迭代器去遍历获取集合中的元素。

注意一下免费获取的方式:添加小助手VX:xuanwo008!

由于内容太多,答案我只写了一部分,剩下的已整理成文档形式上传网盘了,添加小助手VX:xuanwo008即可领取

「两万字」面试官:听说你很懂集合源码,接我二十道问题

 

「两万字」面试官:听说你很懂集合源码,接我二十道问题

<think>好的,我现在需要帮用户整理关于MySQL面试的提方式和经典问题示例。用户之前已经提供了一些引用内容,包括索引、事务隔离、字符集等问题,还有之前的回答涉及增删改查语句。我需要综合这些信息,同时参考用户提到的站内引用,来生成合适的回答。 首先,用户想要的是面试的提方式和问题示例。提方式可能包括直接问题、场景题、对比题、优化题、底层原理题和陷阱题。我需要确保每个类别都有例子,并且结合引用中的内容,比如索引、MVCC、字符集等。 接下来,我需要检查用户提供的引用内容。引用1提到索引相关的10道题,引用2涉及引擎、查询缓存、表空间等,引用3是事务隔离和MVCC,引用4是字符集问题。这些都需要融入到问题示例中,确保覆盖重要知识点。 同时,用户之前的回答中包含了增删改查的示例,可能还需要补充索引优化、事务、锁机制等内容。例如,用户之前提到了ON DUPLICATE KEY UPDATE,这可能涉及到唯一索引和重复键处理,可以作为一个问题。 还要注意问题的层次,从基础到高级,覆盖不同的方面。例如,基础概念、索引优化、事务与锁、存储引擎、性能调优、设计实践、特殊场景和综合应用。每个部分都需要有具体的例子,并引用相关的资料。 需要确保回答结构清晰,使用用户指定的格式,比如分点列出提方式和问题示例,并在适当的位置添加引用标记。例如,在索引的问题后添加[^1],在字符集的问题后添加[^4]。 另外,用户可能希望问题示例不仅包括问题本身,还有简要的解析方向,帮助面试者理解如何回答。例如,在索引失效场景中,列举几种情况并说明原因。 最后,要检查是否有遗漏的重要主题,比如MVCC、LBCC、回表查询、临时表等,这些在引用2和3中都有提到,需要包含在问题示例中。 总结步骤: 1. 确定提方式的分类,并给出每个类别的例子。 2. 整理经典问题示例,覆盖各个重要知识点,结合引用内容。 3. 为每个问题添加解析方向,并标注引用来源。 4. 确保结构清晰,符合用户之前的回答格式。 5. 检查是否覆盖了用户提供的所有引用内容,避免遗漏关键点。</think>#### MySQL 面试方式及经典问题示例 以下是结合面试高频考点和实际场景的提方式分类及典型问题示例,涵盖基础、进阶和深度原理,供参考: --- ### 一、提方式分类 1. **直接概念型** - "解释什么是索引?它的作用和代价是什么?"[^1] - "MySQL 的 InnoDB 和 MyISAM 引擎有什么区别?"[^2] 2. **场景分析型** - "如果查询 `WHERE age > 20 AND name LIKE '张%'` 时未走索引,可能的原因是什么?" - "订单表每天新增 100 万数据,查询变慢,如何优化?" 3. **对比选择型** - "`CHAR` 和 `VARCHAR` 的区别是什么?什么场景下选择 `VARCHAR`?"[^4] - "悲观锁和乐观锁的实现方式及适用场景?"[^3] 4. **优化实践型** - "如何优化 `SELECT COUNT(*)` 的性能?" - "分页查询 `LIMIT 100000,10` 慢,如何解决?" 5. **底层原理型** - "B+树索引为什么适合数据库?它的层高如何计算?"[^1] - "MVCC 如何实现事务隔离?Read View 的生成规则是什么?"[^3] 6. **陷阱排查型** - "为什么 `utf8mb4` 比 `utf8` 更推荐使用?"[^4] - "`WHERE col+1=10` 会导致索引失效吗?为什么?"[^1] --- ### 二、经典问题示例 #### 基础概念 1. **索引机制** - 问题:B+树索引的叶子节点存储什么?为什么非叶子节点不存数据? - 解析方向:减少磁盘IO、范围查询优化[^1] 2. **事务特性** - 问题:如何通过 Redo Log 和 Undo Log 保证事务的持久性和原子性? - 解析方向:日志写入顺序、崩溃恢复机制[^3] #### 索引优化 3. **索引失效场景** - 问题:以下哪些操作会导致索引失效? ```sql WHERE LEFT(name,3)='abc' -- 函数运算 WHERE age=20 OR name='Tom' -- OR 条件未全索引 ``` - 解析方向:最左前缀原则、隐式类型转换[^1] 4. **覆盖索引与回表** - 问题:什么是回表查询?如何通过索引优化避免? - 解析方向:联合索引设计、`EXPLAIN` 的 `Using index`[^2] #### 事务与锁 5. **隔离级别实现** - 问题:RR(可重复读)隔离级别如何避免幻读? - 解析方向:Next-Key Lock 锁机制、MVCC 快照读[^3] 6. **死锁排查** - 问题:两个事务分别执行以下语句,如何产生死锁? ```sql -- 事务A UPDATE accounts SET balance=balance-100 WHERE id=1; UPDATE accounts SET balance=balance+100 WHERE id=2; -- 事务B UPDATE accounts SET balance=balance-50 WHERE id=2; UPDATE accounts SET balance=balance+50 WHERE id=1; ``` - 解析方向:锁顺序冲突、死锁检测机制 #### 存储引擎 7. **InnoDB 特性** - 问题:为什么 InnoDB 推荐使用自增主键? - 解析方向:页分裂优化、B+树插入效率[^2] 8. **内存表与临时表** - 问题:`MEMORY` 引擎和临时表的适用场景是什么? - 解析方向:数据生命周期、并发支持限制[^2] #### 性能调优 9. **慢查询分析** - 问题:如何定位并优化一条执行时间超过 2 秒的 SQL? - 解析方向:`EXPLAIN` 执行计划、索引覆盖、子查询重构 10. **连接池配置** - 问题:连接数设置过高会导致什么问题?如何合理配置? - 解析方向:上下文切换开销、`max_connections` 参数调整 #### 设计实践 11. **字符集问题** - 问题:为什么 `utf8mb4` 是 MySQL 中更推荐的字符集? - 解析方向:Emoji 存储、四字节编码支持 12. **分库分表策略** - 问题:如何设计水平分表方案?分片键的选择依据是什么? - 解析方向:数据均匀分布、查询路由优化 #### 特殊场景 13. **大数据量删除** - 问题:如何安全删除 1 亿条历史数据而不引发锁表? - 解析方向:分批删除、低峰期操作、`pt-archiver` 工具 14. **高并发更新** - 问题:库存扣减场景如何保证数据一致性? - 解析方向:乐观锁版本号、`SELECT ... FOR UPDATE`[^3] #### 综合应用 15. **场景设计题** - 问题:设计一个电商系统的订单表,需支持按用户ID、订单状态、时间范围查询,如何设计表结构和索引? - 解析方向:联合索引顺序、冷热数据分离[^1][^2] --- ### 三、高频考点总结 | **类别** | **高频关键词** | |----------------|------------------------------------------| | 索引优化 | B+树、最左前缀、覆盖索引、索引下推、Cardinality | | 事务与锁 | ACID、MVCC、隔离级别、Next-Key Lock、死锁检测 | | 存储引擎 | InnoDB vs MyISAM、行锁 vs 表锁、Redo Log | | 性能调优 | 执行计划、慢查询日志、连接池、批量操作 | | 设计实践 | 范式 vs 反范式、字符集、分库分表、读写分离 | --- ### 四、回答技巧 1. **结构化回答**:使用 "原理 + 场景 + 示例" 三段式,例如解释索引时,先说明 B+树结构,再结合 `WHERE` 条件优化场景,最后用 `EXPLAIN` 展示效果。 2. **陷阱识别**:注意面试官隐含考点,如 "为什么主键要自增?" 实际考察页分裂问题[^2]。 3. **原理延伸**:回答完基础问题后,可补充延伸知识(如:B+树层高计算、Read View 源码实现),体现深度[^1][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值