知识点笔记
java
parallelStream
- strem: 顺序遍历
- parallelStream: 并行遍历
anyMatch
找到一个符合谓词时候就返回, 最好与parallelStream
搭配
peek map
两个函数的返回值都是一个新的Stream,但是接收类型 peek是Consumer,map是Function
使用场景: 对象数组的集合流, peek 为遍历数组, 对item进行处理; map为获取出某个属性或对整个item进行处理后返回
参考:
map
HashMap、TreeMap和LinkedHashMap
-
LinkedHashMap按照访问顺序:
Map<String, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
, 默认情况下LinkedHashMap的访问顺序是关闭的(即accessOrder为false), 开启后会通过map.get("a")
使a到最后, 前提是a存在. -
hutool中的Dict继承了LinkedHashMap, 但是没有给出accessOrder的设置(目前5.8.6是)
-
TreeMap: 默认比较器是
键1.compareTo(键2)
, 也即键的自然顺序. -
HashMap转TreeMap来排序:
- hutool中的
MapUtil.sort(map)
; - hutool中的原理:
- 如果要手写通过值来判断顺序的话, 这个比较器中使用的
map
中的值(也即将map当作字典表去比较的), 也即当往sort中put的时候map
中必须要有这个键, 不然.getCode()
时候会有空异常, 也可以在比较器中给value一个默认值(如果可以让没有的在最前面, 如果默认指给得最大会遍历整个map来确定排在最后) - 如果sortMap已经创建出来了, 再次修改map中的一个键将值中的code修改得不一样, 再次sortMap.put()时候因为存在了那个键, 只会替换, 并不会按照map修改键对应值的新顺序来;
- 相对应的, 如果构建出sortMap之后, 给map新增一个新的键值对, 再sortMap.put()时候, sortMap会按照map中的顺序插入.
- 也就是排好序之后, 已经存在的键不会修改; 新进来的键会按照map中的比较器(是按照map的值得顺序, 也即新插入的键会有用, 传入map获取值, 插入的值没用)获取插入位置.
- 如果要手写通过值来判断顺序的话, 这个比较器中使用的
Map<String, R<String>> map = new TreeMap<>(); map.put("apple", R.ok()); map.put("banana", R.param()); map.put("orange", R.fail()); TreeMap<String, R<String>> sortMap = new TreeMap<>(Comparator.comparing(p -> Integer.parseInt(map.get(p).getCode()))); sortMap.putAll(map); System.out.println("sortMap = " + JSONUtil.toJsonStr(sortMap));
- hutool中的
-
区别
内部数据结构: HashMap:使用数组和链表(JDK 8之前)或者数组和红黑树(JDK 8及以后)的组合来存储键值对。 TreeMap:使用红黑树作为内部数据结构,以保持键的有序性。 LinkedHashMap:使用哈希表和双向链表的组合来存储键值对。双向链表维护了插入顺序或访问顺序,保证了迭代顺序与插入顺序或访问顺序一致。 迭代顺序: HashMap:不关心插入顺序或访问顺序,迭代顺序无法保证。 TreeMap:基于键的自然顺序或自定义比较器的顺序,迭代顺序为升序。 LinkedHashMap:迭代顺序可以选择插入顺序或访问顺序(最近访问的元素放在最后),保证了迭代顺序与插入顺序或访问顺序一致。 性能方面: HashMap:插入和查找操作的时间复杂度为O(1),但在哈希冲突较多的情况下,插入和查找操作可能会退化到O(n)。 TreeMap:插入和查找操作的时间复杂度为O(log n),自带有排序功能。 LinkedHashMap:插入和查找操作的时间复杂度为O(1),与HashMap类似,但比HashMap略慢。 内存消耗: HashMap:内存消耗相对较低,不需要额外的空间来维护有序性。 TreeMap:内存消耗较高,每个节点需要存储键、值以及额外的指针和颜色信息。 LinkedHashMap:内存消耗略高于HashMap,因为需要额外的双向链表来维护顺序。 综上所述,LinkedHashMap在功能上结合了HashMap和LinkedList的特点,既可以快速插入和查找,又可以保持插入顺序或访问顺序。然而与HashMap相比,LinkedHashMap在内存消耗方面稍高,并且略慢一些。在选择使用时,要根据具体的需求来进行权衡和选择。
@Conditional
运行jar包时候可以指定: java -jar myapp.jar --spring.profiles.active=prod
ConditionalOnExpression
- 只能用于方法、类或注解的声明上
- 基于 Spring 表达式语言(SpEL)来定义条件的,可以使用 SpEL 中的各种运算符、函数和属性来组合条件;如
@ConditionalOnExpression("'prod'.equals('${spring.profiles.active}')")
或@ConditionalOnExpression("${spring.profiles.active.equals('prod')}")
. - 常用的字符串函数:
- startsWith(prefix):判断字符串是否以指定的前缀开头。
- endsWith(suffix):判断字符串是否以指定的后缀结尾。
- contains(substring):判断字符串是否包含指定的子串。
- equals(str):判断字符串是否与指定的字符串相等。
- matches(regex):判断字符串是否匹配指定的正则表达式。
- substring(startIndex):获取从指定位置开始到字符串结尾的子串。
- substring(startIndex, endIndex):获取指定位置范围内的子串。
- toLowerCase():将字符串转换为小写字母。
- toUpperCase():将字符串转换为大写字母。
- 可用在定时任务上
@Slf4j @Configuration @EnableScheduling @RequiredArgsConstructor @ConditionalOnExpression("'prod'.equals('${spring.profiles.active}')") public class MidJob { }
- 在注入bean时候可以与
@Bean
放在一起 - 在输入自己写的服务时候
- 在写的setter或者构造函数上加上
- 使用
@RequiredArgsConstructor
的onConstructor
参数, 如:@RequiredArgsConstructor(onConstructor_ = {@Autowired, @ConditionalOnExpression(...)})
Conditional
使用方法与上面类似, 不过这个是需要实现org.springframework.context.annotation.Condition.matches()
方法, 见下图:
sdf线程安全
SimpleDateFormat
非线程安全, Date- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- hutool里面DateUtil的
parse
和format
方法.
DateTimeFormatter
线程安全, LocalDateTime- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- hutool里面LocalDateUtil的
parse
和format
方法, DateUtil的parseLocalDateTime
和formatLocalDateTime
;
- 参考:
map.compute
根据键是否存在执行put操作. Function: 单参数 BiFunction: 双参数
compute
: 不管键是否存在都执行 参数类型(K, BiFunction)computeIfAbsent
: 键不存在时候执行 参数类型(K, BiFunction) / (K, V)computeIfPresent
键存在时候执行 参数类型(K, BiFunction)- 例子
Map<Integer, Integer> map = new HashMap<>(); // put 1,2 map.put(1, 2); // 2存不存在都执行后面的方法 map.compute(2, (k, v) -> { return 3; }); // 2存不存在都执行后面的方法 map.compute(2, (k, v) -> { return k + (v == null ? 0 : v) + 3; }); // 2不存在时候执行方法, 类似ObjUtil.defaultIfNull(); map.computeIfAbsent(2, k -> 3); // 2存在时候执行此方法 map.computeIfPresent(2, (k, v) -> 3);
poi外边框
多单元格的外边框
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelOuterBorderThickWithRegionUtil {
public static void main(String[] args) throws IOException {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet1");
// 省略单元格填充
// 定义合并区域
CellRangeAddress region = new CellRangeAddress(0, 3, 0, 3);
sheet.addMergedRegion(region);
// 默认细线THIN / 中等MEDIUM / 加粗THICK
BorderStyle thickBorder = BorderStyle.THICK;
// 使用RegionUtil设置区域边框
RegionUtil.setBorderTop(thickBorder, region, sheet);
RegionUtil.setBorderBottom(thickBorder, region, sheet);
RegionUtil.setBorderLeft(thickBorder, region, sheet);
RegionUtil.setBorderRight(thickBorder, region, sheet);
// 将工作簿写入文件
FileOutputStream fileOut = new FileOutputStream("outer_border_example.xlsx");
workbook.write(fileOut);
fileOut.close();
workbook.close();
}
}
数据库
开启sql打印
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mysql 列变行
- 注意when后多个满足条件时, 会只取一个, 不会报错.
select max(case when then)
SELECT name, MAX(CASE WHEN category = 'A' THEN value END) AS 'A', MAX(CASE WHEN category = 'B' THEN value END) AS 'B', MAX(CASE WHEN category = 'C' THEN value END) AS 'C' FROM my_table GROUP BY name;
case when in() then
方法, 将1,2,3的算作1; 5,6,7的算作8SELECT CASE WHEN your_column IN (1, 2, 3) THEN 1 WHEN your_column IN (5, 6, 7) THEN 8 ELSE your_column END AS grouped_column, COUNT(*) AS group_count FROM your_table GROUP BY grouped_column;
- 参考: mysql列变行
mybatis
@var
必须在数据库配置文件的数据库地址url中加上一个属性allowMultiQueries=true
参考: MySQL设置变量以及如何在Mybatis中使用
insert select *
中间不要value 或 values, mysql中可以省略into
- mysql:
insert t1(v1, v2, v3) select v1, v2, v3 from t2
- pgsql:
insert into t1(v1, v2, v3) select v1, v2, v3 from t2
mysql field()
FIELD(str,str1,str2,str3,...)
- 与order by 组合使用, 让字段按照后续字典表中的内容排序
select * from demo order by field(review_status, 2, 3, 1)
, 其中review_status
为字段名
field()
后可以跟desc
与asc
asc
(默认): 按照表顺序(231), 且不存在231中的数据排在前面, 231的数据在最后desc
: 按照表降序(132), 且不存在132中的数据排在后面, 132的数据在最前面
- 单独使用, 求出第一个值在后列表中的位置(从1开始)
- 第一个值是数字或字符串:
select field(1, 1, 2, 3, 4, 5) res
结果是1 - 第一个值是语句:
select field((select id from (SELECT * FROM demo where id = 1) tt), 4, 1, 2, 1, 3) res
结果是2(后面多个1是取第一个的索引) - 后面存在语句
select field(1, (select id from (SELECT * FROM demo where id in (1, 2, 3)) tt)) res
运行报错Subquery returns more than 1 row
, 表示子查询返回多列, 如果其他地方遇到这种情况需要使用limit 1
或关键词any
或some
等等 - 补充: 当后面列表中不存在第一个值 或 第一个值为null时 返回0
- 第一个值是数字或字符串:
sum(type = 1)
注意必须用sum才可以使用条件,count不可以, 如果count(*)=0时候type_1_percentage为null
SELECT COUNT(*) AS total_count,
SUM(type = 1) AS type_1_count,
(SUM(type = 1) / COUNT(*)) * 100 AS type_1_percentage
FROM your_table;
pgsql中虽然不支持这样, 但是可以sum(case when type = 1 then 1 else 0), 巧妙引入case when语句来解决
redis 备份恢复
- 备份: 使用redis-cli连接server, 如果有密码的话 auth 123(密码)授权
- bgsave: 后台生成备份文件
- save: 生成备份文件, 会阻塞其他指令
生成的文件路径在redis.conf
中的dir
下
- 关闭需要恢复的redis服务器上的redis服务(因为redis关闭时候会将内容的数据备份到路径下, 我们要覆盖他旧的备份文件)
- 移动到需要备份redis服务器上的dir指向的路径(如果dir配置也是默认的, 可以选择配置一下, 或者执行以下save指令, 看生成的dump文件在哪里,就复制到哪里), 然后启动redis即可.
关于dir配置的路径: 默认的./
, 但是我生成的备份文件在根目录下, 反正根目录, 主目录, redis安装目录结合生成/修改时间检查一下. 恢复服务器那边不清楚的话, 可以执行save
指令查看以下生成dump.rdb文件在哪里, 就复制到哪里就行, 建议配置将日志和路径都修改到redis安装目录或者自定义的数据路径中, 防止出现默认./
结果出现在根目录下的情况.
联表更新
- mysql
UPDATE t1 JOIN t2 ON t1.t2_id = t2.id SET t1.t2_name = t2.name;
- pgsql
UPDATE t1 SET t2_name = t2.name FROM t2 WHERE t1.t2_id = t2.id;
执行中的sql
- pgsql:
SELECT pid, query FROM pg_stat_activity
- mysql: 无法找到pid与正在执行sql的关系, 可人为通过
show processlist
来找(来源是表information_schema.processlist
), 故也可通过SELECT * FROM information_schema.processlist
实现
数据库迁移
- 处理之前一定做好数据备份!!! 或者在同版本同架构的虚拟机上进行测试
- mysql
- 停止mysql
- 默认数据路径:
/var/lib/mysql
- 查看目标目录上的用户和用户组是否与默认路径相同, 如果不同, 使用
chown -R 用户:组 /路径
修改 - 复制或移动到目标目录
- 配置文件路径:
/etc/mysql/my.cnf 或 /etc/my.cnf
, 修改其内的datadir
- 启动后进行
- pgsql
- 停止pgsql:
systemctl stop postgresql-14
- 默认数据位置:
/var/lib/pgsql/14/data
- 查看目标目录上的用户和用户组是否与默认路径相同, 如果不同, 使用
chown -R pgsql:pgsql /home/pgsql/data
修改 - 复制或移动到目标路径
- 修改目录权限
chmod 0700 /home/pgsql/data
或chmod 0750 /home/pgsql/data
- 配置文件路径: 目标目录
data
下的配置文件postgresql.conf
, 修改期内的data_directory
- 修改
/usr/lib/systemd/system/postgresql-14.service
: 找到Environment=PGDATA后面的路径 - 重载:
systemctl reload postgresql-14
- 启动pgsql:
systemctl start postgresql-14
- 停止pgsql:
navicat结果标题
-- NAME: 自定义的标题
, 另起一行写查询.- 参考: navicat 查询结果重命名
redis命名空间
命名空间在图形化工具rdm
中看到的效果类似文件目录一样, 他将同样前缀(:
前的, 大于等于两条数据)的数据以前缀为目录归并起来, 而在使用上就当成一个整体使用即可
(这里提供两个版本下载rdm2021.4, 和rdm2022.5, 老版本稳定, 新版本界面和功能多, 电脑性能不高可能会卡)
redisTemplate.opsForValue().set("cata:1", "1");
redisTemplate.opsForValue().set("cata:1:2", "2");
redisTemplate.opsForValue().set("cata:1:2:3", "3");
redisTemplate.opsForValue().set("cata:1:2:4", "4");
// 非逐级的
redisTemplate.opsForValue().set("cata:2:2:3:4", "5");
redisTemplate.opsForValue().set("cata:2:2:3:5", "6");
coalesce
- 含义: 从传入参数中找到第一个不为null的方法
- 用法:
coalesce(null, 0)
- 类似: 深入理解与实战SQL IFNULL()函数
- 参考: sql:函数:COALESCE()
linux
du(disk usage)
- 用法:
du -sh * | sort -rh
, 中间*
是路径 - 解释:
-s
: 只显示总计大小,而不显示每个子目录和文件的大小。就是加了不限制子文件(目录), 不加显示子文件(目录), 子文件的大小是都计算进去的-h
: 以人类可读的格式显示大小,例如使用KB、MB、GB等单位.sort -rh
: 将结果按照大小从大到小排序, r倒序, h以人类可读格式.- 更多通过
sort --help
了解.
- 补充
- du -sh 后面没有
*
时候, 统计的是当前目录的总大小 - 显示前面几条: 最后加
| head -n 5
. 以下针对-s
的有无举两个例子- du -h 路径没有
*
- du -h 路径有
*
, 相比上一个就少了一个当前当前目录, 因为没有-s, 所以所有目录和子文件都展示
- du -sh 路径没有
*
, 这种就显示那个目录下的
- du -sh 路径有
*
, 只显示当前目录的各个目录内容, 加了-s只显示当前目录下各个的总大小
- du -h 路径没有
- du -sh 后面没有
systemctl
- systemctl 中
ExecStart
等配置的指令或脚本内的指令使用的环境变量并不是$PATH
,
而是固定的/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
, 也即默认的$PATH
路径,
所以只在$PATH
中添加路径并不行.可以将链接加入上面路径中或者在ExecStart
中或脚本中使用绝对路径, 比如/path/to/command
- 或者建立软链接:
ln -s /path/to/comand /usr/bin/command
- 如果环境中存在多个java版本或者自己按照的jre找不到错误所在, 可以优先考虑使用绝对路径
- 备注: 指令比如java, 一些脚本(如tomcat)会自动去自动找
JAVA_HOME
, 这时候可以在service文件中添加
Environment=JAVA_HOME=/usr/lib/jvm/java-1.8.0/
- 参考1: systemctl方式启动jar报错java: command not found
- 参考2: 执行Shell脚本,报java: command not found
- 参考3: systemd Unable to find Java
ps(process status)
- 显示所有进程:
ps -e
或ps -A
- 显示所有用户的进程:
ps -ef
或ps aux
, ps aux --sort=-%mem | head -n 5 显示5个占用内存最多的进程. - 显示进程树状结构:
ps -ejH
或ps axjf
kylin audit
- 问题描述: 内存占用高
- 临时解决:
systemctl stop auditd.service
- 升级版本:
yum install -y audit
- 参考:
systemctl打印
- 问题描述: 使用
systemctl status tomcat
时控制台无打印. 或者打印完没有退出, 末尾显示lines 1-13/13 (END)
- 原因: 可能会因为屏幕缓冲区的限制而无法完全显示
- 临时读取
- 重定向到一个文件中
systemctl status tomcat >> a
cat a
查看
systemctl status tomcat | less
, less支持向前向后滚动, 只加载需要展示的, 退出后shell上不会保留systemctl status tomcat | more
, more只能向前, 会加载所以, 然后逐页展示, 退出后shell后保留systemctl status tomcat --no-pager
, 输出将不会被任何分页器处理
- 重定向到一个文件中
- 禁用分页展示
- 临时解决:
export SYSTEMD_PAGER=""
, 或者export PAGER=""
- 永久解决:
- 修改用户的shell文件
.bashrc
或.bash_profile
- 在最后添加
export SYSTEMD_PAGER=""
或export PAGER=""
source ~/.bashrc
加载- 在新窗口里面测试是否生效
- 修改用户的shell文件
- 临时解决:
- 参考: 通过systemctl status查询服务状态未退出
ulimit句柄数
vim /etc/security/limits.conf
, 保存之后需要重启java进程(打开太多文件 / Too Many Open Files)#任何用户可以打开的最大的文件描述符数量,默认1024,这里的数值会限制tcp连接 * soft nofile 655350 * hard nofile 655350 #任何用户可以打开的最大进程数 * soft nproc 655350 * hard nproc 650000
- 参考