# Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

0%

## 记录下 Java Stream 的一些高效操作

发表于 2022-05-15 分类于 java 阅读次数： Disqus：

我们日常在代码里处理一些集合逻辑的时候用到 Stream 其实还挺多的，普通的取值过滤集合一般都是结合 ide 的提示就能搞定了，但是有些不太常用的就在这记录下，争取后面都更新记录下来。

### 自定义 distinctByKey 对结果进行去重

stream 中自带的 distinct 只能对元素进行去重
比如下面代码

    public static void main(String[] args) {
            List<Integer> list = new ArrayList<>();
            list.add(1);
            list.add(1);
            list.add(2);
            list = list.stream().distinct().collect(Collectors.toList());
            System.out.println(list);
        }

结果就是去了重的

    [1, 2]

但是当我的元素是个复杂对象，我想根据对象里的某个元素进行过滤的时候，就需要用到自定义的 `distinctByKey` 了，比如下面的想对 userId 进行去重

    public static void main(String[] args) {
            List<StudentRecord> list = new ArrayList<>();
            StudentRecord s1 = new StudentRecord();
            s1.setUserId(11L);
            s1.setCourseId(100L);
            s1.setScore(100);
            list.add(s1);
            StudentRecord s2 = new StudentRecord();
            s2.setUserId(11L);
            s2.setCourseId(101L);
            s2.setScore(100);
            list.add(s2);
            StudentRecord s3 = new StudentRecord();
            s3.setUserId(12L);
            s3.setCourseId(100L);
            s3.setScore(100);
            list.add(s3);
            System.out.println(list.stream().distinct().collect(Collectors.toList()));
        }
        @Data
        static class StudentRecord {
            Long id;
            Long userId;
            Long courseId;
            Integer score;
        }

结果就是

    [StudentRecord(id=null, userId=11, courseId=100, score=100), StudentRecord(id=null, userId=11, courseId=101, score=100), StudentRecord(id=null, userId=12, courseId=100, score=100)]

因为对象都不一样，所以就没法去重了，这里就需要用

    public static <T> Predicate<T> distinctByKey(
                Function<? super T, ?> keyExtractor) {

            Map<Object, Boolean> seen = new ConcurrentHashMap<>();
            return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
        }

然后就可以用它来去重了

    System.out.println(list.stream().filter(distinctByKey(StudentRecord::getUserId)).collect(Collectors.toList()));

看下结果

    [StudentRecord(id=null, userId=11, courseId=100, score=100), StudentRecord(id=null, userId=12, courseId=100, score=100)]

但是说实在的这个功能感觉应该是 stream 默认给实现的

### 使用 java.util.stream.Collectors#groupingBy 对 list 进行分组

这个使用场景还是蛮多的，上面的场景里比如我要对 userId 进行分组，就一行代码就解决了

    System.out.println(list.stream().collect(Collectors.groupingBy(StudentRecord::getUserId)));

#### 结果

    {11=[StudentRecord(id=null, userId=11, courseId=100, score=100), StudentRecord(id=null, userId=11, courseId=101, score=100)], 12=[StudentRecord(id=null, userId=12, courseId=100, score=100)]}

很方便的变成了以 `userId` 作为 `key`，以相同 `userId` 的 `StudentRecord` 的 `List` 作为 `value` 的 `map` 结构

## 闲话篇-路遇神逻辑骑车带娃爹

发表于 2022-05-08 分类于 生活 阅读次数： Disqus：

周末吃完中饭去买菜，没想到碰到这个神（zhi）奇（zhang）大哥带着两个娃

阅读全文 »

## Leetcode 1115 交替打印 FooBar ( Print FooBar Alternately *Medium* ) 题解分析

发表于 2022-05-01 分类于 Java ， leetcode 阅读次数： Disqus：

无聊随机了道并发题，还是比较明显的用信号量就可以解决的题，不过第一次做还是有点不适应，要加油多练练

阅读全文 »

## 屯菜惊魂记

发表于 2022-04-24 分类于 生活 阅读次数： Disqus：

