Java基础知识储备
2024-07-12 18:03:21 4 举报
AI智能生成
Java基础知识储备
作者其他创作
大纲/内容
类加载器
类加载器分类
除启动类加载器外都派生于ClassLoader
类加载过程
类什么时候初始化
类初始化顺序
双亲委派机制
原理
优点
如何打破双亲委派机制
改类后为什么需要重启`
在jvm生命周期中每个类如果存在,则不会重复加载
运行时数据区
程序计数器
线程私有的,生命周期与线程生命周期保持一致
存储当前线程正在执行的 Java 方法的 JVM 指令地址
分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成
Java虚拟机栈
栈的特点及运行原理
栈帧
局部变量表
操作数栈
动态链接(指向运行时常量池的方法引用)
方法返回地址
本地方法栈
JNI
简介
组成
作用
堆
堆内存区域划分
对象创建内存分配过程
新生区与老年区配置比例
分代收集思想 Minor GC、Major GC、Full GC
堆空间的参数设置
字符串常量池为什么要调整位置?
方法区
方法区的内部结构
方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出, 虚拟机同样会抛出内存溢出的错误。
方法区大小设置
方法区的垃圾回收
执行引擎
简介
解析器
字节码解释器
模版解释器
运行模式
热点代码
热点探测
基于采样的热点探测
基于计数器的热点探测
热点代码缓存
JIT编译器
简介
即时编译器
触发条件
即时编译器实现
C1编译器
C2编译器
javac编译器和即时编译器
机器码
指令
指令集
汇编语言
高级语言
字节码
Java 代码编译和执行过程
JVM调优
性能的三个指标
JVM调优时机
调优的基本原则
调优目标
调优工具
调优步骤
JVM参数
示例
GC
内存结构
线程私有
Java虚拟机栈
本地方法栈
程序计数器
线程公有
方法区
堆
内存中“垃圾”的定义
引入计数法
可达性分析算法
GC ROOT对象包含哪些?
不可达对象不一定会被回收?
垃圾回收算法
标记清除(内存碎片)
标记整理
复制
分代回收
垃圾回收器
Serial(复制)
Serial Old(标记整理)
ParNew(复制)
Parallel Scavenge(复制)
Parallel Old(标记整理)
CMS(标记清除)
初始标记(STW)-并发标记-并发预清理-重新标记(STW)-并发清理-并发重置
G1
Region
Remembered Set(RS)
hash表
Card Table
Region中存活对象的指针
初始标记(STW)-并发标记-最终标记-筛选回收
引用分类
强引用
软引用(内存不足时回收)
弱引用(不考虑内存回收)
虚引用(任何时候)
终结器引用
内存问题
内存溢出
内存泄漏
数据库连接(datasource)、io连接、网络连接(socket)未关闭
初始标记为什么会产生STW?
概念、特点、对比
JMM
原子性
可见性
热点代码缓存
有序性
指令重排
redis
九种数据类型及应用场景
字符串(String)
缓存(字符串、序列化对象或者图片)、分布式锁、计数器、分布式Session、限流
哈希表(Hash)
对象类型数据
列表(List)
简单队列,列表显示、分页、热点新闻
集合(Set)
赞、踩、标签,统计独立IP,交并差集
有序列表(ZSet)
排行榜、商品排序
地理空间(Geo)
地理信息(地理空间索引、附近商家)
位图(Bitmaps)
记录状态(登录状态、签到)
基数统计(HyperLogLog)
元素数量或者体积大时,计算基数所需空间固定很小
统计不重复的元素个数、搜索关键词数量、数据分析
流信息(Streams)
消息队列
内存淘汰策略和过期删除策略
内存淘汰策略
noviction
volatile-lru
allkeys-lru
volatile-lfu
allkeys-lfu
volatile-random
allkeys-random
volatile-ttl
过期删除策略
定时删除
定期删除
惰性删除
持久化机制
RDB
定义
原理
优缺点
AOF
定义
日志重写
工作原理
优缺点
数据恢复
持久化方案建议
三种集群模式
主从复制
主要作用
工作原理
同步
命令传播
优缺点
哨兵模式
主要功能
工作原理
哨兵监控
哨兵通知及故障转移
优缺点
Cluster 集群模式
数据分片
主要作用
数据分区
高可用
数据分片
复制原理
一致性保存
集合部署
缓存常见问题及优化方案
缓存击穿
场景
热点数据过期
分布式存储
主从复制,读写分离
缓存前置
兜底逻辑
高并发请求
解决方案
互斥锁
分布式锁
布隆过滤器
读写锁
空对象
缓存穿透
场景
恶意攻击
缓存与数据库不一致
解决方案
缓存空对象
数据预热
数据库查询过滤
缓存雪崩
场景
设置了相同的过期时间
服务器宕机
解决方案
设置相同的过期时间
数据持久化
使用Redis集群
使用缓存淘汰策略
限流和熔断
备份和灾难恢复
监控和告警
网络模型
内存存储
Reactor模型
单线程模型和多线程模型
网络协议
多线程
线程池
原理
采用池化思想(类似连接池、常量池、对象池等)管理线程的工具
核心组件
核心线程数
概念
池中一直存活的线程数,即使处于空闲状态;c核心线程也会被回收
参数设置
CPU密集型任务
CPU核心数+1
IO密集型任务
CPU核心数 * 2
混合任务
具体情况具体分析
最大线程数
参数设置
资源受限的系统
高并发系统
200
概念
核心线程全部繁忙且任务队列打满之后,线程池会追加线程,直到总线程数达到maximumPoolSize这个上限
任务队列
资源受限的系统
高并发系统
1000
阻塞队列
定义:BlockingQueue是一个基于阻塞机制(ReentrantLock)实现的线程安全的队列
ArrayBlockingQueue:由数组实现的有界阻塞队列,需指定队列大小,生产者消费者共用一个锁对象,无法真正并行运行,性能较低
LinkedBlockingQueue:有链表组成的无界阻塞队列,默认使用Integer.MAX_VALUE作为队列大小,生产者消费者分别维护独立的锁来控制数据同步
SynchronousQueue
不存储元素的阻塞队列,无容量
PriorityBlockingQueue
优先级队列,支持优先级排序的无界阻塞队列,通过Comparable接口的compareTo()方法实现
DelayQueue
延迟队列,支持延时获取元素的无界阻塞队列
LinkedTransferQueue
一个由链表结构组成的无界阻塞队列,与LinkedBlockingQueue相比多了transfer和tryTranfer方法,该方法在有消费者等待接收元素时会立即将元素传递给消费者
LinkedBlockingDeque
双端队列,一个由链表结构组成的双端阻塞队列
keepAliveTime
非核心线程空闲到达这个时间后,会被回收,将allowCoreThreadTimeOut参数设置为true后,核心线程也会这样
TimeUnit
ThreadFactory
指定线程池创建线程的方式
任务拒绝策略
AbortPolicy
CallerRunsPolicy
DiscardPolicy
DiscardOldestPolicy
自定义拒绝策略
好处
降低资源消耗,提升响应速度
降低创建、销毁线程带来的开销,直接复用已创建的线程
降低使用复杂度
将提交与执行解耦,只需要向里面提交任务
提高线程的可管理性
避免无限制创建线程
线程池状态
running
运行状态
shutdown
关闭状态,不再接受新任务提交,但是会将已保存在任务队列中的任务处理完
stop
停止状态,不再接受新任务提交,并且会中断当前正在执行的任务、放弃任务队列中已有的任务
tidying
整理状态,所有的任务都执行完毕后(也包括任务队列中的任务执行完),当前线程池中的活动线程数降为 0 时的状态。到此状态之后,会调用线程池的 terminated() 方法。
terminated
销毁状态,当执行完线程池的 terminated() 方法之后就会变为此状态。
线程池源码
ThreadPoolExecutor.execute-ThreadPoolExecutor.addWorker-Thread.start-Worker.run-Worker.runWorker
execute(Runnable command)
通过 ctl.get()得到线程池的当前线程数,如果线程数小于corePoolSize,则调用 addWorker(commond,true)方法创建新的线程执行任务,否则执行步骤2;
步骤1失败,说明已经无法再创建新线程,那么考虑将任务放入阻塞队列,等待执行完任务的线程来处理。基于此,判断线程池是否处于Running状态(只有Running状态的线程池可以接受新任务),如果任务添加到任务队列成功则进入步骤3,失败则进入步骤4;
来到这一步需要说明任务已经加入任务队列,这时要二次校验线程池的状态,会有以下情形:
线程池不再是Running状态了,需要将任务从任务队列中移除,如果移除成功则拒绝本次任务
线程池是Running状态,则判断线程池工作线程是否为0,是则调用 addWorker(commond,true)添加一个没有初始任务的线程(这个线程将去获取已经加入任务队列的本次任务并执行),否则进入步骤4;
线程池不是Running状态,但从任务队列移除任务失败(可能已被某线程获取?),进入步骤4;
线程池不再是Running状态了,需要将任务从任务队列中移除,如果移除成功则拒绝本次任务
线程池是Running状态,则判断线程池工作线程是否为0,是则调用 addWorker(commond,true)添加一个没有初始任务的线程(这个线程将去获取已经加入任务队列的本次任务并执行),否则进入步骤4;
线程池不是Running状态,但从任务队列移除任务失败(可能已被某线程获取?),进入步骤4;
将线程池扩容至maximumPoolSize并调用 addWorker(commond,false)方法创建新的线程执行任务,失败则拒绝本次任务。
addWorker
runworker
https://mp.weixin.qq.com/s/I0IzEPffkwpwWkTmJ-J8Vw
https://mp.weixin.qq.com/s/nj9tnz1JI3ZsLeuk8S5DTg
单线程实现多并发
时间片轮转
时间循环
协程
定义
异步编程
ThreadLocal
原理
使用场景
共享变量
线程不安全的工具对象(如:SimpleDateFormat)
Spring中的事务管理器
线程状态
new
runnable
ready
running
blocked
一个正在阻塞等待一个监视器锁的线程处于这一状态
waiting
timed-waiting
terminated
切换线程状态
Thread.sleep(long millis)
当前线程调用,当前线程进入TIMED_WAITING状态,但不释放对象锁
Thread.yield()
当前线程调用,当前线程放弃CPU时间片,但不释放锁资源,状态改为就绪状态,让OS再次选择线程
thread.jion()/join(long milis)
当前线程调用其他线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不释放锁,join()基于wait实现
obj.wait()/wait(long timeout)
当前线程释放对象锁,进入等待队列,通过notify()/notifyAll()唤醒
obj.notify()/obj.notifyAll()
唤醒对象监视器上等待的任意单个线程
LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines)
当前线程不需要获得锁,就进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒
https://blog.csdn.net/pange1991/article/details/53860651
中断
interrupt()
中断线程,不会影响线程执行,只是作为一种标志位,被捕获InterruptedException后,中断状态为false
isInterrupted
返回中断状态
interrupted
返回中断状态,且清除控制位
execute()
执行过程
判断线程池的状态,如果不是running状态,直接执行拒绝策略
如果当前线程数<核心线程数,则新建一个线程来处理提交的任务
如果当前线程数>核心线程数且任务队列没满,则将任务队列放入阻塞队列中执行
如果核心线程数<当前线程数<最大线程数,且任务队列已满,则创建新的线程执行提交的任务
如果当前线程数 > 最大线程数,且队列已满,则执行拒绝策略拒绝该任务
Java锁
概念
锁种类
重入锁
读写锁
公平锁
private ReentrantLock lock = new ReentrantLock(true)
互斥锁
信号量
偏向锁
锁场景
数据库访问
文件读写
共享内存
队列操作
网络通信
锁问题
死锁问题
重入问题
性能问题
公平性问题
如何减少死锁
拆分大事务
将长时间的计算、网络操作或其他操作移除出事务
使用乐观锁
提高系统性能
ReentrantLock
lock流程
ReentrantLock.lock-ReentrantLock.Sync.lock-ReentrantLock.NonfairSync/FairSync.lock-AQS.acquire-AQS.tryAquire-NonfairSync/fairSync.tryAquire-若加锁失败,将线程存入等待队列AQS.addWaiter-等待队列中的线程尝试获取锁操作,如果获取成功或者被中断就退出AQS.acquireQueued
tryAquire()内容
1)判断等待队列是否有线程处于等待状态,如果没有,尝试获取锁;如果有,就进入等待队列(NonfairSync没有)
2)采用CAS方式修改线程同步状态,如果成功返回true
3)支持当前线程,重复获得锁,将state值加1
2)采用CAS方式修改线程同步状态,如果成功返回true
3)支持当前线程,重复获得锁,将state值加1
ReentrantLock如何实现可重入的
AQS中有变量exclusiveOwnerThread存储当前拥有独占访问权限的线程,tryAquire()通过getExclusiveOwnerThread获取对应线程
unlock流程
ReentrantLock.unlock-AQS.release-AQS.tryRelease-尝试释放锁Sync.tryRelease-若释放成功,从等待队列中获取一个等待线程并尝试唤醒AQS.unparkSuccessor
AQS
原理
AQS是基于 CLH 队列,使用volatile修饰共享变量state,线程通过CAS方式去改变state状态值,如果成功则获取锁成功,失败则进入等待队列,等待被唤醒的线程同步器框架
使用
AQS是JUC包下线程同步器实现的基石,ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、ThreadPoolExecutor等
https://mp.weixin.qq.com/s/KrTn6WQZOdcVfhCCEwGgPg
信号量(Semaphore)
信号量用于控制同时访问资源的线程数量
使用场景
控制多个线程依次执行
private final Semaphore semaphoreA = new Semaphore(2);
currentSemaphore.acquire();
nextSemaphore.release();
currentSemaphore.acquire();
nextSemaphore.release();
互斥量(Mutex)
互斥量用于保证同一时间只有一个线程访问资源
synchronized与lock的区别
灵活性
Lock可尝试获取和定时获取,synchronized不支持
等待可中断
lock获取所得操作可中断Lock.lockInterruptibly(),synchronized不支持
锁分离
lock通过Condition对象允许将等待/通知机制与对象锁分离(lock.newCondition(), await(),signal(),signalAll()),synchronized的等待/通知机制与对象锁关联(wait(),notify(),notifyAll())
性能
lock可能会好些
原理
synchronized
基于对象头的Mark Word来实现的,包括偏向锁、轻量级锁和重量级锁,通过monitor enter和monitor exit实现
ReentrantLock
通过内部的AbstractQueuedSynchronizer(AQS)框架来实现锁的获取、释放以及线程等待和唤醒等功能
语法简洁性
https://mp.weixin.qq.com/s/XtSJomdqP1XjuTuv-0DqVQ
设计模式
好处
可扩展
可靠性
可读性
重用性
7大原则
单一职责原则
接口隔离原则
依赖倒转原则
里氏替换原则
开闭原则
迪米特法则
合成复用原则
种类
创建型
单例模式
懒汉式
线程不安全、线程安全
饿汉式
SpringBoot 2.5.5默认实现方式
线程不安全、线程安全
静态内部类
枚举
工厂模式
进销项第三方配置
简单工厂模式
根据传入的参数决定创建哪种类型的对象
工厂方法模式·
定义了一个创建对象的接口,但由子类决定实例化哪个类
抽象工厂模式
提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类
原型模式
避免初始化的拷贝,不想被拷贝
建造者模式
结构型
适配器模式
百望、航信大象、航信诺诺,查验需要参数不相同,但对其进行封装,对外统一用一个方法调用
外观模式
多个复杂的子系统提供一个一致的接口
代理模式
为其他对象提供一种代理以控制对这个对象的访问
aop、Spring事务
组合模式
依据树形结构来组合对象,用来表示部分以及整体层次,比如:学校、院、系的关系
一个对象参数有多个子对象参数组成
享元模式
通过共享对象来减少创建大量相似对象时的内存消耗
池化技术、数据库连接池、线程池
桥接模式
用于将抽象部分与实现部分分离,使得它们可以独立地变化
jdbc连接数据库,利用驱动来桥接
装饰器模式
行为型
策略模式
票据过敏词
模板方法模式
百望对接token、参数、调用
观察者模式
获取所有对象都执行逻辑,代码
命令模式
访问者模式
迭代器模式
https://mp.weixin.qq.com/s/AIJBdwTVuN_U-tZoiKznOg
Java基础
反射
优缺点
Volatile的原理
哈希冲突
hash算法
不同长度的输入,通过散列算法,变成固定长度的输出,结果为散列值
被计算的数据是无限的,但是范围是有限的
解决方案
开放定址法(线性探测法)
按次序在哈希表(散列表)中找一个空闲的位置插入,threadlocal通过该方法解决
链式寻址法
存入单向链表,hashMap通过链式寻址法和红黑树来解决
再哈希法
继续hash算法,直到没有hash冲突
建立公共溢出区
单独的地址存放
队列(Queue)
阻塞队列
ArrayBlockingQueue
用数组实现的 有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序
LinkedBlockingQueue
用链表实现的 有界阻塞队列,此队列的默认和最大长度为Integer.MAX_VALUE
PriorityBlockingQueue
带优先级的队列,而不是先进先出队列,元素按优先级顺序被移除,而且它是无界的,也就是没有容量上限
DelayQueue
无界阻塞队列,只有在延迟期满时才能从中提取元素
LinkedBlockingDeque
由链表结构组成的双向阻塞队列
非阻塞队列
ConcurrentLinkedQueue
单向链表结构的无界并发队列, 非阻塞队列,由CAS实现线程安全,内部基于节点实现
ConcurrentLinkedDeque
双向链表结构的无界并发队列, 非阻塞队列,由CAS实现线程安全
PriorityQueue
内部基于数组实现,线程不安全的队列
双端队列
Deque
双端队列是在两端都可以进行插入和删除的队列
拦截器与过滤器
过滤器
实现Filter 接口或通过@WebFilter注解实现对特定URL拦截
方法
init():容器启动初始化过滤器时被调用,返回true,过滤器才能生效,只会掉一次
doFilter()
容器中的每一次请求都会调用该方法,方法内有个参数FilterChain,FilterChain的实现类ApplicationFilterChain,ApplicationFilterChain的回调函数doFiler() 实现的过滤器
destroy()
容器销毁 过滤器实例时调用,只会掉一次
拦截器
实现HandlerInterceptor接口
方法
preHandle()
如果返回false,这个和其他拦截器都会失效
postHandle()
在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用,,先进后出
afterCompletion(()
在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行
区别
实现原理
过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)
使用范围
过滤器的接口Filter在servlet中,因此只能在web容器中使用,拦截器是spring的一个组件,使用没有限制
触发时机
Tomcat - Filter - servlet - Interceptor - controller
拦截的请求范围
过滤器几乎可以对所有进入容器的请求起作用,,而拦截器只会对Controller中请求或访问static目录下资源的请求起作用
注入Bean情况
Filter支持注入Bean、Interceptor不支持,拦截器在SpringContext之前加载,,还没有DI
Tomcat优化
操作系统优化(内核参数)
TCP连接数优化、time_wait的优化
Tomcat配置文件参数的优化
redirectPort
maxThreads、minSpareThreads、maxSpareThreads、processorcache、acceptCount,maxKeepAliveRequests
URIEncoding
connectionTimeout、disableUploadTimeout、connectionUploadTimeout
enablelookups
compression、compressionMinSize、compressableMimeType
JVM调优
-Xms、-Xmx、-Xmn、
子主题
编程思想对比
面向接口编程
手段:通过接口规约对象的属性和方法,是面向对象一部分
目的:统一标准问题(公用部分行为)
面向切面编程
手段:分离业务的主逻辑和次逻辑的一种思想
目的:解决的是逻辑分离问题(主逻辑和次逻辑分开,其实主要是分离业务逻辑和非业务逻辑分开)
场景:系统日志的记录、请求的拦截
面向对象编程
针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分
集合
安全类
Vector、Stack、HashTable、ConcurrentHashMap、ConcurrentLinkedQueue、ArrayBlockingQueue
Set
HashSet
原理
底层是HashMap
add方法是map.put(e, PRESENT)==null
去重原理
通过key值相同去重,对象必须重写equals和hashcode方法
key的hash相等,如果equals也相等,说明已存在,如果有一个不相等,则正常put
ConcurrentHashMap
组成
Node数组+链表+红黑树
原理
CAS+synchronized
CAS:存在内存值a,预期值b,想要更新成c,若a == b,则a更新成c
①若数组不存在,则初始化内存容量为16的Node数组;
②若无hash冲突,则通过CAS的方式将元素添加到Node数组,val和next都用volatile标识
③若数组正在扩容,则协助扩容
④若hash冲突则对Node数组上的索引加锁,再向链表或红黑树中添加数据
⑤增加map数量t
刷题
哈希
1. 两数之和
遍历,存入map,key为值,value为索引
128. 最长连续序列(不论顺序)
放入set,while set.contains(++num),max(最大,当前最大)
从最小开始,过滤重复数据,查找加一数据是否存在,当前最大,全局最大,max
1446.连续字符(考虑顺序)
变量最长长度,当前字符长度,遍历,max方法
49. 字母异位词分组
Map<String, List<String>> map = new HashMap<>();
for (String str : strs) {
char[] strBytes = str.toCharArray();
Arrays.sort(strBytes);
String key = new String(strBytes);
List<String> itemList = map.getOrDefault(key, new ArrayList<String>());
itemList.add(str);
map.put(key, itemList);
}
return new ArrayList<List<String>>(map.values());
for (String str : strs) {
char[] strBytes = str.toCharArray();
Arrays.sort(strBytes);
String key = new String(strBytes);
List<String> itemList = map.getOrDefault(key, new ArrayList<String>());
itemList.add(str);
map.put(key, itemList);
}
return new ArrayList<List<String>>(map.values());
排序算法
冒泡算法
迭代算法
二分算法
搜索二维矩阵
两个二分算法,mid的获取,high= mid -1
堆算法
最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
父节点i的左子节点在位置:(2*i+1);
父节点i的右子节点在位置:(2*i+2);
子节点i的父节点在位置:floor((i-1)/2)
父节点i的右子节点在位置:(2*i+2);
子节点i的父节点在位置:floor((i-1)/2)
https://mp.weixin.qq.com/s/NelX8acil-1vdtm8aRZg_A
链表
2. 两数相加
迭代,当前节点的值val放在外面,node.next.val += 1
19. 删除链表的倒数第 N 个结点
ListNode result = new ListNode(0, head);
ListNode node1 = result;
ListNode node2 = result;
遍历,通过node1确定删除位置,只能修改next
ListNode node1 = result;
ListNode node2 = result;
遍历,通过node1确定删除位置,只能修改next
21. 合并两个有序链表
迭代
148. 排序链表
迭代,传head、detail节点,不断取中间节点,直到取到detail,最后调用合并两个有序链表
树
94. 二叉树的中序遍历
迭代
104. 二叉树的最大深度
迭代:左最大高度,右最大高度,Math.max() + 1
给你一个二叉树的根节点 root ,返回其 最大路径和
迭代,node.val +左最大和+右最大和,Math.max(原最大,现最大),返回node.val + 最大一边
108. 将有序数组转换为二叉搜索树
迭代、左分支、右分支、返回node
102. 二叉树的层序遍历
队列,fifo,while,queue.size(),遍历,poll(),offer(node.left), offer(node.right),add(List)
双指针
需要移动两头时,则可以用双指针
11. 盛最多水的容器
Math.max(maxArea, Math.min(height[left], height[right]) * (right - left)),然后移动端的一端
15. 三数之和
排序、遍历一个数,另外两个书用双指针获取,子方法
前缀和加哈希表
和为 K 的连续子数组
数组和为map的key,索引或者次数为值,赋初值0,1,map.containKey(curNum-key)为true,则是存在连续数组
36. 有效的数独
连个二维数组一个三维数组,最后一个索引为单元格的值,值为数量
77. 组合
迭代加循环,通过队列解决迭代创建List(new ArrayList<>(deque)),deque.size() == k控制长度,循环控制起始位置
238. 除自身以外数组的乘积
1.拿到所有数的乘积,做除法
2.定义三个length长度的数组,一个表示指定位置的左侧乘积,一个表示指定位置的右侧乘积
sql
185. 部门工资前三高的所有员工
3>子查询员工表薪资>主查询员工表薪资
184. 部门工资最高的员工
where (e.departmentid, e.salary) in
(
select departmentid, max(salary) as salary from employee group by departmentid
)
(
select departmentid, max(salary) as salary from employee group by departmentid
)
1934-确认率
round(ifnull(avg(c.action = 'confirmed'), 0), 2)
180. 连续出现的数字
row_number() over(order by id),row_number() over(partition by num order by id),group by t.num, (t.rn - t.id_rn),having count(1) >= 3
177. 第N高的薪水
mysql:176,oracle:rownum
1327. 列出指定时间段内所有的下单产品
o.order_date like '2020-02-%'
176. 第二高的薪水
select case count(distinct e1.salary)
when 2 then min(e1.salary) else null end as SecondHighestSalary
from employee e1
where 2 > (
select count(distinct e2.salary) from employee e2 where e1.salary < e2.salary
when 2 then min(e1.salary) else null end as SecondHighestSalary
from employee e1
where 2 > (
select count(distinct e2.salary) from employee e2 where e1.salary < e2.salary
178. 分数排名
dense_rank()连续排序
570. 至少有5名直接下属的经理
相当于两个表
550. 游戏玩法分析 IV
date_add(min(event_date), interval 1 day)计算日期
602. 好友申请 II :谁有最多的好友
select requester_id as ids from RequestAccepted union all select accepter_id from RequestAccepted两列合并
1193. 每月交易 I
date_format(trans_date, '%Y-%m')、sum(if(state = 'approved', amount, 0))、count(if(state = 'approved', 1, null))
收藏
0 条评论
下一页