7.7 KiB
7.7 KiB
- 索引的概念
- 用于提高查找速度的一种数据结构
- 为什么要用B+树,B-树和B+树区别,为什么不用红黑树
- Hash表结构存储数据
- 通过key的hash计算得到存储下表
- Hash只能进行等值查询,切换速度非常快,但是无法用于范围查询
- 二叉查找树
- 左边小,右边大,自身带排序
- 二分查找
- 极端情况
- 根节点的选取,如果选取不好就会不平衡
- 出现某一个方向子树过长
- 平衡二叉树
- 改善二叉树极端情况
- 降低IO次数,每走一个节点进行一次IO
- 数的高度过高,IO性能下降
- B树
- 存放指针及数据,保存了排序功能
- 指定阶数M,一个节点包含的子节点最大数量M越大,高度越低
- 范围查询大的时候需要返回根节点再次便利
- 存储了data信息,记录增多的时候树的高度会变高
- B+树
- 数据全部存在子节点中,非子节点只存储键值和指针
- 子节点之间添加链表结构,解决范围查询时需要返回根节点
- 范围查询效率提高很多,单个查询比B+树提高不大
- Hash表结构存储数据
- 那些情况适合建立索引,那些不适合建立索引
- 频繁使用where
- 分组字段,分组的前提为排序
- 统计字段
- 加了索引不需要产生中间表去重
- 频繁更新字段
- 数据单一固定
- 数据量大(数据类型text)
- where条件不怎么用的
- 索引分类,最左前缀,聚簇索引
- 主键索引
- 唯一索引(一级索引)
- 普通索引(二级索引)
- 联合索引(需要符合最左前缀原则)
- 最左前缀
- 第一个值为从小到大排序,第二个不是
- 第一个值确定的情况下,第二个值是有序的
- 从最左边开始连续的索引都可以匹配上,直到遇到范围查询,> < between
- 已经排序好的字段是会生效的,
- a=1 and b>1 ab都会生效
- a>1 and b=1 b索引会失效,a>1 时b为无序的
- MyISAM和InnoDB数据结构
- MYD数据 MYI索引
- ibd数据和索引,读一个文件速度更快
- 聚簇索引
- 根据主键索引可以直接拿到数据,索引和数据放在一起
- InnoDB
- 定义主键为聚簇索引
- 未定义索引,选择第一个部位null唯一索引
- 以上都没用,使用一个6字节长整型的隐式字段ROWID字段构建聚簇索引,该RPWID字段会在插入新行时自动递增
- 非聚簇索引(辅助索引)
- 除聚簇索引外
- 只存储主键ID
- 查询可能需要回表,通过辅助索引查询得到ID去主键索引中查找
- MyISAM中通过主键查询得到数据在硬盘中的地址,再根据该地址去磁盘中寻找
- 主键索引唯一,辅助索引可能等值也会查询出多个数据
- 索引覆盖
- 通过辅助索引可以获得全部数据,不需要回表
- 索引失效
- select *
- %左边
- 未最左匹配
- 数据库认为不要
- 计算
- or
- != > <
- 优化
- 辅助索引覆盖
- 自增主键ID
- 避免select *
- 少用子查询,能使用外连接就外连接,避免笛卡尔积
- 使用短索引,非叶子节点存储更多索引降低树的高度
- 为什么推荐使用自增主键作为索引
- 页分裂
- 降低复杂度
- List集合
- 结合源码,回答核心流程,夹带关键字
- ArrayList
- 如何扩容,大小如何自动增加
-
- 初始化,未给定长度,默认调用午餐构造器
- elementdata 常量数组为空
- new之后数组长度为0
-
- 添加第一个元素
- 计算容量,比较长度后返回初始化长度为10
-
- 数组扩容
- grow方法增加长度
- 新容量为老容量加上老容量右移以为,即1.5倍
- 掉用复制数组的方法
-
- 如何扩容,大小如何自动增加
- 复制某个ArrayList到另一个里面去
- clong,addAll,copy,stream流
- 重写Clone方法
- 浅复制
- 返回一个一样的ArrayList,其中一个改变了元素,另一个也会改变
- 两个集合中存储同一份元素引用
- 深复制
- 重写clone方法,使用迭代器便利,重新创建引用对象,逐个添加
- 索引中ArrayList增加或删除某个元素的运行过程,效率很低吗
- 效率很低,添加或删除某个元素都需要对数组中的元素进行移位,需要不断进行ArrayCopy很浪费时间
- 添加,后面元素都后移一位,再添加
- 删除,后面元素都前移一位覆盖
- 很大的数组需要拷贝,如何快速拷贝
- 创建时就指定数组的大小
- 获得线程安全的ArrayList
- synchorizoned关键字
- LinkedList和ArrayList如何选择
- ArrayList底层为数组,查询效率高,增删效率低
- LinkedList底层为链表,查询效率低,增删效率高
- Vector
- 扩容为2倍,线程安全
- Set
- HashSet
- 无序,去重,非线程安全
- new新建HashMap
- 底层通过HashMap的key值实现的,值为常量PRESENT(一个空的Object对象)
- put调用setValue方法
- 添加时为什么要给个value为PRESENT常量
- PRESENT决定返回oldValue还是null判断是否已经存在
- HashSet remove方法比较PRESENT
- HashMap的删除方法成功会返回value,失败返回null,通过返回判断是否删除
- 去重原理
- hashcode和equals方法
- 先判断hash值,再通过==和wquals判断,返回true则为重复元素
- HashSet和TreeSet
- 无序HashSet,有序TreeSet
- 如何得到一个线程安全的Set集合
- Collections.synchorizeonedSet()
- HashSet
- HashMap的底层结构
- jdk1.7 数组+单向链表
- jdk1.8 数组+链表(单向/双向)+红黑树
- 内部定义静态Node类
- Node<K, V>
- TreeNode有TreeNode left,TreeNode right继承LinkedHashMap.Entry继承HashMap.Node
- 如何解决Hash碰撞
- 存放元素需要通过Hash计算来得到Hash值,Hash值为元素要存放的位置,当两个不同元素经过Hash计算得到的Hash值相同,两个元素要存放在同一个位置,产生冲突
- HashMap的put方法
- 初始化table数组长度为16,第一次添加table为null,通过resize()方法,给新容量16,阈值16*0.75
- threadhold,扩容因子0.75
- 何时从单链表转化为红黑树
- 常量TREEIFY_THREADHOLD = 8 通过treeify()方法重组为红黑二叉树
- 扩容方法
- 通过resize方法扩容,判断是否达到阈值,达到就扩容为原来的两倍
- 数组长度小于临界值MIN_TREEIFY_64或等于null时
- 何时扩容
- 转红黑树时要判断,转换时需要搬运元素,第一个元素要扩容
- 添加一个元素后
- HashMap设置长度为11,那么数组容量为多少
- tbaleSizeFor()方法重组
- hash计算更加平均,与数组长度值有关
- 设置为大于我们自定义的值的第一个2的幂次方的值,设置为11长度为16
- HashMap何时从红黑树转化为单链表模式
- 扩容时需要搬运数据,如果红黑二叉树不需要转化条件就会转化为链表
- resize()方法转移红黑二叉树时会调用splite()方法
- lohead,hihead,分割时会产生高位树和低位树,小于等于6时调用untreeify方法退回链表结构
- 多线程并发使用时,容易造成死循环/死锁
- 非线程安全
- jdk1.7头插法,jdk1.8尾插法
- ConcurrentHashMap
- 线程安全的集合
- ConcurrentHashMap在jdk1.7和1.8的区别
- jdk1.7 Segment段数组+Entry数组+单链表
- Segment继承ReenTrantLock类,每一个segment相当于一个锁,这就是分段锁
- 当其它线程进行put操作时,发现锁资源被占用时回去进行节点创建避免线程空闲,这种思想也叫预创建
- segment长度固定为16,HashEntry可以扩容
- jdk1.8 和HashMap结构一样
- CAS + setCtl
- jdk1.7 Segment段数组+Entry数组+单链表
- 如何保证线程安全
- key和value不支持null的原因
- 二义性