因某国际大都市的给力表现，昨儿旁边行政区启动应急响应，同事早上就在群里说要去超市买菜了，到了超市人还特别多，由于来的就是我们经常去的那家超市，一方面为了安全，另一方面是怕已经抢不到了，就去了另一家比较远的超市，开车怕没车位就骑了小电驴，还下着小雨，结果到了超市差不多 12 点多，超市里出来的人都是推着一整车一整车的物资，有些比较像我，整箱的泡面，好几提纸巾，还有各种吃的，都是整箱整箱的，进了超市发现结账包括自助结账的都排很长的队，到了蔬菜货架附近，差点哼起那首歌“空空如也~”，新鲜蔬菜基本已被抢空，只剩下一些卖相不太好的土豆番薯之类的，也算是意料之外情理之中了，本来以为这家超市稍微离封控区远一些会空一点，结果就是所谓的某大都市封控了等物资，杭州市是屯了物资等封控，新鲜蔬菜没了我们也只能买点其他的，神奇的是水果基本都在，可能困难时期水果不算必需品了？还是水果基本人人都已经储备了很多，不太能理解，虽然水果还在，但是称重的地方也还有好多人排队，我们采取了并行策略，LD 在那排队，遥控指挥我去拿其他物资，拿了点碱水面，黑米，那黑米的时候还闹了个乌龙，因为前面就是散装鸡蛋的堆货的地方，结果我们以为是在那后面排队，结果称重那个在那散步了，我们还在那排队，看到后面排队，那几个挑的人也该提醒下吧，几个鸡蛋挑了半天，看看人家大妈，直接拿了四盘，看了下牛奶货架也比较空，不过还有致优跟优倍，不过不算很实惠，本来想买，只是后来赶着去结账，就给忘了，称好了黑米去看了下肉，结果肉也没了，都在买猪蹄，我们也不太爱吃猪蹄，就买了点鸡胸肉，整体看起来我们买的东西真的有点格格不入，不买泡面（因为 LD 不让买了），也不屯啥米和鸡蛋，其实鸡蛋已经买了，米也买了，其他的本身冰箱小也放不下太多东西，我是觉得还可能在屯一点这那的，LD 觉得太多了，基本的米面油有了，其他调味品什么也有了。后面就是排队结账，我去排的时候刚好前面一个小伙子跟大妈在争执，大妈说我们差不多时间来的，你要排前面就前面，小伙子有点不高兴，觉得她就是插队，哈哈，平时一般这种剧情都是发生在我身上的，这会看着前面的吵起来还是很开心的，终于有跟我一样较真的人了，有时候总觉得我是个很纠结，很较真的人，但是我现在慢慢认可了这种较真，如果没有人指出来这种是插队行为，是不对的，就会有越来越多的人觉得是可以随意插队的，正确的事应该要坚持，很多情况大家总是觉得多一事不如少一事，鸡毛蒜皮的没什么好计较的，正是这种想法，那么多人才不管任何规则，反而搞得像遵守规则都是傻 X 似的。回到屯物资，后面结账排到队了也没来得及买原来想买的花生牛奶什么的，毕竟那么多人排着队，回家后因为没有蔬菜，结果就只能吃干菜汤和饭了

## nginx 日志小记

发表于 2022-04-17 分类于 nginx 阅读次数： Disqus：

nginx 默认的日志有特定的格式，我们也可以自定义，

默认的格式是预定义的 combined

    log_format combined '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent"';

配置的日志可以使用这个默认的，如果满足需求的话

    Syntax:	access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
            access_log off;
    Default: access_log logs/access.log combined;
    Context: http, server, location, if in location, limit_except

而如果需要额外的一些配置的话可以自己定义 `log_format` ，比如我想要给日志里加上请求时间，那就可以自己定义一个 `log_forma`t 比如添加下

    $request_time
    request processing time in seconds with a milliseconds resolution;
    time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client

    log_format combined_extend '$remote_addr - $remote_user [$time_local] '
                        '"$request" $status $body_bytes_sent '
                        '"$http_referer" "$http_user_agent" "$request_time"';

然后其他的比如还有 gzip 压缩，可以设置压缩级别，flush 刷盘时间还有根据条件控制

这里的条件控制简单看了下还比较厉害

比如我想对2xx 跟 3xx 的访问不记录日志

    map $status $loggable {
        ~^[23]  0;
        default 1;
    }

    access_log /path/to/access.log combined if=$loggable;

当 `$loggable` 是 0 或者空时表示 if 条件为否，上面的默认就是 1，只有当请求状态 status 是 2xx 或 3xx 时才是 0，代表不用记录，有了这个特性就可以更灵活地配置日志

文章主要参考了 nginx 的 log 模块的文档