国有企业搬砖三年由余,每日crud, 技术还在 jsp+ssm 每天挣扎不堪。 猛回头看一下现在的应届学子,后生可畏,总感觉我们当时都是渣渣,和他们现在毕业水平相比我也是自愧不如。真是不知不觉中 Java 都已经卷成这样了吗。犹记得老师曾告诉我们说零几年那会你会写个HTML 都能月薪上万。现在你熟悉各种框架JVM,数据库可能连找个实习都费劲。说起来挺搞笑的,我现在出去感觉面试个实习生都费劲【手动狗头】。本博文一直更新,欢迎指正。
题目都是从牛客上找的,CSDN 找的答案。
目录
一. 字节一年社招
1. 用过哪些Java容器?Which kind of containers of Java you hava been used?
2. ArrayList 和 LinkedList 特点及各自应用场景?
3. TreeMap 怎么按照自己想要的顺序排序
4. 说下 TreeMap 和 LinkedHashMap
12. 你在项目中用Redis 的场景
13. 说下 Redis 有哪些数据类型
岸柏科技(一面)
2. Mysql 有哪些锁
3. 解释一下ACID 都是什么
4. Innodb 中索引的实现
易通集团
一号店
4. HashMap 的底层实现原理
5. HashMap 是线程安全的吗
8. 写一个单例模式。
一. 字节一年社招
先来个一个年字节的社招练练手
1. 用过哪些Java容器?Which kind of containers of Java you hava been used?2. ArrayList 和 LinkedList 特点及各自应用场景?这个范围很广泛,一般来说来分为 Collection 和 Map 两大类。只针对数据进行存储的Collection, 和键值对存储的 Map, 最常用的集合 ArrayList,和LinkedList, 如果在多线程的情况下可以使用 Vector, 或者直接使用Collecitons 中的线程安全的方法。如果不要求顺序和重复值可以使用Set ,常用到的有 HashSet 和 TreeSet。队列我接触到基本上就是比如生产者消费者模式中这些固定的写法,线程池之类的队列。Map 常见的有 HashMap 和 TreeMap。
Well, Containers of Java is very wide. generally, It can be divided into Collection and Map. Collection store data without index, Map store key-value pairs. The most common use of collection,such as ArrayList, and LinkedList. Also use vectors if you have to store in multiple threads, or simply use the thread-safe method in Collecitons'. Set can be used if sequential and duplicate values are not required. Such as HashSet and TreeSet are most common I haved used to define in my program. Queue, I've been use queue in that situation,basically like the producer consumer model, thread pools and things like that. Common use of Map like HashMap and TreeMap.
3. TreeMap 怎么按照自己想要的顺序排序ArrayList 采用数组进行存储,特点是存储访问快,插入慢,LinkedList 采用链表的方式进行存储,特点是访问慢插入快。
从数据结构实现上讲,ArrayList 是动态数据的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
从随机访问效率上讲,ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
从增加和删除效率上讲,在非首尾的增加和删除 *** 作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删 *** 作要影响数组内的其他数据的下标。
综合来讲,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除 *** 作较多时,推荐使用LinkedList。
一般来说有两种方式,一种是根据 TreeMap 中 key 的字典顺序来排序,默认排序规则对应底层数据结构红黑树的中序遍历,另一种可以自定义排序规则,实现 Comparator接口, 有一个可能会遇到的坑是如果使用默认排序且 key 是数字字符串,比如 "1","3","21", 这样,如果想按照数字大小排序需要把 key 改成Long 类型。
3.1加餐:这里记录一下什么玩意是红黑树,一直也没有搞明白这个红黑树是干啥的。
1. 平衡树介绍
红黑树的本质是对概念模型: 2-3-4树 的一种实现,2-3-4树是阶数为 4 的 B 树,B 树,全名 Balance Tree, 平衡树。 这种结构主要用来做查找。 最重要的特性在于平衡,这样使我们能够在最坏情况下也保持 O(LogN) 的时间复杂度实现查找(一个不具备平衡性的查找树可能退化成单链表,时间复杂度会到 O(N))。 平衡的定义是说从空链接到根节点的距离。
由于 2-3-4 树是一颗阶数为 4 的 B 树,所以会存在一下节点:
2节点:包含1个元素的节点,有2个子节点
3节点:包含2个元素的节点,有3个子节点
4节点:包含3个元素的节点,有4个子节点
2节点存放着一个key[X] , 两个指针,分别指向小于X 的子节点和大于X 的子节点;3节点中存放在两个 key[X,Y], 三个指针,分别执行小于X 的子节点,介于 X,Y 之间的子节点和大于 Y 的子节点; 4节点一次类推。
234树节点至少有1个元素,符合二叉查找树的性质,即父节点大于左子节点,小于右子节点,但对于234 有多个元素时,每个元素必须大于它左边的和它的左子树中元素。
2. 234树到红黑树的转化
红黑树是对概念模型234树的一种实现,由于直接进行不同节点间的转化会造成较大的开销,所以选择以二叉树为基础,在二叉树的属性中加入一个颜色属性来表示234树种不同的节点。
234树种的2节点对应着红黑树中的黑色节点,而234树中的非2节点是以红节点+黑节点的方式存在,红节点的意义是与黑色父节点结合,表达着234书中的3,4节点。(看到这里好像刚明白存在几个子节点就叫几节点,好尴尬。)
我们先看234树到红黑树的节点转换。2节点直接转化为黑色节点;3节点这里可以有两种表现形式,左倾红节点或右倾红节点。而4节点被强制要求转化为一个黑父带着左右两个红色儿子。
4. 说下 TreeMap 和 LinkedHashMap图解:什么是红黑树? - 知乎
TreeMap 底层数据结构就是红黑树,和HashMap 的红黑树结构一样,与HashMap 不同的是,TreeMap 利用了红黑树左节点小,右节点大的性质,根据 key 进行排序,使每个元素能够插入到红黑树大小适当的位置,维护了key 的大小关系,适用于需要进行排序的场景。
LinkedHashMap 本身是继承 HashMap 的,所以它拥有 HashMap 的所有特性,在此基础上还提供了两大特性
第一:按照插入顺序进行访问
链表特性
LinkedHashMap 的数据结构,就像是把LinkedList 的每个元素换成了 HashMap 的Node, LinkedHashMap 像两者的结合体,不过也正是因为增加了这些结构,才能把 Map 的元素都串联起来,形成一个链表,既然是一个链表,那就可以保证顺序了。
按照顺序新增
在 LinkedHashMap 初始化时,我们默认 accessOrder 为 false, 意思就是会按照插入顺序提供访问,插入方法使用的是父类 HashMap 的 put 方法,不过覆写了 put 方法执行中调用的 newNode以及newTreeNode 和 afterNodeAccess 方法,put 方法中的newNode以及newTreeNode 方法,可以控制新增节点追加到链表的尾部,这样每次新节点都追加到尾部,即可保证插入顺序了。
按照顺序访问
LinkedHashMap 只提供了单向访问,即按照插入的顺序从头到尾进行访问,不能像 LinkedList 那样可以做到双向访问,因此主要通过迭代器进行访问,在迭代器初始化的时候,默认从头节点开始访问,在迭代的过程中,不断访问当前节点的 after 节点即可
Map 对 key、value 和 entity 都提供出了迭代的方法,假设我们需要迭代 entity,就可使用 LinkedHashMap.entrySet().iterator() 这种写法直接返回 LinkedHashIterator ,LinkedHashIterator 是迭代器,我们调用迭代器的 nextNode 方法就可以得到下一个节点
先前在新增节点时,就已经使用put 方法中的newNode以及newTreeNode 方法来维护元素之间的插入顺序,所以迭代访问时非常简单,只需要不断的访问当前节点的下一个节点即可
第二:实现了访问最少最先删除功能,其目的是把很久都没有访问的 key 自动删除。
这种策略其实就是 LRU(Least recently used,最近最少使用),简单来说,在链表中的LRU,就是经常访问的元素会被追加到队尾,这样不经常访问的数据自然就被前移,慢慢靠近队头,然后我们可以通过设置删除策略,比如当 Map 元素个数大于多少时,把头节点删除,这就实现了最少最先的删除
5. 说下 HashMap
6. ConcurrentHashMap 怎么实现的, 1.7和1.8分别说下
7. ConcurrentHashMap 怎么取的 size 值
8. 一个非常多元素的HashMap , rehash 非常耗时,所以需要在他rehash 过程中还能get、put ,你有什么解决方案或者思路,谈谈你的理解?
9. 怎么防止恶意请求刷接口
10. 说下 ES 的倒排索引
11. 那 ES 怎么切词的呢,有写过切词插件吗?
12. 你在项目中用Redis 的场景13. 说下 Redis 有哪些数据类型从自己当前负责参与开发的一个项目中来看,redis主要的应用场景有如下几个,第一个是保存用户信息,这个需要频繁的获取。比如在打开某一个页面进行查询时,就先需要获取用户信息,看用户是否具有查询权限;第二个应用场景是,当数据库查询比较慢时,也会使用到redis缓存,第一次查询可能会比较慢,就将结果缓存在redis中,当第二次进行访问时就快多了;第三个应用场景是使用字典表进行翻译某些字段的值,将字典表进行放在redis中进行存储;第四个应用场景是,当查询用户的权限时,将所有用户对应的权限信息放在redis中。
在 Redis 中,所有的对象都被封装成 redisObject, 包括 type、encoding 两个属性
type:就是 Redis 支持的 string、hash、list、set、和 zset 五种类型
encoding:就是对应的数据结构
对redis 来说,所有key 都是字符串
1. String 字符串类型
是redis中最基本的数据类型,一个key对应一个value。
String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象。
使用:get 、 set 、 del 、 incr、 decr 等
实战场景:
- 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。
- 计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。
- session:常见方案spring session + redis实现session共享
2. Hash是一个Map,指值本身又是一种键值对结构,如 value={{field1,value1},……fieldN,valueN}}
使用:所有hash的命令都是 h 开头的 hget 、hset 、 hdel 等
实战场景:
- 缓存: 能直观,相比string更节省空间,的维护缓存信息,如用户信息,视频信息等。
3. 链表
List 说白了就是链表(redis 使用双端链表实现的 List),是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据。
实战场景:
- timeline:例如微博的时间轴,有人发布微博,用lpush加入时间轴,展示新的列表信息。
4. Set 集合
集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中 1. 不允许有重复的元素,2.集合中的元素是无序的,不能通过索引下标获取元素,3.支持集合间的 *** 作,可以取多个集合取交集、并集、差集。
使用:命令都是以s开头的 sset 、srem、scard、smembers、sismember
实战场景
- 标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。
- 点赞,或点踩,收藏等,可以放到set中实现
5. zset 有序集合
有序集合和集合有着必然的联系,保留了集合不能有重复成员的特性,区别是,有序集合中的元素是可以排序的,它给每个元素设置一个分数,作为排序的依据。
(有序集合中的元素不可以重复,但是score 分数 可以重复,就和一个班里的同学学号不能重复,但考试成绩可以相同)。
使用: 有序集合的命令都是 以 z 开头 zadd 、 zrange、 zscore
实战场景:
- 排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。
14. 说说你在项目中怎么用的这些数据类型
15. 一次请求拿出来所有的数据有什么问题,那怎么改进?
16. Redis 怎么分片的
17. Redis 的删除策略
岸柏科技(一面)1. 自我介绍
2. Mysql 有哪些锁3. 解释一下ACID 都是什么1. 加锁的目的:
解决事务的隔离性问题,让事务之间相互不影响,每个事务进行 *** 作的时候都必须先对数据加上一把锁,防止其它事务同时 *** 作数据。
2. 锁是基于什么实现的:
数据库里面的锁是基于索引实现的,在Innodb 中我们的锁都是作用在索引上面的,当我们的SQL 命中索引时,那么锁住的就是命中条件内索引节点(行锁),如果没有命中索引的话,那我们锁的就是整个索引树(表锁)。(不得不说设计的很科学,服)
3. 锁的分类
基于锁的属性分类:共享锁、排他锁。
基于锁的粒度分类:表锁、行锁、记录锁、间隙锁、临键锁。
基于锁的状态分类:意向共享锁、意向排他锁。
共享锁:
共享锁又称为读锁,简称 S 锁。当一个事务对数据加上读锁之后,其他事务只能对该数据加读锁,而无法对数据加写锁,知道所有的锁释放之后其他事务才能对其加持写锁。加了共享锁之后,无法再加排他锁,这也就可以避免读取数据的时候会被其他事务修改,从而导致重复度问题。
排他锁:
排他锁又称写锁,简称 X 锁;当一个事务对数据加上写锁之后,其他事务将不能再为数据加任何锁,知道该锁释放之后,其他事务才能对数据进行加锁。加了排他锁之后,其他事务就无法再对数据进行读取和修改,所以也就无法出现脏写和脏读的问题。
表锁
表锁指上锁的时候锁住的是整个表,当下一个事务访问该表的时候,必须等前一个事务释放锁才能进行对表进行访问。特点:力度大,加锁简单,容易冲突;
行锁
行锁是对所有行级别锁的一个统称,比如下面说的记录锁、间隙锁、临键锁都是属于行锁,行锁是指加锁的时候锁住的是表的某一行或者多行记录,多个事务访问同一张表时,只有被锁住的记录不能访问,其他记录可以正常访问;特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高;
记录锁
记录锁属于行锁中的一种,记录锁的范围只是表中的某一条记录,记录锁是说事务再加锁后所著的只是表的某一条记录。
触发条件:精准条件命中,并且命中索引;
例如:update user_info set name=’张三’ where id=1 ,这里的id是索引。
记录锁的作用: 加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事务读取的脏读问题。
间隙锁(Gap Lock)
间隙锁属于行锁中的一种,间隙锁是在事务加锁后其锁住的是表记录的某一个区间,当表的相邻 ID 之间出现空隙则会形成一个区间,遵循左开右闭原则。
触发条件:范围查询,查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中。
间隙锁作用:防止幻读问题,事务并发的时候。两次查询间隙区间内如果提交了一个区间内的记录会导致两次查询结果不一致。
临键锁(Next-Key Lock)
临键锁也属于行锁的一种,并且它是INNODB的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住。
触发条件:范围查询,条件命中了索引。
临键锁的作用:结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之后,在范围区间内数据不允许被修改和插入。
状态锁
状态锁包括意向共享锁和意向排他锁,把他们区分为状态锁的一个核心逻辑,就是因为这两个锁都是藐视是否可以对某一个表进行加表的状态。
意向锁的解释:当一个事务试图对整个表进行加锁(共享锁或排他锁)之前,首先需要获取得对应类型的意向锁。这个意思是比如一个事务对表内一条记录修改时,另一个事务也要修改一条信息,因为第一个事务加了排他锁,所以别的事务需要一条条检查索引是否加锁,所以第一个事务在加排他锁之前,获得了这个表的意向排他锁,第二个事务直接根据这个意向排他锁就知道有事务在对表进行 *** 作,就可以避免了对整个索引树的每个节点扫描是否加锁。这个状态就是我们的意向锁。
意向共享锁
当一个事务试图对这个表进行加共享锁之前,首先需要获得这个表的意向共享锁。
意向排他锁
当一个事务试图对整个表急性加排他锁之前,首先需要获得这个表的意向排他锁。
4. Innodb 中索引的实现什么是事务?
在数据库系统里而言,事务是代表一个或者一系列 *** 作的最小逻辑单元,所有在这个逻辑单元内的 *** 作要么全部成功,要么就全部失败,不存在任何中间状态,一旦事务失败那么所有的更改都会被撤销,一旦事务成功所有的 *** 作结果都会被保存。
为什么要有事务?
事务机制存在的目的就是无论我们的 *** 作过程中是成功、失败、异常、或是收到干扰的情况下,事务都能保证我们数据的一致性。(例子,转账 *** 作)
事务的特性(ACID)
要实现事务的最终目的,需要集中机制组合才能实现,这几种机制就是事务的几个特性,分别是原子性、隔离性、一致性、持久性。用一句话总结这几个特性之间的关系,那就是“一致性是事务的最终目的,而原子性、隔离性、持久性其实都是为了实现一致性的手段。”
- 原子性(Atomicty):一个事务必须是一系列 *** 作的最小单元,这个 *** 作的过程中,要么整个执行,要么整个回滚,不存在只执行了其中某一个或者某几个步骤。
- 隔离性(Isolation):隔离性是说两个事务的执行都是独立隔离开来的,事务之前不会相互影响,多个事务 *** 作一个对象时会以串行等待的方式保证事务相互之间是隔离的。
- 一致性(Consistency):事务要保证数据库整体数据的完整性和业务的数据的一致性,事务成功提交整体数据修改,事务错误则回滚到数据回到原来的状态。
- 持久性(Durability):持久性是指一旦事务成功提交后,只要修改的数据都会进行持久化,不会因为异常、宕机而造成数据错误或丢失。
Innodb的索引采用了B+树的存储结构来管理
① 叶子节点存储所有的数据,内节点只存储键值;
② 由于键值既在叶子节点上也在内节点上,需要一定的存储空间,空间换性能;
③ 查询的复杂度和B+树的层数有关,层数越少,性能越好;
④ 层数和每个数据页储存多少条数据有关,也就是和key_length有关。所以B+树不适合存储长的索引列。
Innodb索引分为聚簇索引(clustered index)和普通索引(secondary index):
聚簇索引:每张表必须有且只有一个聚簇索引来存放所有的数据。它的叶子节点存放了整张表的所有数据行。聚簇索引第一会选择我们建表时明确定义的主键列;如果没定义主键列,第二会选择第一个非空的唯一索引列;如果前二者都没有,Innodb会选择隐藏的Rowid(实例级别,6byte)做为聚簇索引。
普通索引:除了聚簇索引,其他的所有索引都为普通索引。它的叶子节点只存放普通索引列和对应的用于回表的键值,这个键值就是上面说的主键、第一个非空唯一索引或者Rowid。当然还包括TRXID,ROLLPTR列。
5. B+ 树
6. AUTO_INCREMENT 原理
7. 数据库的索引有哪几种?为什么要用B+树来做索引?组合索引和几个单个的索引有什么区别?数据库的大表查询优化了解吗?MVCC机制了解不?MVCC 机制有什么问题? mysql 慢语句调优做过吗?说说你是怎么做的?
8. Redis 了解吗?说说怎么用redis 实现分布式锁?
9. Redis 常用数据机构及底层数据结构实现
10. 如何解决 Redis 的并发竞争 Key 问题
11. 如何保证缓存与数据库双写时的数据一致性?
12. 死锁产生的原因
必要条件:
互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
循环等待条件:在发生死锁时,必然存在一个进程-资源的环形链。
预防死锁:
资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请求和保持条件)
可剥夺资源,即当某秦城获得了部分资源,但得不到其他资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每个进程按标号递增的顺序请求资源,释放则相反(破坏环路等待条件)
13. 进程、线程区别,什么时候用线程
14. 如何实现一个线程池,Java 中线程池如何进行配置
15. linux 中有哪些常见的指令,进行介绍
16. select、poll、epoll 有没有了解过,讲解一下
17. 线程切换,引申到 Java 阻塞->运行
18. 页面置换算法有哪些介绍一下
易通集团1. jdk 8 stream 如何保证线程安全?
2. 背包算法, 红黑树,双向链表
3. 双向链表实现;
4. List 的子类都有哪些
5. left join 和 right join
6. Map 类型,底层实现原理
7. 如何检测死锁?
8. 使用过的前端框架都有哪些?
一号店1. JVM 虚拟机的内存结构
2. 垃圾回收算法
3. 类加载
4. HashMap 的底层实现原理5. HashMap 是线程安全的吗用 Node[] table 数组来实现,数组中放的是Node 节点,是一个静态内部类,实现了 Map.Entry 接口。节点内部有 key, value, hash, next 四个成员变量,其中 next 为指向下一个 Node 节点的引用。因此数据结构采用数组 + 单向链表 实现。
put 方法的步骤:
- 对 key 的hashcode 值再计算hash.
- 用计算出的hash 值,与表的length 求 & 算出索引位置。
- 如果索引碰撞,遍历链表,判断是否有 key 存在,若存在更新 value 值。(不过再 JDK 1.8 中链表元素时,会把链表结构转换成红黑树结构进行存储,提高查询的性能)
- 若未碰撞,直接放入表中。
- 放入前,如果元素个数超过负载因子的比例,则进行rehash, 扩容,之后插入。
- null key 的元素永远会放到 index 为 0 的链表里。扩容:初始容量为2 的整数次方,不够会用最接近给定容量的2 的整数次方,初始为16,灭磁扩容数据大小会加倍。然后所有元素重新再新table 中计算 hash 值, 重新得到桶位置,放入。
不安全为什么HashMap线程不安全?以及实现HashMap线程安全的解决方案_gougege0514的博客-CSDN博客_hashmap为什么线程不安全
6. 如何实现一个线程安全的HashMap?
7. ConcurrentHashMap 的底层实现原理?
8. 写一个单例模式。class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)