知识点笔记

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));
    
  • 区别

    内部数据结构:
    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或者构造函数上加上
      • 使用@RequiredArgsConstructoronConstructor参数, 如:@RequiredArgsConstructor(onConstructor_ = {@Autowired, @ConditionalOnExpression(...)})
  • Conditional
    使用方法与上面类似, 不过这个是需要实现org.springframework.context.annotation.Condition.matches()方法, 见下图:
    image

sdf线程安全

  • SimpleDateFormat非线程安全, Date
    • SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    • hutool里面DateUtil的parseformat方法.
  • DateTimeFormatter线程安全, LocalDateTime
    • DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    • hutool里面LocalDateUtil的parseformat方法, DateUtil的parseLocalDateTimeformatLocalDateTime;
  • 参考:

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的算作8
    SELECT
    	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()后可以跟descasc
    • 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或关键词anysome等等
    • 补充: 当后面列表中不存在第一个值 或 第一个值为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/datachmod 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

redis命名空间

命名空间在图形化工具rdm中看到的效果类似文件目录一样, 他将同样前缀(:前的, 大于等于两条数据)的数据以前缀为目录归并起来, 而在使用上就当成一个整体使用即可
(这里提供两个版本下载rdm2021.4, rdm2022.5, 老版本稳定, 新版本界面和功能多, 电脑性能不高可能会卡)
image

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

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 路径没有*
        image
      • du -h 路径有*, 相比上一个就少了一个当前当前目录, 因为没有-s, 所以所有目录和子文件都展示
        image
      • du -sh 路径没有*, 这种就显示那个目录下的
        image
      • du -sh 路径有*, 只显示当前目录的各个目录内容, 加了-s只显示当前目录下各个的总大小
        image

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 -eps -A
  • 显示所有用户的进程: ps -efps aux, ps aux --sort=-%mem | head -n 5 显示5个占用内存最多的进程.
  • 显示进程树状结构: ps -ejHps axjf

kylin 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 加载
      • 在新窗口里面测试是否生效
  • 参考: 通过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
    
  • 参考
posted @ 2022-11-03 10:59  Codorld  阅读(96)  评论(0编辑  收藏  举报