<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>LCZ&apos;s world</title><description>subtitle</description><link>http://72.155.89.172/</link><language>zh_CN</language><item><title>java知识1</title><link>http://72.155.89.172/posts/java%E7%9F%A5%E8%AF%861/</link><guid isPermaLink="true">http://72.155.89.172/posts/java%E7%9F%A5%E8%AF%861/</guid><pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[ ] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;1.java中的序列化和反序列化&lt;/h1&gt;
&lt;p&gt;序列化就是把内存中的 Java 对象转成二进制字节流，这样才能存到硬盘或者通过网络发给别的机器。反序列化就是反过来，把字节流还原成 Java 对象。硬盘和网络只认 0 和 1，不认 Java 对象，所以必须有这么一层转换。&lt;/p&gt;
&lt;p&gt;序列化的内容一般是可保存、可传输、可打印的形式。对于密码等敏感字段，要防止序列化。&lt;/p&gt;
&lt;p&gt;1）怎么做序列化：类必须实现 Serializable 接口，这玩意儿就是个许可证，没它 JDK 序列化机制压根不让你序列化&lt;/p&gt;
&lt;p&gt;2）怎么防序列化：敏感字段比如密码，加个 transient 关键字，序列化的时候自动跳过&lt;/p&gt;
&lt;p&gt;3）怎么稳（保持兼容性）：显式定义 serialVersionUID，相当于给类打个版本戳，防止改了类结构后反序列化旧数据时报错(自动转换走默认值或默认处理)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String username;
    private transient String password; // 不参与序列化
    private int age;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;对象不能直接传输：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;对象在 JVM 里是&quot;立体&quot;的，到处都是引用关系。比如某个字段指向服务器内存地址 0x1234 处的另一个对象，这地址只在当前 JVM 有效，传到网络另一端，人家客户端的 0x1234 地址上根本不是同一个东西。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;序列化要干的活就是把这些引用关系&quot;压扁&quot;，把整个对象图递归地转成一段自包含的字节序列。接收端拿到后，反序列化时在自己的堆上重建整个对象图，引用关系照样能还原回来。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;静态变量（static）不参与序列化，&lt;/h2&gt;
&lt;p&gt;序列化存的是对象的状态。静态变量属于类。&lt;/p&gt;
&lt;h2&gt;JDK自带序列化的问题&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;性能差，字节流过大，速度慢&lt;/li&gt;
&lt;li&gt;跨语言不友好，py，go读不懂，无法互通
常用JSON序列化解决，虽然性能一般，但可读性好。RPC框架用Protobuf、Hessian等&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;面试官追问&lt;/h2&gt;
&lt;p&gt;提问：transient 和 static 修饰的字段都不会被序列化，它们有什么区别？&lt;/p&gt;
&lt;p&gt;回答：本质不同。static 字段属于类，压根就不是对象状态的一部分，所以不存在&quot;序列化不序列化&quot;的问题，它就不在序列化的范畴内。transient 是专门告诉序列化机制&quot;这个实例字段我不想存&quot;，比如密码、缓存这类不适合持久化的数据。一个是&quot;不归你管&quot;，一个是&quot;归你管但我不让你存&quot;。&lt;/p&gt;
&lt;p&gt;提问：如果父类没有实现 Serializable，子类实现了，序列化子类对象时父类的字段会怎样？&lt;/p&gt;
&lt;p&gt;回答：父类字段不会被序列化。反序列化时，子类字段正常恢复，但父类字段会调用父类的无参构造器重新初始化。如果父类没有无参构造器，反序列化直接报错。所以如果需要父类字段也参与序列化，父类也得实现 Serializable。&lt;/p&gt;
&lt;p&gt;提问：Externalizable 接口和 Serializable 有什么区别？&lt;/p&gt;
&lt;p&gt;回答：Serializable 是自动序列化，JDK 帮你搞定所有字段。Externalizable 是手动挡，必须自己实现 writeExternal 和 readExternal 方法，一个字段一个字段地写出去、读进来。好处是完全可控，可以做定制优化；坏处是麻烦，漏写一个字段就丢数据。性能要求高的场景可以考虑，一般业务代码用 Serializable 就够了。&lt;/p&gt;
&lt;p&gt;提问：为什么说反序列化是 Java 安全漏洞的重灾区？&lt;/p&gt;
&lt;p&gt;回答：反序列化会触发对象的构造过程，如果恶意构造的字节流能让某些类的特定方法被执行，就可能造成远程代码执行。典型的就是利用 Commons-Collections、Fastjson 这些库里的 gadget chain，攻击者只要找到一条能从反序列化入口通往危险方法的调用链，就能在服务器上执行任意代码。防御手段包括：升级有漏洞的依赖库、配置反序列化白名单、用 JSON 替代 Java 原生序列化。&lt;/p&gt;
&lt;p&gt;解释：反序列化不只是“把数据读回来”，它还会根据字节流里的类信息，去创建对象、调用对象的方法。攻击者一旦能控制这段字节流，就可能让服务器执行他想要的代码。攻击者可以伪造一段序列化数据，里面指定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要反序列化成哪个类；&lt;/li&gt;
&lt;li&gt;类的字段值是什么；&lt;/li&gt;
&lt;li&gt;这些字段又引用了哪些其他对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;2.java中HashMap的原理&lt;/h1&gt;
&lt;p&gt;HashMap 底层就是一个数组，结合链表和红黑树来解决冲突。&lt;/p&gt;
&lt;p&gt;它的工作原理其实很简单，主要说这 3 点:&lt;/p&gt;
&lt;p&gt;1）怎么存&lt;/p&gt;
&lt;p&gt;存一个 Key-Value 时，先算 Key 的 hashCode，然后用 (table.length-1) &amp;amp; hash 算出应该放在数组的哪个下标位置。&lt;/p&gt;
&lt;p&gt;2）冲突了怎么办&lt;/p&gt;
&lt;p&gt;两个不同的 Key 算出来下标一样，就叫哈希冲突。HashMap 的解决办法是用链表把它们串起来，JDK 8 做了优化，如果同一个下标下的链表超过 8 个节点（且数组长度大于等于 64），就转成红黑树，查找速度从 O(n) 变成 O(logn)。&lt;/p&gt;
&lt;p&gt;3）扩容机制&lt;/p&gt;
&lt;p&gt;数组长度是有限的，存多了会挤。HashMap 有个负载因子默认 0.75，数组用了 75% 就自动扩容，把数组大小翻倍，然后把所有数据重新算位置放到新数组里。这个操作叫 rehash，比较耗性能，所以初始化时最好给个预估容量。&lt;/p&gt;
&lt;h2&gt;HashMap 是非线程安全的&lt;/h2&gt;
&lt;p&gt;多线程环境下可能出问题，比如数据覆盖或者死循环，这时候应该用 ConcurrentHashMap。&lt;/p&gt;
&lt;h2&gt;hashCode 和 equals 的重要性&lt;/h2&gt;
&lt;p&gt;HashMap 的 key 必须正确实现 hashCode() 和 equals() 方法。hashCode() 决定元素存在哪个桶，equals() 决定两个 key 是不是同一个。&lt;/p&gt;
&lt;p&gt;put 的时候，如果两个 key 的 hashCode() 相同但 equals() 返回 false，它们会被当成不同的 key 存在同一个桶的不同位置。如果 hashCode() 实现得不好导致分布不均，或者 equals() 写错了，会导致元素找不到或者重复插入。
&lt;a href=&quot;#my-anchor&quot;&gt;跳转到解释equals和hashcode的关系&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;hashmap的扩容机制&lt;/h2&gt;
&lt;p&gt;默认容量16&lt;/p&gt;
&lt;p&gt;HashMap 的扩容由负载因子控制，默认是 0.75。意思就是当元素数量超过 容量 × 0.75 时触发扩容。比如初始容量 16，阈值就是 12，存第 13 个元素时就会扩容。&lt;/p&gt;
&lt;p&gt;扩容的规则很简单：容量直接翻倍，从 16 变 32，32 变 64，始终保持 2 的幂次。扩容后所有元素要重新分配位置，这个过程叫 rehashing。即每个元素的存储位置会根据新容量的大小重新计算，并移动到新的数组中。&lt;/p&gt;
&lt;h2&gt;面试官追问&lt;/h2&gt;
&lt;p&gt;提问：为什么链表转红黑树的阈值是 8，不是 6 或者 10？
回答：根据泊松分布计算，在负载因子 0.75 的情况下，一个桶里元素个数达到 8 的概率已经非常低了，大概是千万分之六。选 8 是因为这个概率下转红黑树基本只会在 hash 分布极差的时候发生，正常情况几乎不会触发，既保证了极端情况的性能，又避免了频繁转换的开销。&lt;/p&gt;
&lt;p&gt;提问：JDK 8 改成尾插法就完全线程安全了吗？
回答：没有。尾插法只是解决了扩容时链表成环的问题，但 HashMap 本身还是非线程安全的。并发 put 的时候还是可能丢数据，比如两个线程同时往一个空桶插入，后插入的会覆盖先插入的。多线程场景必须用 ConcurrentHashMap。&lt;/p&gt;
&lt;p&gt;提问：HashMap 的 key 可以用 null 吗？
回答：可以，HashMap 允许一个 null key。存的时候会把 null key 的 hash 值当成 0，固定放在数组下标 0 的位置。但 ConcurrentHashMap 不允许 null key，因为多线程场景下无法区分&quot;key 不存在&quot;和&quot;key 存在但 value 是 null&quot;。&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;my-anchor&quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;h1&gt;3.解释equals和hashcode的关系&lt;/h1&gt;
&lt;p&gt;契约规定：equals 相等的对象，hashCode 必须相等；但hashCode 相等的对象，equals 不一定相等。先比较hashcode后比较equals。&lt;/p&gt;
&lt;p&gt;这条规则在使用 HashMap、HashSet 这类基于哈希的集合时特别重要。这些集合判断两个对象是否相同，先比 hashCode，hashCode 不等直接认为是两个不同对象，根本不会调 equals。所以如果只重写 equals 不重写 hashCode，集合就废了。&lt;/p&gt;
&lt;p&gt;默认的hashcode返回对象的内存地址，equals默认比较的是引用地址，默认是==&lt;/p&gt;
&lt;p&gt;重写可以用lombok自动生成。&lt;/p&gt;
&lt;p&gt;提问：HashMap 的 key 用可变对象有什么风险？
回答：如果 key 是可变对象，存进去之后又改了参与 hashCode 计算的字段，那这个 entry 就再也找不到了。因为修改后 hashCode 变了，HashMap 会到新的槽位去找，但数据还在老槽位，get 返回 null，remove 也删不掉，造成内存泄漏。所以 HashMap 的 key 最好用不可变对象，String、Integer 这些都可以，自定义类当 key 要特别小心。&lt;/p&gt;
&lt;p&gt;重写 equals 不重写 hashCode，会出现equals相同但hashcode不同，可能会导致业务逻辑上重复的元素被重复添加，查找找不到，例如key是对象的情况，查找的凭据对象是新new的。&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h1&gt;参考资料&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mianshiya.com/&quot;&gt;面试鸭&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>mq学习过程中的疑问复盘</title><link>http://72.155.89.172/posts/mq%E5%AD%A6%E4%B9%A0%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E7%96%91%E9%97%AE%E5%A4%8D%E7%9B%98/</link><guid isPermaLink="true">http://72.155.89.172/posts/mq%E5%AD%A6%E4%B9%A0%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E7%96%91%E9%97%AE%E5%A4%8D%E7%9B%98/</guid><pubDate>Fri, 18 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[x] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;本次对话复盘总结&lt;/h1&gt;
&lt;p&gt;本次对话围绕 Java / Spring AMQP 中的几个核心概念展开：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;匿名内部类与嵌套接口&lt;/li&gt;
&lt;li&gt;Lambda 表达式&lt;/li&gt;
&lt;li&gt;Java Future&lt;/li&gt;
&lt;li&gt;SLF4J 日志占位符与异常处理&lt;/li&gt;
&lt;li&gt;RabbitMQ ReturnCallback 触发机制&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;1. &lt;code&gt;ReturnsCallback&lt;/code&gt; 是类还是方法？为什么里面还能有方法？&lt;/h2&gt;
&lt;h3&gt;结论&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;RabbitTemplate.ReturnsCallback&lt;/code&gt; 是一个&lt;strong&gt;接口&lt;/strong&gt;，不是方法。&lt;br /&gt;
&lt;code&gt;setReturnsCallback(...)&lt;/code&gt; 才是方法。&lt;/p&gt;
&lt;h3&gt;代码拆解&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        // ...
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;部分&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setReturnsCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RabbitTemplate 的方法，用于设置回调&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RabbitTemplate.ReturnsCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义在 RabbitTemplate 内部的接口&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;returnedMessage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;接口中的抽象方法，此处被实现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new ... { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;匿名内部类，创建接口的实现类对象&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;关键点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new 接口() { ... }&lt;/code&gt; 是&lt;strong&gt;匿名内部类&lt;/strong&gt;语法。&lt;/li&gt;
&lt;li&gt;大括号里写的是接口方法的实现，所以“里面还能有方法”很正常。&lt;/li&gt;
&lt;li&gt;等价于先定义一个实现类，再 &lt;code&gt;new&lt;/code&gt; 它。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;2. 类里面的接口（嵌套接口）&lt;/h2&gt;
&lt;h3&gt;结论&lt;/h3&gt;
&lt;p&gt;Java 允许在类里面定义接口，称为&lt;strong&gt;成员接口 / 嵌套接口&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class RabbitTemplate {
    public interface ReturnsCallback {
        void returnedMessage(ReturnedMessage returned);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;特点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;默认隐式 &lt;code&gt;static&lt;/code&gt;&lt;/strong&gt;&lt;br /&gt;
即使不写 &lt;code&gt;static&lt;/code&gt;，成员接口也是静态的，不依赖外部类对象。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;访问需要带外部类名&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RabbitTemplate.ReturnsCallback
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编译后生成 &lt;code&gt;外部类$内部接口.class&lt;/code&gt;&lt;/strong&gt;&lt;br /&gt;
例如 &lt;code&gt;RabbitTemplate$ReturnsCallback.class&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;设计好处&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;归属清晰：一看就知道属于 &lt;code&gt;RabbitTemplate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;避免命名污染&lt;/li&gt;
&lt;li&gt;方便组织相关代码&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;常见例子&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Map.Entry&lt;/code&gt; 就是定义在 &lt;code&gt;Map&lt;/code&gt; 接口内部的嵌套接口。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;3. 为什么可以用 Lambda 表达式？&lt;/h2&gt;
&lt;h3&gt;结论&lt;/h3&gt;
&lt;p&gt;因为 &lt;code&gt;ReturnsCallback&lt;/code&gt; 是&lt;strong&gt;函数式接口&lt;/strong&gt;——只有一个抽象方法。&lt;br /&gt;
Java 8 规定：函数式接口可以用 Lambda 创建实例。&lt;/p&gt;
&lt;h3&gt;函数式接口定义&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public interface ReturnsCallback {
    void returnedMessage(ReturnedMessage returned); // 唯一抽象方法
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;匿名内部类 → Lambda&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 匿名内部类
new RabbitTemplate.ReturnsCallback() {
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        log.error(&quot;触发return callback,&quot;);
        log.debug(&quot;exchange: {}&quot;, returned.getExchange());
    }
}

// Lambda
returned -&amp;gt; {
    log.error(&quot;触发return callback,&quot;);
    log.debug(&quot;exchange: {}&quot;, returned.getExchange());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;原理：目标类型推断&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;setReturnsCallback(ReturnsCallback callback)&lt;/code&gt; 需要 &lt;code&gt;ReturnsCallback&lt;/code&gt; 类型，&lt;br /&gt;
编译器知道该接口只有一个抽象方法，因此能推断出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;参数类型是 &lt;code&gt;ReturnedMessage&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;方法体就是 Lambda 的 &lt;code&gt;{ ... }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;注意&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Lambda 和匿名内部类不完全等价（&lt;code&gt;this&lt;/code&gt; 指向不同，底层实现不同）。&lt;/li&gt;
&lt;li&gt;只有&lt;strong&gt;函数式接口&lt;/strong&gt;才能用 Lambda。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;4. Java 的 Future 是什么？&lt;/h2&gt;
&lt;h3&gt;一句话&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Future&lt;/code&gt; 是 &lt;code&gt;java.util.concurrent&lt;/code&gt; 下的接口，表示&lt;strong&gt;异步任务的未来结果&lt;/strong&gt;，类似“取餐号”。&lt;/p&gt;
&lt;h3&gt;核心方法&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;阻塞等待结果&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get(timeout, unit)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;超时等待&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isDone()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;任务是否完成&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isCancelled()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;是否取消&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cancel(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;尝试取消&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;基本用法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ExecutorService pool = Executors.newFixedThreadPool(2);

Future&amp;lt;Integer&amp;gt; future = pool.submit(() -&amp;gt; {
    Thread.sleep(1000);
    return 42;
});

// 可以先做别的事
Integer result = future.get(); // 阻塞取结果
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;局限性&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;获取结果只能阻塞或轮询。&lt;/li&gt;
&lt;li&gt;不方便组合多个 Future。&lt;/li&gt;
&lt;li&gt;没有回调机制。&lt;/li&gt;
&lt;li&gt;异常处理麻烦（&lt;code&gt;ExecutionException&lt;/code&gt;）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;增强版：CompletableFuture&lt;/h3&gt;
&lt;p&gt;支持链式调用、组合、回调、手动完成、更好的异常处理。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CompletableFuture&amp;lt;Integer&amp;gt; cf = CompletableFuture.supplyAsync(() -&amp;gt; 42);
cf.thenAccept(r -&amp;gt; System.out.println(&quot;结果：&quot; + r));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;FutureTask&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;FutureTask&lt;/code&gt; 同时实现 &lt;code&gt;Runnable&lt;/code&gt; 和 &lt;code&gt;Future&lt;/code&gt;，可交给线程执行并取结果。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;5. &lt;code&gt;log.error&lt;/code&gt; 为什么第一个没有 &lt;code&gt;{}&lt;/code&gt; 占位符？&lt;/h2&gt;
&lt;h3&gt;结论&lt;/h3&gt;
&lt;p&gt;因为 SLF4J 对 &lt;strong&gt;&lt;code&gt;Throwable&lt;/code&gt;（异常）&lt;/strong&gt; 有特殊处理：&lt;br /&gt;
异常作为&lt;strong&gt;最后一个参数&lt;/strong&gt;传入时，会自动打印堆栈，&lt;strong&gt;不需要 &lt;code&gt;{}&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;两条日志对比&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;log.error(&quot;处理确认结果异常&quot;, ex);
// ex 是异常，直接作为最后一个参数，自动打印堆栈

log.error(&quot;发送消息失败，收到 nack, reason : {}&quot;, result.getReason());
// result.getReason() 是普通对象，必须用 {} 占位
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SLF4J 参数匹配规则&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;先看消息中有几个 &lt;code&gt;{}&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果最后一个参数是 &lt;code&gt;Throwable&lt;/code&gt;，且占位符数量少于参数数量，则该异常不参与占位符填充，专门用于打印堆栈。&lt;/li&gt;
&lt;li&gt;剩余参数按顺序填充 &lt;code&gt;{}&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;正确写法&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;写法&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log.error(&quot;消息&quot;, ex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;异常打印堆栈，无需 &lt;code&gt;{}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log.error(&quot;消息 {}&quot;, obj)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;普通对象用 &lt;code&gt;{}&lt;/code&gt; 填充&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log.error(&quot;消息 {}&quot;, ex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;错误！&lt;/strong&gt; 异常被当普通参数，不打印堆栈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log.error(&quot;消息 {} &quot;, msg, ex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;msg&lt;/code&gt; 填充 &lt;code&gt;{}&lt;/code&gt;，&lt;code&gt;ex&lt;/code&gt; 打印堆栈&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;6. ReturnCallback 是怎么触发的？&lt;/h2&gt;
&lt;h3&gt;一句话&lt;/h3&gt;
&lt;p&gt;消息到达 Exchange，但&lt;strong&gt;路由不到任何队列&lt;/strong&gt;，且开启了 &lt;code&gt;mandatory=true&lt;/code&gt;，Broker 会把消息退回，Spring AMQP 收到后触发 &lt;code&gt;ReturnsCallback&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;与 ConfirmCallback 的区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;回调&lt;/th&gt;
&lt;th&gt;触发时机&lt;/th&gt;
&lt;th&gt;关注点&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConfirmCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;消息到达 Exchange（或未到达）&lt;/td&gt;
&lt;td&gt;是否成功到交换机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReturnsCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;到达 Exchange 但路由不到队列&lt;/td&gt;
&lt;td&gt;是否成功进队列&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;触发前提&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;消息到达 Exchange，但路由失败&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exchange 存在&lt;/li&gt;
&lt;li&gt;routingKey 找不到匹配的 Binding&lt;/li&gt;
&lt;li&gt;没有任何队列接收&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;必须开启 &lt;code&gt;mandatory&lt;/code&gt; 或 &lt;code&gt;publisher-returns&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;spring:
  rabbitmq:
    publisher-returns: true
    template:
      mandatory: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rabbitTemplate.setMandatory(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;触发流程&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;生产者 publish
      │
      ▼
消息到达 Exchange
      │
      ├── 能路由到队列 ──► 入队，结束（不触发）
      │
      └── 路由不到队列
              │
              ├── mandatory=false ──► 丢弃，不触发
              │
              └── mandatory=true
                      │
                      ▼
            Broker 退回（Basic.Return）
                      │
                      ▼
        Spring AMQP 回调 ReturnsCallback.returnedMessage()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;ReturnedMessage&lt;/code&gt; 包含&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exchange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;交换机&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;routingKey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;路由键&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;message&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;消息本身&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;replyCode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;退回码，如 &lt;code&gt;312 NO_ROUTE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;replyText&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;退回原因文本&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;常见不触发原因&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;没开 &lt;code&gt;mandatory&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Exchange 不存在（走 ConfirmCallback，ack=false）。&lt;/li&gt;
&lt;li&gt;路由成功，正常入队。&lt;/li&gt;
&lt;li&gt;只用了 &lt;code&gt;convertAndSend&lt;/code&gt; 但没设置 mandatory。&lt;/li&gt;
&lt;li&gt;确认机制没开（实际项目常与 publisher-confirm 一起开）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;典型组合使用&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -&amp;gt; {
    if (ack) {
        log.info(&quot;消息到达交换机成功&quot;);
    } else {
        log.error(&quot;消息到达交换机失败：{}&quot;, cause);
    }
});

rabbitTemplate.setReturnsCallback(returned -&amp;gt; {
    log.error(&quot;消息路由到队列失败：{}&quot;, returned);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;判断逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Confirm ack=true + 无 Return → 成功到交换机且入队。&lt;/li&gt;
&lt;li&gt;Confirm ack=true + 有 Return → 到交换机但路由失败。&lt;/li&gt;
&lt;li&gt;Confirm ack=false → 连交换机都没到。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;7. 核心要点速查&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;主题&lt;/th&gt;
&lt;th&gt;核心结论&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReturnsCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;是接口，不是方法；&lt;code&gt;setReturnsCallback&lt;/code&gt; 才是方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;匿名内部类&lt;/td&gt;
&lt;td&gt;&lt;code&gt;new 接口() { ... }&lt;/code&gt;，就地实现接口方法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;嵌套接口&lt;/td&gt;
&lt;td&gt;类里面可以定义接口，默认 &lt;code&gt;static&lt;/code&gt;，用 &lt;code&gt;外部类.接口&lt;/code&gt; 访问&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;仅函数式接口可用，是匿名内部类的简洁写法&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Future&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;异步任务取餐号；&lt;code&gt;get()&lt;/code&gt; 阻塞取结果；&lt;code&gt;CompletableFuture&lt;/code&gt; 是增强版&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SLF4J 异常&lt;/td&gt;
&lt;td&gt;异常作为最后一个参数自动打印堆栈，不加 &lt;code&gt;{}&lt;/code&gt;；普通参数必须加 &lt;code&gt;{}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReturnCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;消息到 Exchange 但路由失败 + &lt;code&gt;mandatory=true&lt;/code&gt; → Broker 退回 → 触发回调&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConfirmCallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;关注消息是否到达 Exchange；与 ReturnCallback 配合判断消息全链路状态&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;8. 易混淆点提醒&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ReturnsCallback&lt;/code&gt; 是接口，不是方法。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;new 接口() { ... }&lt;/code&gt; 是匿名内部类，不是直接实例化接口。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lambda 只能用于函数式接口（有且仅有一个抽象方法）。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Future.get()&lt;/code&gt; 会阻塞当前线程。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SLF4J 中异常不要加 &lt;code&gt;{}&lt;/code&gt;，否则不打印堆栈。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ReturnCallback 触发的前提是 &lt;code&gt;mandatory=true&lt;/code&gt;，否则消息被静默丢弃。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exchange 不存在时走 ConfirmCallback，不走 ReturnCallback。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;以上为本次对话的完整复盘总结，建议按主题分块复习，重点掌握匿名内部类、函数式接口、SLF4J 异常处理和 RabbitMQ 两个回调的触发条件与区别。&lt;/p&gt;
</content:encoded></item><item><title>微服务（部分）和mq学习日记</title><link>http://72.155.89.172/posts/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%83%A8%E5%88%86%E5%92%8Cmq%E5%AD%A6%E4%B9%A0%E6%97%A5%E8%AE%B0/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E5%BE%AE%E6%9C%8D%E5%8A%A1%E9%83%A8%E5%88%86%E5%92%8Cmq%E5%AD%A6%E4%B9%A0%E6%97%A5%E8%AE%B0/</guid><pubDate>Thu, 17 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[x] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;hmall 微服务项目排错与知识点总结&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;一次完整的踩坑链：网关缺类启动失败 → RabbitMQ 认证/vhost → 购物车 user_id 为 null →
Feign 跨服务丢失用户 → 复制 Maven 模块不识别。
贯穿主题：&lt;strong&gt;依赖作用域、条件装配、用户信息在多次&quot;进程边界&quot;上的显式传递&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;一、问题时间线（每个错误的根因一句话）&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;现象&lt;/th&gt;
&lt;th&gt;根因&lt;/th&gt;
&lt;th&gt;修复手段&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;网关启动 &lt;code&gt;ClassNotFoundException: MessageConverter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;hm-common 的 &lt;code&gt;MqConfig&lt;/code&gt; 被无条件自动装配，但 amqp 依赖是 provided，网关没有该类&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MqConfig&lt;/code&gt; 加 &lt;code&gt;@ConditionalOnClass&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;trade-service 启动 &lt;code&gt;ACCESS_REFUSED ... PLAIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RabbitMQ 不存在 hmall 用户或密码不对&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rabbitmqctl add_user / change_password&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;trade-service 报 &lt;code&gt;530 vhost /hmall not found&lt;/code&gt;（服务能启动但不断重试）&lt;/td&gt;
&lt;td&gt;虚拟主机未创建、未授权&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add_vhost&lt;/code&gt; + &lt;code&gt;set_permissions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;cart-service 加购报 &lt;code&gt;Field &apos;user_id&apos; doesn&apos;t have a default value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;网关只打印 userId 没下传；下游无拦截器，ThreadLocal 为 null&lt;/td&gt;
&lt;td&gt;网关 mutate 加头 + 下游 UserInfoInterceptor + MvcConfig&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;余额支付 Feign 调 user-service 返回 500&lt;/td&gt;
&lt;td&gt;Feign 是全新 HTTP 请求，不会自动透传请求头，下游 UserContext 又为 null&lt;/td&gt;
&lt;td&gt;自定义 &lt;code&gt;RequestInterceptor&lt;/code&gt; 复制 user-info 头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;复制进来的 publisher 文件夹不是模块&lt;/td&gt;
&lt;td&gt;未登记 &lt;code&gt;&amp;lt;modules&amp;gt;&lt;/code&gt;；且子 pom 的 parent 还指向旧项目 mq-demo&lt;/td&gt;
&lt;td&gt;改 parent、补依赖、登记模块、Reload Maven&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;二、Maven 依赖机制&lt;/h2&gt;
&lt;h3&gt;1. classpath（类路径）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JVM 运行时查找所有 .class / jar 的&quot;路径清单&quot;，IDEA 启动命令里的 &lt;code&gt;-classpath a;b;c.jar&lt;/code&gt; 就是它。&lt;/li&gt;
&lt;li&gt;清单里没有的类，运行时就抛 &lt;code&gt;ClassNotFoundException&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每个服务的 classpath 相互独立&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;判断缺类问题的第一现场：在启动命令的 classpath 里搜报错类所在的 jar。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 依赖范围 scope&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;scope&lt;/th&gt;
&lt;th&gt;编译可见&lt;/th&gt;
&lt;th&gt;运行时在 classpath&lt;/th&gt;
&lt;th&gt;向下游传递&lt;/th&gt;
&lt;th&gt;典型用途&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;compile（默认）&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;普通依赖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;provided&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;否&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;否&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;servlet-api（外部 Tomcat 提供）、hm-common 里的 amqp/mybatis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;provided 的语义：&quot;编译时借我看，运行环境会自备，不要打包、不要传递&quot;。&lt;/li&gt;
&lt;li&gt;微服务公共模块中&quot;可选功能&quot;依赖（MQ、数据库、webmvc）应设 provided，由&lt;strong&gt;真正使用的服务自己引入 starter&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. 依赖传递&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;A 依赖 B（compile），B 依赖 C（compile），则 A 的 classpath 自动有 C。&lt;/li&gt;
&lt;li&gt;provided 在传递链上会被&quot;掐断&quot;。&lt;/li&gt;
&lt;li&gt;本项目实际链路：
&lt;ul&gt;
&lt;li&gt;网关 → hm-common，amqp 是 provided → &lt;strong&gt;拿不到 MessageConverter&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;trade/pay-service 自己声明 &lt;code&gt;spring-boot-starter-amqp&lt;/code&gt;（compile）→ starter 传递整套 amqp jar → &lt;strong&gt;正常&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4. 复制外部 Maven 模块的标准手续（三步，缺一不可）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;改子模块 pom 的 &lt;code&gt;&amp;lt;parent&amp;gt;&lt;/code&gt;&lt;/strong&gt;：指向当前聚合工程（从旧项目复制来的 parent 会导致 Non-resolvable parent POM），并补齐原父 pom 继承来的依赖；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在根 pom 的 &lt;code&gt;&amp;lt;modules&amp;gt;&lt;/code&gt; 中登记&lt;/strong&gt; &lt;code&gt;&amp;lt;module&amp;gt;publisher&amp;lt;/module&amp;gt;&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDEA 重新导入&lt;/strong&gt;：Maven 面板刷新，或 pom 右键 Add as Maven Project。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;复制文件 ≠ 加入工程。蓝色方块模块图标 = 已被 Maven 识别。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;三、Spring Boot 自动装配&lt;/h2&gt;
&lt;h3&gt;1. spring.factories&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;路径：&lt;code&gt;src/main/resources/META-INF/spring.factories&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;作用：公共 jar 里的配置类不在使用方的扫描包（&lt;code&gt;@SpringBootApplication&lt;/code&gt; 所在包及子包）内，必须在此&quot;登记户口&quot;才会被自动装配。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.hmall.common.config.MyBatisConfig,\
  com.hmall.common.config.MqConfig,\
  com.hmall.common.config.JsonConfig,\
  com.hmall.common.config.MvcConfig
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;包内的配置类（如 hm-service 单体里的 config）能被直接扫描，不需要登记。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. @ConditionalOnClass —— 可选依赖的标准兜底&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@Configuration
@ConditionalOnClass(MessageConverter.class)
public class MqConfig {
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;含义：括号中的类在 classpath 存在时，整个配置类才生效，否则跳过。&lt;/li&gt;
&lt;li&gt;为什么不会因为引用了缺失类而崩：Spring Boot 用 ASM &lt;strong&gt;先读字节码判断条件，再决定是否加载类&lt;/strong&gt;，类不加载就不会解析其方法签名。&lt;/li&gt;
&lt;li&gt;触发背景：JVM 加载类时必须解析其所有方法的参数/返回值类型；反射 &lt;code&gt;getDeclaredMethods()&lt;/code&gt; 时找不到类型即 &lt;code&gt;NoClassDefFoundError&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;hm-common 的统一套路：&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;配置类&lt;/th&gt;
&lt;th&gt;条件类&lt;/th&gt;
&lt;th&gt;防住谁&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MyBatisConfig&lt;/td&gt;
&lt;td&gt;MybatisPlusInterceptor、BaseMapper&lt;/td&gt;
&lt;td&gt;无数据库服务/网关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MqConfig&lt;/td&gt;
&lt;td&gt;MessageConverter&lt;/td&gt;
&lt;td&gt;无 MQ 服务/网关&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MvcConfig&lt;/td&gt;
&lt;td&gt;DispatcherServlet、HttpServletRequest&lt;/td&gt;
&lt;td&gt;WebFlux 网关（无 Servlet API）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;经验法则：&lt;strong&gt;写进 spring.factories 的配置类，只要引用了 provided 依赖的类型，就必须配 &lt;code&gt;@ConditionalOnClass&lt;/code&gt;。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;四、RabbitMQ 排错三连&lt;/h2&gt;
&lt;p&gt;连接认证是分步的，看错误码区分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;TCP 不通：连接超时/拒绝（查容器、端口、防火墙）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户名密码错&lt;/strong&gt;：&lt;code&gt;ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;vhost 不存在&lt;/strong&gt;：&lt;code&gt;reply-code=530, NOT_ALLOWED - vhost /hmall not found&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;vhost 无权限：&lt;code&gt;access to vhost ... refused&lt;/code&gt;（三道关卡的最后一道）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;常用命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 用户
docker exec -it mq rabbitmqctl add_user hmall 123
docker exec -it mq rabbitmqctl change_password hmall 123
docker exec -it mq rabbitmqctl list_users
# 虚拟主机
docker exec -it mq rabbitmqctl add_vhost /hmall
docker exec -it mq rabbitmqctl list_vhosts
# 授权：configure / write / read 三个正则
docker exec -it mq rabbitmqctl set_permissions -p /hmall hmall &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
docker exec -it mq rabbitmqctl list_permissions -p /hmall
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Socket closed&lt;/code&gt; 多为服务端拒绝后主动关闭，不一定是网络问题。&lt;/li&gt;
&lt;li&gt;guest 默认只能 localhost 登录，跨机访问必须自建用户。&lt;/li&gt;
&lt;li&gt;vhost 类似 MySQL 的 database：逻辑隔离队列/交换机/权限。&lt;/li&gt;
&lt;li&gt;监听容器行为差异：认证失败=致命错误，启动直接失败；vhost/网络故障=可恢复错误，应用启动成功但后台每 5 秒重试（功能仍不可用）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;五、ThreadLocal 与用户信息透传&lt;/h2&gt;
&lt;h3&gt;1. UserContext&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public class UserContext {
    private static final ThreadLocal&amp;lt;Long&amp;gt; tl = new ThreadLocal&amp;lt;&amp;gt;();

    public static void setUser(Long userId) { tl.set(userId); }
    public static Long getUser() { return tl.get(); }
    public static void removeUser() { tl.remove(); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;作用域：&lt;strong&gt;同一 JVM + 同一线程&lt;/strong&gt;，本质是线程私有 Map。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;afterCompletion&lt;/code&gt; 必须 &lt;code&gt;remove()&lt;/code&gt;：Tomcat 线程池复用线程，不清理会&quot;用户串号&quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 核心原则&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;ThreadLocal 跨不了进程。凡是跨进程边界（HTTP、MQ），用户信息都必须&lt;strong&gt;显式搭车传输&lt;/strong&gt;，每一跳都要有人搬运；HTTP 协议本身无状态，框架不会自动把头传给&quot;下一个请求&quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3. 用户信息的完整传递链&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;浏览器 --JWT--&amp;gt; 网关验签得到 userId
网关 --(加 user-info 请求头)--&amp;gt; 微服务
微服务拦截器读头 -&amp;gt; UserContext.setUser()
微服务A --Feign 新 HTTP 请求(需 RequestInterceptor 复制头)--&amp;gt; 微服务B
微服务B 拦截器读头 -&amp;gt; 重建 UserContext
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 第一环：网关注入请求头（WebFlux，对象不可变）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 把 userId 放入名为 user-info 的请求头
ServerHttpRequest newRequest = request.mutate()
        .header(&quot;user-info&quot;, userId.toString())
        .build();
ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();
return chain.filter(newExchange);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;WebFlux 的 request/exchange 是&lt;strong&gt;不可变对象&lt;/strong&gt;（响应式并发安全），没有 set 方法。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mutate()&lt;/code&gt; = 建造者模式：以原对象为蓝本复制 → 修改 → &lt;code&gt;build()&lt;/code&gt; 产出新对象，原对象不变（类比 String）。&lt;/li&gt;
&lt;li&gt;exchange 是请求在网关中的上下文容器，&lt;code&gt;chain.filter()&lt;/code&gt; 只收 exchange，所以要把新 request 装回新 exchange。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5. 第二环：下游微服务接收（Spring MVC 拦截器）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public class UserInfoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 1.从请求头取网关传来的用户信息
        String userInfo = request.getHeader(&quot;user-info&quot;);
        // 2.非空则存入 ThreadLocal
        if (StrUtil.isNotBlank(userInfo)) {
            UserContext.setUser(Long.valueOf(userInfo));
        }
        // 3.放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) {
        // 4.请求结束清理，避免线程池复用导致用户串号
        UserContext.removeUser();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. MvcConfig：注册拦截器&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@Configuration
@ConditionalOnClass({DispatcherServlet.class, HttpServletRequest.class})
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WebMvcConfigurer&lt;/code&gt;：Spring MVC 留给开发者的扩展插槽，重写回调即可定制默认配置。&lt;/li&gt;
&lt;li&gt;拦截器在 Controller 之前执行，先把 userId 放入 ThreadLocal，Service 层才取得到。&lt;/li&gt;
&lt;li&gt;放 hm-common + 登记 spring.factories → 所有微服务复用；&lt;code&gt;@ConditionalOnClass&lt;/code&gt; 防网关（WebFlux 无 DispatcherServlet）缺类崩溃。&lt;/li&gt;
&lt;li&gt;执行顺序：&lt;code&gt;preHandle → Controller/Service → afterCompletion&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;7. 第三环：Feign 跨服务透传&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;@Bean
public RequestInterceptor userInfoRequestInterceptor() {
    return template -&amp;gt; {
        // 1.拿到当前线程正在处理的&quot;入站请求&quot;
        ServletRequestAttributes attrs =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attrs == null) {
            // 没有请求上下文（MQ 监听器、定时任务等异步线程），直接跳过
            return;
        }
        // 2.从入站请求头里取出网关注入的 user-info
        String userId = attrs.getRequest().getHeader(&quot;user-info&quot;);
        // 3.写入 Feign 即将发出的&quot;出站请求&quot;头里
        if (StrUtil.isNotBlank(userId)) {
            template.header(&quot;user-info&quot;, userId);
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Feign 是通用 HTTP 客户端，每次调用构造&lt;strong&gt;全新请求&lt;/strong&gt;，默认不复制入站请求头。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RequestContextHolder&lt;/code&gt;：Spring MVC 基于 ThreadLocal 持有&quot;当前入站请求&quot;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;attrs == null&lt;/code&gt; 判断：异步线程（MQ、&lt;code&gt;@Async&lt;/code&gt;、线程池）没有入站请求。&lt;/li&gt;
&lt;li&gt;配置位置：hm-api 的 &lt;code&gt;DefaultFeignConfig&lt;/code&gt;（经 &lt;code&gt;@EnableFeignClients(defaultConfiguration=...)&lt;/code&gt; 全局生效）。&lt;/li&gt;
&lt;li&gt;hm-api 若编译找不到 &lt;code&gt;HttpServletRequest&lt;/code&gt;，加 provided 的 &lt;code&gt;tomcat-embed-core&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;推论：&lt;strong&gt;MQ 消息不透传用户身份&lt;/strong&gt;（收发时间/线程完全不同），需要用户时从业务数据（如订单的 bizUserId）取。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;六、异常栈（Stack Trace）排查方法论&lt;/h2&gt;
&lt;p&gt;固定四步：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;找根因&lt;/strong&gt;：搜 &lt;code&gt;with root cause&lt;/code&gt; / 最底层 &lt;code&gt;Caused by&lt;/code&gt;，读那句&quot;人话&quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;看现场&lt;/strong&gt;：MyBatis 日志的 &lt;code&gt;Preparing&lt;/code&gt;（SQL）+ &lt;code&gt;Parameters&lt;/code&gt;（实际参数），定位什么数据不对。
&lt;ul&gt;
&lt;li&gt;例：INSERT 列清单缺 user_id + 查询参数是 null → 上游传值缺失，而非数据库问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定位代码&lt;/strong&gt;：栈中只找&lt;strong&gt;自己的包名&lt;/strong&gt;（com.hmall.&lt;em&gt;），框架包（org.springframework.&lt;/em&gt; 等）跳过；
IDEA 中 Ctrl+点击蓝色链接，或 Ctrl+G 输行号。
&lt;ul&gt;
&lt;li&gt;注意区分：&quot;异常炸点行&quot;（如 save()）≠&quot;错误数据出生地&quot;（如 getUser() 返回 null），要带着问题向上追。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反向追问边界&lt;/strong&gt;：这个值谁应该赋值？沿调用链一直追到断链处（网关头丢失 / Feign 未透传）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;微服务特有原则：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;调用方日志里的 FeignException/500 只说明&quot;对方挂了&quot;，&lt;strong&gt;真正的异常栈要去被调服务的控制台找&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其他技巧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;异常栈从上往下是&quot;抛出点→调用者&quot;，从下往上是请求实际流动方向。&lt;/li&gt;
&lt;li&gt;看到光秃秃的 Spring 默认错误 JSON（timestamp/status/error/path）而非统一返回，通常是全局异常处理器 &lt;code&gt;@RestControllerAdvice&lt;/code&gt; 未被扫描/未注册 spring.factories。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;七、其他零散知识点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;spring.factories 自动配置 vs 组件扫描&lt;/strong&gt;：前者管 jar 包外配置类，后者管启动包子包。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@Configuration&lt;/strong&gt;：声明配置类；但包外的配置类单靠它不会被发现，仍需 spring.factories。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebFlux(网关) 与 WebMVC(业务服务) 不能混&lt;/strong&gt;：网关 classpath 有 spring-webflux，加载 servlet 相关类会失败——这也是 hm-common 所有配置类都要条件化的原因。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MyBatis-Plus 默认 NOT_NULL 插入策略&lt;/strong&gt;：值为 null 的字段不进 INSERT 列清单，数据库 NOT NULL 无默认值时即报错。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feign 调试&lt;/strong&gt;：&lt;code&gt;Logger.Level.FULL&lt;/code&gt; 可打印完整请求/响应（头、行、体），跨服务排错利器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改公共模块后必须 Rebuild / mvn install&lt;/strong&gt;：下游运行时用的是 jar/target 编译产物。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;八、本次涉及的关键文件清单&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文件&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hm-common/.../config/MqConfig.java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;RabbitMQ 消息转换器，加 @ConditionalOnClass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hm-common/.../config/MvcConfig.java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;注册 UserInfoInterceptor（新建）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hm-common/.../interceptor/UserInfoInterceptor.java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;下游读取 user-info 头入 ThreadLocal（新建）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hm-common/resources/META-INF/spring.factories&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;公共自动装配登记处&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hm-gateway/.../filters/AuthGlobalFilter.java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JWT 验签 + mutate 透传 user-info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hm-api/.../config/DefaultFeignConfig.java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Feign 日志级别 + 请求头透传拦截器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cart-service/.../CartServiceImpl.java&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;修复硬编码 userId=1 的 TODO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;publisher/pom.xml&lt;/code&gt;、根 &lt;code&gt;pom.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;外部模块迁入：改 parent、补依赖、登记 modules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;九、一句话总纲&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;公共模块靠&quot;provided 依赖 + @ConditionalOnClass + spring.factories&quot;做到按需装配；
用户身份靠 ThreadLocal 在进程内保存、靠 HTTP 请求头在每个进程边界被显式搬运（网关 mutate、MVC 拦截器、Feign RequestInterceptor 各管一跳）；
排错时先找 root cause 与 SQL 参数，再沿自己的包名追代码，跨服务就去对方日志里找真相。&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>python变量常量命令规范</title><link>http://72.155.89.172/posts/python%E5%8F%98%E9%87%8F%E5%B8%B8%E9%87%8F%E5%91%BD%E4%BB%A4%E8%A7%84%E8%8C%83/</link><guid isPermaLink="true">http://72.155.89.172/posts/python%E5%8F%98%E9%87%8F%E5%B8%B8%E9%87%8F%E5%91%BD%E4%BB%A4%E8%A7%84%E8%8C%83/</guid><description>本文主要讲解了python的变量常量的命名规范和魔术方法与特殊变量的介绍</description><pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[x] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;PEP 8 命名规范总表&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类型&lt;/th&gt;
&lt;th&gt;推荐风格&lt;/th&gt;
&lt;th&gt;示例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;变量&lt;/td&gt;
&lt;td&gt;小写 + 下划线&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_name&lt;/code&gt;, &lt;code&gt;total_count&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;常量&lt;/td&gt;
&lt;td&gt;全大写 + 下划线&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MAX_RETRY&lt;/code&gt;, &lt;code&gt;DATABASE_URL&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;函数&lt;/td&gt;
&lt;td&gt;小写 + 下划线&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get_user()&lt;/code&gt;, &lt;code&gt;calc_total()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;方法&lt;/td&gt;
&lt;td&gt;小写 + 下划线&lt;/td&gt;
&lt;td&gt;&lt;code&gt;save()&lt;/code&gt;, &lt;code&gt;to_dict()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;类&lt;/td&gt;
&lt;td&gt;大驼峰&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UserInfo&lt;/code&gt;, &lt;code&gt;HttpClient&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;异常&lt;/td&gt;
&lt;td&gt;大驼峰，常用 &lt;code&gt;Error&lt;/code&gt; 结尾&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ValueError&lt;/code&gt;, &lt;code&gt;LoginError&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模块&lt;/td&gt;
&lt;td&gt;小写 + 下划线，短小&lt;/td&gt;
&lt;td&gt;&lt;code&gt;utils.py&lt;/code&gt;, &lt;code&gt;data_loader.py&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;包&lt;/td&gt;
&lt;td&gt;全小写，尽量不用下划线&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mypackage&lt;/code&gt;, &lt;code&gt;httptools&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;类型变量&lt;/td&gt;
&lt;td&gt;大驼峰或单大写&lt;/td&gt;
&lt;td&gt;&lt;code&gt;T&lt;/code&gt;, &lt;code&gt;AnyStr&lt;/code&gt;, &lt;code&gt;UserType&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;私有成员&lt;/td&gt;
&lt;td&gt;单下划线开头&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_internal&lt;/code&gt;, &lt;code&gt;_cache&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名称改写&lt;/td&gt;
&lt;td&gt;双下划线开头&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__secret&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;魔术方法/特殊变量&lt;/td&gt;
&lt;td&gt;双下划线开头和结尾&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__init__&lt;/code&gt;, &lt;code&gt;__name__&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1&gt;魔术方法与特殊变量&lt;/h1&gt;
&lt;h2&gt;一、魔术方法&lt;/h2&gt;
&lt;p&gt;魔术方法：以 &lt;code&gt;__&lt;/code&gt; 开头和结尾，由 Python 自动调用，不要自己直接调用，也不要自创。&lt;/p&gt;
&lt;h3&gt;常用分类&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;类别&lt;/th&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;触发场景&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;生命周期&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__new__&lt;/code&gt; / &lt;code&gt;__init__&lt;/code&gt; / &lt;code&gt;__del__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;创建、初始化、销毁&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;字符串&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__str__&lt;/code&gt; / &lt;code&gt;__repr__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;print()&lt;/code&gt; / 调试表示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;比较哈希&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__eq__&lt;/code&gt; / &lt;code&gt;__hash__&lt;/code&gt; / &lt;code&gt;__bool__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt; / &lt;code&gt;hash()&lt;/code&gt; / &lt;code&gt;bool()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;容器迭代&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__len__&lt;/code&gt; / &lt;code&gt;__getitem__&lt;/code&gt; / &lt;code&gt;__setitem__&lt;/code&gt; / &lt;code&gt;__iter__&lt;/code&gt; / &lt;code&gt;__next__&lt;/code&gt; / &lt;code&gt;__contains__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;len()&lt;/code&gt; / 索引 / &lt;code&gt;for&lt;/code&gt; / &lt;code&gt;in&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;属性访问&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__getattr__&lt;/code&gt; / &lt;code&gt;__setattr__&lt;/code&gt; / &lt;code&gt;__delattr__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;属性不存在 / 设置 / 删除&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;调用上下文&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__call__&lt;/code&gt; / &lt;code&gt;__enter__&lt;/code&gt; / &lt;code&gt;__exit__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;obj()&lt;/code&gt; / &lt;code&gt;with&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;运算符&lt;/td&gt;
&lt;td&gt;&lt;code&gt;__add__&lt;/code&gt; / &lt;code&gt;__radd__&lt;/code&gt; / &lt;code&gt;__iadd__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt; / 右加 / &lt;code&gt;+=&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class User:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f&quot;用户：{self.name}&quot;

    def __repr__(self):
        return f&quot;User(name={self.name!r})&quot;

    def __eq__(self, other):
        return isinstance(other, User) and self.name == other.name

    def __hash__(self):
        return hash(self.name)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;特殊变量&lt;/h1&gt;
&lt;p&gt;特殊变量由 Python 自动维护，通常不要随意覆盖。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;变量&lt;/th&gt;
&lt;th&gt;含义&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__name__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;模块名；直接运行时为 &lt;code&gt;&quot;__main__&quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__file__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;当前模块文件路径&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__doc__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;文档字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__dict__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;模块 / 类 / 实例的命名空间字典&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__class__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;实例所属类&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__bases__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;直接基类元组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__mro__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;方法解析顺序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__all__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;from module import *&lt;/code&gt; 时导出的名字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__slots__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;限制实例属性，节省内存&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__version__&lt;/code&gt; / &lt;code&gt;__author__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;约定俗成的包信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;示例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# main.py
def main():
    print(&quot;运行主程序&quot;)

if __name__ == &quot;__main__&quot;:
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;class User:
    __slots__ = (&quot;name&quot;, &quot;age&quot;)

    def __init__(self, name, age):
        self.name = name
        self.age = age
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;要点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if __name__ == &quot;__main__&quot;:&lt;/code&gt; 用于保护脚本入口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;__all__&lt;/code&gt; 控制 &lt;code&gt;import *&lt;/code&gt; 的导出内容。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;__slots__&lt;/code&gt; 会阻止动态添加属性。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;__version__&lt;/code&gt;、&lt;code&gt;__author__&lt;/code&gt; 不是内置变量，只是社区约定。&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>vscode常用快捷键</title><link>http://72.155.89.172/posts/vscode%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE/</link><guid isPermaLink="true">http://72.155.89.172/posts/vscode%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE/</guid><pubDate>Mon, 14 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[x] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;VS Code 常用快捷键（Windows）&lt;/h1&gt;
&lt;h2&gt;最常用&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + P&lt;/code&gt;：命令面板&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + P&lt;/code&gt;：快速打开文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + F&lt;/code&gt;：全局搜索&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + 反引号&lt;/code&gt;：打开/关闭终端&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + B&lt;/code&gt;：打开/关闭侧边栏&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + /&lt;/code&gt;：行注释/取消注释&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Alt + ↑ / ↓&lt;/code&gt;：向上/向下移动当前行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + Alt + ↑ / ↓&lt;/code&gt;：向上/向下复制当前行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + D&lt;/code&gt;：选中下一个相同内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F12&lt;/code&gt;：转到定义&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + Alt + F&lt;/code&gt;：格式化整个文档&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + S&lt;/code&gt;：保存&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;编辑操作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + X&lt;/code&gt;：剪切&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + C&lt;/code&gt;：复制&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + V&lt;/code&gt;：粘贴&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Z&lt;/code&gt;：撤销&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Y&lt;/code&gt;：重做&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + F&lt;/code&gt;：当前文件查找&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + H&lt;/code&gt;：当前文件替换&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + G&lt;/code&gt;：跳转到指定行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + K&lt;/code&gt;：删除当前行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Enter&lt;/code&gt;：在当前行下方插入新行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + Enter&lt;/code&gt;：在当前行上方插入新行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + ]&lt;/code&gt;：增加缩进&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + [&lt;/code&gt;：减少缩进&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + Alt + A&lt;/code&gt;：块注释&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Backspace&lt;/code&gt;：删除前一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Delete&lt;/code&gt;：删除后一个单词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Home&lt;/code&gt;：行首&lt;/li&gt;
&lt;li&gt;&lt;code&gt;End&lt;/code&gt;：行尾&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Home&lt;/code&gt;：文件开头&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + End&lt;/code&gt;：文件结尾&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;多光标&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Alt + 鼠标点击&lt;/code&gt;：添加多个光标&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Alt + ↑ / ↓&lt;/code&gt;：向上/向下添加光标&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + D&lt;/code&gt;：选中下一个相同内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + L&lt;/code&gt;：选中所有相同内容&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + U&lt;/code&gt;：撤销上一次光标操作&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;导航与跳转&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + P&lt;/code&gt;：快速打开文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + O&lt;/code&gt;：跳转到文件内符号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + T&lt;/code&gt;：搜索工作区符号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F12&lt;/code&gt;：转到定义&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Alt + F12&lt;/code&gt;：预览定义&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + F12&lt;/code&gt;：查看所有引用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F2&lt;/code&gt;：重命名符号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Tab&lt;/code&gt;：切换打开的文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + PageUp&lt;/code&gt;：切换到上一个标签页&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + PageDown&lt;/code&gt;：切换到下一个标签页&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + 1 / 2 / 3&lt;/code&gt;：聚焦第 1/2/3 个编辑器组&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + \&lt;/code&gt;：跳转到匹配括号&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;搜索与替换&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + F&lt;/code&gt;：当前文件查找&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + H&lt;/code&gt;：当前文件替换&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F3&lt;/code&gt;：下一个匹配&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + F3&lt;/code&gt;：上一个匹配&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + F&lt;/code&gt;：全局搜索&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + H&lt;/code&gt;：全局替换&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;视图与面板&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + B&lt;/code&gt;：切换侧边栏&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + E&lt;/code&gt;：资源管理器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + F&lt;/code&gt;：搜索面板&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + G&lt;/code&gt;：源代码管理&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + D&lt;/code&gt;：运行和调试&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + X&lt;/code&gt;：扩展&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + 反引号&lt;/code&gt;：终端&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + 反引号&lt;/code&gt;：新建终端&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + J&lt;/code&gt;：切换底部面板&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + K Z&lt;/code&gt;：禅模式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Esc Esc&lt;/code&gt;：退出禅模式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F11&lt;/code&gt;：全屏&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;文件操作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + N&lt;/code&gt;：新建文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + O&lt;/code&gt;：打开文件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + S&lt;/code&gt;：保存&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + K S&lt;/code&gt;：全部保存&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + S&lt;/code&gt;：另存为&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + W&lt;/code&gt;：关闭当前编辑器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + K W&lt;/code&gt;：关闭所有编辑器&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + T&lt;/code&gt;：重新打开刚关闭的文件&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;调试&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;F5&lt;/code&gt;：启动/继续&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + F5&lt;/code&gt;：停止调试&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F9&lt;/code&gt;：切换断点&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F10&lt;/code&gt;：单步跳过&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F11&lt;/code&gt;：单步进入&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Shift + F11&lt;/code&gt;：单步跳出&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + F5&lt;/code&gt;：重启调试&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;格式化与智能提示&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Shift + Alt + F&lt;/code&gt;：格式化整个文档&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + K Ctrl + F&lt;/code&gt;：格式化选中部分&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Space&lt;/code&gt;：触发建议&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + Space&lt;/code&gt;：参数提示&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + .&lt;/code&gt;：快速修复&lt;/li&gt;
&lt;li&gt;&lt;code&gt;F2&lt;/code&gt;：重命名符号&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;其他实用&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl + ,&lt;/code&gt;：打开设置&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + K Ctrl + S&lt;/code&gt;：打开键盘快捷方式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + K Ctrl + T&lt;/code&gt;：切换主题&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ctrl + Shift + P&lt;/code&gt;：命令面板&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>java集合</title><link>http://72.155.89.172/posts/java%E9%9B%86%E5%90%88/</link><guid isPermaLink="true">http://72.155.89.172/posts/java%E9%9B%86%E5%90%88/</guid><description>bg：最近刷刷法老是遇到不会写的集合，于是在这里借助ai进行弱点集中突破</description><pubDate>Sat, 05 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[x] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Java 常用集合与 API 详解&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;面向 Java 后端面试与日常开发的集合框架系统性整理文档。内容涵盖 &lt;code&gt;Collection&lt;/code&gt; 与 &lt;code&gt;Map&lt;/code&gt; 两大体系下的核心实现类、底层数据结构、关键 API、源码级原理、性能对比与并发场景实践，并补充 Java 8 引入的 &lt;code&gt;Stream&lt;/code&gt; API 与 &lt;code&gt;java.util.concurrent&lt;/code&gt; 包下的并发集合。&lt;strong&gt;对应 JDK 版本以 JDK 8 / 11 / 17 为主&lt;/strong&gt;，关键差异会单独标注。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;一、Java 集合框架总览&lt;/h2&gt;
&lt;h3&gt;1.1 整体架构&lt;/h3&gt;
&lt;p&gt;Java 集合框架（Java Collections Framework, JCF）位于 &lt;code&gt;java.util&lt;/code&gt; 包下，提供了一套统一的接口与实现，让数据的存储、检索、操作可复用、可互操作。整体结构可分为&lt;strong&gt;两大根接口&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.util.Collection&lt;/code&gt;：存储&lt;strong&gt;单列数据&lt;/strong&gt;，每个元素独立存在
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;List&lt;/code&gt;：有序、可重复&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Set&lt;/code&gt;：无序（部分实现有序）、不可重复&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Queue / Deque&lt;/code&gt;：队列与双端队列&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.util.Map&lt;/code&gt;：存储&lt;strong&gt;双列数据&lt;/strong&gt;（key-value 键值对）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;                         Iterable&amp;lt;E&amp;gt;
                             │
                       Collection&amp;lt;E&amp;gt;
              ┌──────────────┼────────────────────┐
            List&amp;lt;E&amp;gt;       Set&amp;lt;E&amp;gt;            Queue&amp;lt;E&amp;gt;
              │             │                  │
       ┌──────┼──────┐   ┌──┼──┐          ┌───┼────┐
   ArrayList  LinkedList HashSet TreeSet  PriorityQueue Deque
       Vector  Stack     LinkedHashSet  ArrayDeque
                                  │
                                  └──&amp;gt; NavigableSet -&amp;gt; SortedSet
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;                                Map&amp;lt;K,V&amp;gt;
                                  │
                ┌─────────────────┼─────────────────┐
            HashMap           SortedMap          Hashtable
                │                │                  │
        LinkedHashMap        TreeMap          Properties
                │
        WeakHashMap / IdentityHashMap
                │
        ConcurrentMap
                │
        ConcurrentHashMap
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 接口规范与设计思想&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;设计原则&lt;/th&gt;
&lt;th&gt;体现&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;面向接口编程&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;业务代码应面向 &lt;code&gt;List&lt;/code&gt; / &lt;code&gt;Map&lt;/code&gt; 等接口编程，而不是具体实现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;可选操作（Optional Operation）&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;接口中部分方法如 &lt;code&gt;add(int, E)&lt;/code&gt; 标注 &lt;code&gt;UnsupportedOperationException&lt;/code&gt;，未实现时可抛出（不可变集合常用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;equals / hashCode 契约&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Set&lt;/code&gt; 与 &lt;code&gt;Map&lt;/code&gt; 的 key 要求正确实现这俩方法，否则去重与查找失效&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fail-fast 机制&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;多数集合在迭代过程中检测到结构性修改会抛 &lt;code&gt;ConcurrentModificationException&lt;/code&gt;（&lt;code&gt;ConcurrentHashMap&lt;/code&gt; 等是 fail-safe）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;1.3 包路径速查&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;包&lt;/th&gt;
&lt;th&gt;主要内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.util&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;集合框架主体（&lt;code&gt;List&lt;/code&gt; / &lt;code&gt;Set&lt;/code&gt; / &lt;code&gt;Map&lt;/code&gt; / &lt;code&gt;Queue&lt;/code&gt; / 工具类）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.util.concurrent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;并发集合（&lt;code&gt;ConcurrentHashMap&lt;/code&gt; / &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt; / &lt;code&gt;BlockingQueue&lt;/code&gt; 等）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.util.concurrent.atomic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;原子类（不属集合但常配合并发集合使用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.util.function&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;函数式接口（&lt;code&gt;Predicate&lt;/code&gt; / &lt;code&gt;Function&lt;/code&gt; / &lt;code&gt;Supplier&lt;/code&gt; 等，配合 Stream 使用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.util.stream&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stream API（&lt;code&gt;Stream&lt;/code&gt; / &lt;code&gt;Collectors&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.util.Collections&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;集合工具类（旧 API）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;1.4 集合与数组的区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;数组&lt;/th&gt;
&lt;th&gt;集合&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;长度&lt;/td&gt;
&lt;td&gt;固定&lt;/td&gt;
&lt;td&gt;动态扩容&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;类型&lt;/td&gt;
&lt;td&gt;可存基本类型与对象&lt;/td&gt;
&lt;td&gt;只能存对象（基本类型需装箱）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;功能&lt;/td&gt;
&lt;td&gt;仅有 length 属性&lt;/td&gt;
&lt;td&gt;提供丰富的 CRUD、批量操作、流式 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;极致紧凑（内存连续）&lt;/td&gt;
&lt;td&gt;一般略低于数组，但接口与实现优化已接近数组&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;1.5 通用遍历方式&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = Arrays.asList(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);

// 1. for 循环（List 专属）
for (int i = 0; i &amp;lt; list.size(); i++) {
    System.out.println(list.get(i));
}

// 2. 增强 for（foreach，本质是 Iterator）
for (String s : list) {
    System.out.println(s);
}

// 3. Iterator（可在遍历中安全 remove）
Iterator&amp;lt;String&amp;gt; it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (&quot;b&quot;.equals(s)) it.remove();
}

// 4. forEach + Lambda（Java 8+）
list.forEach(System.out::println);

// 5. Stream（Java 8+，可链式 + 并行）
list.stream().filter(s -&amp;gt; s.length() &amp;gt; 0).forEach(System.out::println);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;二、List 接口详解&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;List&lt;/code&gt; 接口继承自 &lt;code&gt;Collection&lt;/code&gt;，特征是&lt;strong&gt;有序（按插入顺序）&lt;/strong&gt;、&lt;strong&gt;可重复&lt;/strong&gt;、&lt;strong&gt;可按索引访问&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;2.1 List 接口核心 API&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 位置访问
E get(int index);
E set(int index, E element);    // 返回旧值
void add(int index, E element);
E remove(int index);

// 查找（基于 equals）
int indexOf(Object o);          // 第一次出现的下标，无则返回 -1
int lastIndexOf(Object o);

// 范围视图（重要！返回的是原集合的视图，修改互相影响）
List&amp;lt;E&amp;gt; subList(int fromIndex, int toIndex);

// 批量操作
boolean addAll(int index, Collection&amp;lt;? extends E&amp;gt; c);

// Java 8+ 默认方法
default void replaceAll(UnaryOperator&amp;lt;E&amp;gt; operator);   // 原地替换
default void sort(Comparator&amp;lt;? super E&amp;gt; c);           // 原地排序
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.2 ArrayList&lt;/h3&gt;
&lt;h4&gt;2.2.1 底层结构与关键字段&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ArrayList&lt;/code&gt; 是基于&lt;strong&gt;动态数组&lt;/strong&gt;实现的，&lt;strong&gt;随机访问快&lt;/strong&gt;（O(1)）、&lt;strong&gt;插入删除慢&lt;/strong&gt;（平均 O(n)，涉及数组拷贝）、&lt;strong&gt;非线程安全&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ArrayList&amp;lt;E&amp;gt; extends AbstractList&amp;lt;E&amp;gt;
        implements List&amp;lt;E&amp;gt;, RandomAccess, Cloneable, java.io.Serializable {

    private static final int DEFAULT_CAPACITY = 10;       // 初始容量
    private static final Object[] EMPTY_ELEMENTDATA = {};  // 空数组常量
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 区分无参构造
    transient Object[] elementData;  // 存储元素的数组，未被序列化
    private int size;                 // 实际元素数量
    protected transient int modCount = 0;  // 结构修改计数（fail-fast）
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.2.2 三种构造方式&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;new ArrayList&amp;lt;&amp;gt;();                 // elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
new ArrayList&amp;lt;&amp;gt;(int initialCapacity);
new ArrayList&amp;lt;&amp;gt;(Collection&amp;lt;? extends E&amp;gt; c);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无参构造&lt;strong&gt;不会&lt;/strong&gt;立即分配 10 个容量的数组，而是把内部数组设为一个空数组常量。&lt;strong&gt;首次 &lt;code&gt;add&lt;/code&gt; 时才扩容到 10&lt;/strong&gt;——这是一种懒加载策略。&lt;/p&gt;
&lt;h4&gt;2.2.3 扩容机制（核心）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
}

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)  // 容量已满，需扩容
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

private Object[] grow() {
    return grow(size + 1);
}

private Object[] grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity &amp;gt;&amp;gt; 1);  // 1.5 倍
    if (newCapacity - minCapacity &amp;lt; 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE &amp;gt; 0)               // 接近 Integer.MAX_VALUE
        newCapacity = hugeCapacity(minCapacity);
    return elementData = Arrays.copyOf(elementData, newCapacity);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每次扩容为原容量的 &lt;strong&gt;1.5 倍&lt;/strong&gt;（&lt;code&gt;oldCapacity + oldCapacity &amp;gt;&amp;gt; 1&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;Arrays.copyOf&lt;/code&gt; 复制数组，开销是 O(n)，所以&lt;strong&gt;预判容量一次性分配&lt;/strong&gt;可以显著减少扩容次数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hugeCapacity&lt;/code&gt; 在 &lt;code&gt;minCapacity &amp;lt; 0&lt;/code&gt;（即溢出）时抛 &lt;code&gt;OutOfMemoryError&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;2.2.4 常用 API 与复杂度&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;时间复杂度&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get(int)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;随机访问极快&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set(int, E)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;直接覆盖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add(E)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;均摊 O(1)&lt;/td&gt;
&lt;td&gt;末尾追加，扩容时 O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add(int, E)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;需移动后续元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove(int)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;需移动后续元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove(Object)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;先线性查找，再移动&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contains(Object)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;线性扫描&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;indexOf(Object)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;线性扫描&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clear()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;遍历置 null，便于 GC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iterator()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;返回内部类 &lt;code&gt;Itr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;forEach(Consumer)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;默认方法，Java 8+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;2.2.5 迭代器 fail-fast&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public E next() {
    checkForComodification();
    ...
}

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;只要在迭代过程中&lt;strong&gt;通过集合自身&lt;/strong&gt;（而非 &lt;code&gt;iterator.remove()&lt;/code&gt;）修改结构就会触发 &lt;code&gt;ConcurrentModificationException&lt;/code&gt;。&lt;code&gt;ConcurrentModificationException&lt;/code&gt; 是一种&lt;strong&gt;最佳努力&lt;/strong&gt;的检测，并不能保证 100% 触发并发问题。&lt;/p&gt;
&lt;h4&gt;2.2.6 使用建议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;已知元素数量时使用 &lt;code&gt;new ArrayList&amp;lt;&amp;gt;(initialCapacity)&lt;/code&gt;，避免多次扩容与拷贝。&lt;/li&gt;
&lt;li&gt;频繁在头/中部插入删除、且数据量大时，改用 &lt;code&gt;LinkedList&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;多读少写、要线程安全时考虑 &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;（写时复制）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.3 LinkedList&lt;/h3&gt;
&lt;h4&gt;2.3.1 底层结构&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;LinkedList&lt;/code&gt; 基于&lt;strong&gt;双向链表&lt;/strong&gt;，实现了 &lt;code&gt;List&lt;/code&gt; 与 &lt;code&gt;Deque&lt;/code&gt; 接口，因此既可以当列表也可以当队列/栈。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class LinkedList&amp;lt;E&amp;gt; extends AbstractSequentialList&amp;lt;E&amp;gt;
        implements List&amp;lt;E&amp;gt;, Deque&amp;lt;E&amp;gt;, Cloneable, java.io.Serializable {

    transient int size = 0;
    transient Node&amp;lt;E&amp;gt; first;   // 头节点
    transient Node&amp;lt;E&amp;gt; last;    // 尾节点

    private static class Node&amp;lt;E&amp;gt; {
        E item;
        Node&amp;lt;E&amp;gt; next;
        Node&amp;lt;E&amp;gt; prev;
        Node(E item, Node&amp;lt;E&amp;gt; next, Node&amp;lt;E&amp;gt; prev) {
            this.item = item; this.next = next; this.prev = prev;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.3.2 关键操作源码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public void addFirst(E e) {
    Node&amp;lt;E&amp;gt; f = first;
    Node&amp;lt;E&amp;gt; newNode = new Node&amp;lt;&amp;gt;(null, f, null);
    first = newNode;
    if (f == null) last = newNode;
    else f.prev = newNode;
    size++;
    modCount++;
}

public void addLast(E e) {
    Node&amp;lt;E&amp;gt; l = last;
    Node&amp;lt;E&amp;gt; newNode = new Node&amp;lt;&amp;gt;(e, null, l);
    last = newNode;
    if (l == null) first = newNode;
    else l.next = newNode;
    size++;
    modCount++;
}

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;  // 这里会根据 index 决定从前往后还是从后往前遍历
}

Node&amp;lt;E&amp;gt; node(int index) {
    if (index &amp;lt; (size &amp;gt;&amp;gt; 1)) {       // 前半段，从前往后
        Node&amp;lt;E&amp;gt; x = first;
        for (int i = 0; i &amp;lt; index; i++) x = x.next;
        return x;
    } else {                          // 后半段，从后往前
        Node&amp;lt;E&amp;gt; x = last;
        for (int i = size - 1; i &amp;gt; index; i--) x = x.prev;
        return x;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;get(int)&lt;/code&gt; 会根据 index 位置选择更短的方向遍历，是个小优化。&lt;/p&gt;
&lt;h4&gt;2.3.3 时间复杂度&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;方法&lt;/th&gt;
&lt;th&gt;时间复杂度&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addFirst / removeFirst&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;头插/头删&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;addLast / removeLast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;尾插/尾删&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add(int, E)&lt;/code&gt; / &lt;code&gt;remove(int)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;需要先定位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get(int)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)，平均 n/4&lt;/td&gt;
&lt;td&gt;通过折半遍历&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contains(Object)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;线性扫描&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;peek() / poll() / push() / pop()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;当作栈/队列使用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;2.3.4 Deque 接口常用方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;队头&lt;/th&gt;
&lt;th&gt;抛异常&lt;/th&gt;
&lt;th&gt;返回特殊值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;队头取&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getFirst()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peekFirst()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;队头删&lt;/td&gt;
&lt;td&gt;&lt;code&gt;removeFirst()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pollFirst()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;队头插&lt;/td&gt;
&lt;td&gt;&lt;code&gt;addFirst()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offerFirst()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;队尾&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getLast()&lt;/code&gt; / &lt;code&gt;addLast()&lt;/code&gt; / &lt;code&gt;removeLast()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peekLast()&lt;/code&gt; / &lt;code&gt;offerLast()&lt;/code&gt; / &lt;code&gt;pollLast()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;推荐用 &lt;code&gt;offer&lt;/code&gt; / &lt;code&gt;poll&lt;/code&gt; / &lt;code&gt;peek&lt;/code&gt;，因为它们通过返回值表达失败（&lt;code&gt;null&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;），不会抛异常。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;2.3.5 使用建议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;频繁在头尾插删，且不需要随机访问时，使用 &lt;code&gt;LinkedList&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要把 LinkedList 当作 ArrayList 用&lt;/strong&gt;——&lt;code&gt;get(i)&lt;/code&gt; 是 O(n)，比 &lt;code&gt;ArrayList&lt;/code&gt; 慢很多，且每个节点有额外两个指针（内存开销更大、缓存局部性差）。&lt;/li&gt;
&lt;li&gt;实际工程中，&lt;strong&gt;优先选 &lt;code&gt;ArrayDeque&lt;/code&gt; 作为栈/队列&lt;/strong&gt;，比 &lt;code&gt;LinkedList&lt;/code&gt; 更紧凑、更快（基于数组，无包装节点）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.4 Vector&lt;/h3&gt;
&lt;h4&gt;2.4.1 特性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;：所有方法都用 &lt;code&gt;synchronized&lt;/code&gt; 修饰，是 JDK 早期提供的线程安全 List。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;扩容机制&lt;/strong&gt;：默认扩容为 &lt;strong&gt;2 倍&lt;/strong&gt;（与 ArrayList 的 1.5 倍不同）。&lt;/li&gt;
&lt;li&gt;性能远不如 &lt;code&gt;ArrayList&lt;/code&gt; + 手动同步或并发集合，已经&lt;strong&gt;基本被淘汰&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement &amp;gt; 0) ? capacityIncrement : oldCapacity);
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;2.4.2 Stack&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Stack&lt;/code&gt; 继承自 &lt;code&gt;Vector&lt;/code&gt;，提供 LIFO 语义：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Stack&amp;lt;E&amp;gt; extends Vector&amp;lt;E&amp;gt; {
    public E push(E item) { add(item); return item; }
    public synchronized E pop() { ... }
    public synchronized E peek() { ... }
    public boolean empty() { return size() == 0; }
    public synchronized int search(Object o) { return indexOf(o) + 1; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;不推荐使用 &lt;code&gt;Stack&lt;/code&gt;（继承自 &lt;code&gt;Vector&lt;/code&gt;，同步开销大）。推荐使用 &lt;code&gt;ArrayDeque&lt;/code&gt;，效率更高。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2.5 List 实现类对比&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;ArrayList&lt;/th&gt;
&lt;th&gt;LinkedList&lt;/th&gt;
&lt;th&gt;Vector&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;底层&lt;/td&gt;
&lt;td&gt;动态数组&lt;/td&gt;
&lt;td&gt;双向链表&lt;/td&gt;
&lt;td&gt;动态数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;随机访问&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;头插&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;尾插&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;中间插删&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)（先定位）&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内存占用&lt;/td&gt;
&lt;td&gt;小&lt;/td&gt;
&lt;td&gt;较大（前后指针）&lt;/td&gt;
&lt;td&gt;小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;线程安全&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是（粗粒度同步）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;默认扩容&lt;/td&gt;
&lt;td&gt;1.5x&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;2x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推荐度&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★（被 ArrayDeque 取代）&lt;/td&gt;
&lt;td&gt;✗（淘汰）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;2.6 List 选型建议&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;随机访问多、尾部追加为主&lt;/strong&gt; → &lt;code&gt;ArrayList&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;头尾操作频繁、要实现队列/栈&lt;/strong&gt; → &lt;code&gt;ArrayDeque&lt;/code&gt;（数组实现、更紧凑）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多读少写、要线程安全&lt;/strong&gt; → &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高并发写多读多&lt;/strong&gt; → 业务层加锁 + &lt;code&gt;ArrayList&lt;/code&gt;，或使用 &lt;code&gt;Collections.synchronizedList(new ArrayList&amp;lt;&amp;gt;())&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;明确不可变、共享只读&lt;/strong&gt; → &lt;code&gt;List.of(...)&lt;/code&gt; / &lt;code&gt;Collections.unmodifiableList(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;三、Set 接口详解&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Set&lt;/code&gt; 接口继承自 &lt;code&gt;Collection&lt;/code&gt;，特征是&lt;strong&gt;不允许重复元素&lt;/strong&gt;（基于 &lt;code&gt;equals&lt;/code&gt; 判断），&lt;strong&gt;最多一个 null&lt;/strong&gt;（部分实现禁止 null）。&lt;code&gt;Set&lt;/code&gt; 的常用实现有 &lt;code&gt;HashSet&lt;/code&gt; / &lt;code&gt;LinkedHashSet&lt;/code&gt; / &lt;code&gt;TreeSet&lt;/code&gt; / &lt;code&gt;EnumSet&lt;/code&gt; / &lt;code&gt;CopyOnWriteArraySet&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;3.1 Set 接口核心 API&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Set&lt;/code&gt; 没有 &lt;code&gt;List&lt;/code&gt; 那种按索引访问的方法，方法定义和 &lt;code&gt;Collection&lt;/code&gt; 大体一致。Java 8+ 在 &lt;code&gt;Collection&lt;/code&gt; 上补充了大量默认方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Collection 默认方法（Set 全部继承）
default boolean removeIf(Predicate&amp;lt;? super E&amp;gt; filter);

// Stream
default Stream&amp;lt;E&amp;gt; stream();
default Stream&amp;lt;E&amp;gt; parallelStream();

// forEach
default void forEach(Consumer&amp;lt;? super E&amp;gt; action);

// 数学集合运算（需要目标 Set 实现了对应方法）
boolean containsAll(Collection&amp;lt;?&amp;gt; c);             // 子集
boolean addAll(Collection&amp;lt;? extends E&amp;gt; c);        // 并集
boolean retainAll(Collection&amp;lt;?&amp;gt; c);                // 交集
boolean removeAll(Collection&amp;lt;?&amp;gt; c);                // 差集
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 HashSet&lt;/h3&gt;
&lt;h4&gt;3.2.1 底层结构&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;HashSet&lt;/code&gt; 是基于 &lt;strong&gt;&lt;code&gt;HashMap&lt;/code&gt;&lt;/strong&gt; 实现的，所有元素作为 &lt;code&gt;HashMap&lt;/code&gt; 的 key 存放，value 统一为一个名为 &lt;code&gt;PRESENT&lt;/code&gt; 的 &lt;code&gt;Object&lt;/code&gt; 常量。所以 &lt;code&gt;HashSet&lt;/code&gt; 的所有特性都源自 &lt;code&gt;HashMap&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class HashSet&amp;lt;E&amp;gt; extends AbstractSet&amp;lt;E&amp;gt;
        implements Set&amp;lt;E&amp;gt;, Cloneable, java.io.Serializable {

    private transient HashMap&amp;lt;E, Object&amp;gt; map;
    private static final Object PRESENT = new Object();

    public HashSet() { map = new HashMap&amp;lt;&amp;gt;(); }
    public HashSet(int initialCapacity) { map = new HashMap&amp;lt;&amp;gt;(initialCapacity); }
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap&amp;lt;&amp;gt;(initialCapacity, loadFactor);
    }

    public boolean add(E e) {
        return map.put(e, PRESENT) == null;
    }

    public boolean contains(Object o) {
        return map.containsKey(o);
    }

    public boolean remove(Object o) {
        return map.remove(o) == PRESENT;
    }

    public int size() { return map.size(); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.2.2 关键特性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不允许重复&lt;/strong&gt;（依赖 hashCode + equals 共同决定）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无序&lt;/strong&gt;（迭代顺序取决于哈希值与桶分布）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;允许一个 null&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;增删改查&lt;strong&gt;均摊 O(1)&lt;/strong&gt;（哈希足够均匀时）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非线程安全&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.2.3 去重原理（重要）&lt;/h4&gt;
&lt;p&gt;对象放入 &lt;code&gt;HashSet&lt;/code&gt; 时：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;计算 &lt;code&gt;hashCode()&lt;/code&gt; 得到哈希值&lt;/li&gt;
&lt;li&gt;通过哈希值定位桶（数组下标）&lt;/li&gt;
&lt;li&gt;若桶为空直接放入；不为空则逐个用 &lt;code&gt;equals()&lt;/code&gt; 比较&lt;/li&gt;
&lt;li&gt;若 &lt;code&gt;equals()&lt;/code&gt; 返回 true，则视为重复，丢弃新元素&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;因此&lt;strong&gt;自定义对象若要正确去重，必须同时重写 &lt;code&gt;hashCode&lt;/code&gt; 与 &lt;code&gt;equals&lt;/code&gt;&lt;/strong&gt;，且契约一致：&lt;code&gt;a.equals(b) ⇒ a.hashCode() == b.hashCode()&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;3.2.4 使用示例&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Set&amp;lt;String&amp;gt; set = new HashSet&amp;lt;&amp;gt;();
set.add(&quot;a&quot;);
set.add(&quot;b&quot;);
set.add(&quot;a&quot;);       // 不会被加入
System.out.println(set.size());  // 2

// 自定义对象去重
class User {
    String name;
    int age;
    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User u = (User) o;
        return age == u.age &amp;amp;&amp;amp; Objects.equals(name, u.name);
    }
    @Override public int hashCode() { return Objects.hash(name, age); }
}
Set&amp;lt;User&amp;gt; users = new HashSet&amp;lt;&amp;gt;();
users.add(new User(&quot;tom&quot;, 18));
users.add(new User(&quot;tom&quot;, 18));  // 不会加入
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.2.5 性能与负载因子&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;HashSet&lt;/code&gt; 委托 &lt;code&gt;HashMap&lt;/code&gt;，其容量与负载因子由 &lt;code&gt;HashMap&lt;/code&gt; 决定（默认 16，负载 0.75）。元素数量超过 &lt;code&gt;capacity * loadFactor&lt;/code&gt; 时触发扩容，桶数翻倍。&lt;/p&gt;
&lt;h3&gt;3.3 LinkedHashSet&lt;/h3&gt;
&lt;h4&gt;3.3.1 底层结构&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;LinkedHashSet&lt;/code&gt; 继承自 &lt;code&gt;HashSet&lt;/code&gt;，底层使用 &lt;strong&gt;&lt;code&gt;LinkedHashMap&lt;/code&gt;&lt;/strong&gt;。它在哈希表基础上维护了一条&lt;strong&gt;双向链表&lt;/strong&gt;记录插入顺序（或访问顺序，构造参数控制）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public LinkedHashSet() { super(16, .75f, true); }

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap&amp;lt;&amp;gt;(initialCapacity, loadFactor);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;dummy&lt;/code&gt; 参数仅用于区分与 &lt;code&gt;HashSet(int, float)&lt;/code&gt; 重载，无业务含义。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;3.3.2 特性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;保持插入顺序&lt;/strong&gt;（默认）&lt;/li&gt;
&lt;li&gt;不允许重复&lt;/li&gt;
&lt;li&gt;比 &lt;code&gt;HashSet&lt;/code&gt; 略慢（多维护一条链表），但仍接近 O(1) 增删查&lt;/li&gt;
&lt;li&gt;可用于实现 LRU（&lt;code&gt;LinkedHashMap&lt;/code&gt; 的 accessOrder=true + removeEldestEntry）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3.3.3 简单 LRU 缓存&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;class LRUCache&amp;lt;K, V&amp;gt; extends LinkedHashMap&amp;lt;K, V&amp;gt; {
    private final int maxSize;
    public LRUCache(int maxSize) {
        super(16, .75f, true);  // accessOrder=true
        this.maxSize = maxSize;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry&amp;lt;K, V&amp;gt; eldest) {
        return size() &amp;gt; maxSize;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.4 TreeSet&lt;/h3&gt;
&lt;h4&gt;3.4.1 底层结构&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;TreeSet&lt;/code&gt; 基于 &lt;strong&gt;&lt;code&gt;TreeMap&lt;/code&gt;（红黑树）&lt;/strong&gt; 实现，元素是 &lt;code&gt;TreeMap&lt;/code&gt; 的 key，因此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;有序&lt;/strong&gt;：自然顺序或自定义 &lt;code&gt;Comparator&lt;/code&gt; 顺序&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不允许重复&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不允许 null&lt;/strong&gt;（自然顺序下，因为 &lt;code&gt;compareTo&lt;/code&gt; 会 NPE）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增删改查 O(log n)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;public class TreeSet&amp;lt;E&amp;gt; extends AbstractSet&amp;lt;E&amp;gt;
        implements NavigableSet&amp;lt;E&amp;gt;, Cloneable, java.io.Serializable {
    private transient NavigableMap&amp;lt;E, Object&amp;gt; m;
    private static final Object PRESENT = new Object();

    public TreeSet() { this(new TreeMap&amp;lt;&amp;gt;()); }
    public TreeSet(Comparator&amp;lt;? super E&amp;gt; comparator) { this(new TreeMap&amp;lt;&amp;gt;(comparator)); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.4.2 NavigableSet 扩展 API&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 范围操作
E lower(E e);       // 小于 e 的最大元素
E floor(E e);       // 小于或等于 e 的最大元素
E ceiling(E e);     // 大于或等于 e 的最小元素
E higher(E e);      // 大于 e 的最小元素

// 头尾
E first();          // 最小的
E last();           // 最大的
E pollFirst();      // 取出并移除最小
E pollLast();       // 取出并移除最大

// 子集视图
NavigableSet&amp;lt;E&amp;gt; subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive);
NavigableSet&amp;lt;E&amp;gt; headSet(E toElement, boolean inclusive);
NavigableSet&amp;lt;E&amp;gt; tailSet(E fromElement, boolean inclusive);

// 逆序
NavigableSet&amp;lt;E&amp;gt; descendingSet();
Iterator&amp;lt;E&amp;gt; descendingIterator();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.4.3 比较器&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 1. 自然顺序（元素实现 Comparable）
TreeSet&amp;lt;Integer&amp;gt; ts1 = new TreeSet&amp;lt;&amp;gt;();
ts1.add(3); ts1.add(1); ts1.add(2);
// 遍历：1 2 3

// 2. 自定义比较器
TreeSet&amp;lt;String&amp;gt; ts2 = new TreeSet&amp;lt;&amp;gt;(Comparator.comparingInt(String::length));
ts2.add(&quot;aa&quot;); ts2.add(&quot;b&quot;); ts2.add(&quot;ccc&quot;);
// 遍历：b aa ccc（按长度升序，长度相同按 String 自然顺序）

// 3. Lambda 比较器（Java 8+）
TreeSet&amp;lt;User&amp;gt; ts3 = new TreeSet&amp;lt;&amp;gt;((a, b) -&amp;gt; Integer.compare(a.getAge(), b.getAge()));
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;3.4.4 使用建议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;需要&lt;strong&gt;有序 + 去重&lt;/strong&gt;，首选 &lt;code&gt;TreeSet&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;频繁范围查询 / 找最值：&lt;code&gt;TreeSet&lt;/code&gt; 比 &lt;code&gt;ArrayList + sort + binarySearch&lt;/code&gt; 方便&lt;/li&gt;
&lt;li&gt;元素若用自定义对象，必须实现 &lt;code&gt;Comparable&lt;/code&gt; 或在构造时传入 &lt;code&gt;Comparator&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3.5 EnumSet&lt;/h3&gt;
&lt;p&gt;专为枚举设计，底层是&lt;strong&gt;位向量&lt;/strong&gt;（一个 long 存储 64 个元素），&lt;strong&gt;极快且极紧凑&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

EnumSet&amp;lt;Day&amp;gt; weekend = EnumSet.of(Day.SAT, Day.SUN);
EnumSet&amp;lt;Day&amp;gt; weekday = EnumSet.complementOf(weekend);

for (Day d : EnumSet.range(Day.MON, Day.FRI)) { ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.6 CopyOnWriteArraySet&lt;/h3&gt;
&lt;p&gt;基于 &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt; 实现。&lt;strong&gt;写时复制&lt;/strong&gt;，写操作性能差但读操作极快且线程安全，适合&lt;strong&gt;读多写极少&lt;/strong&gt;的场景（如黑白名单）。&lt;/p&gt;
&lt;h3&gt;3.7 Set 实现类对比&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;HashSet&lt;/th&gt;
&lt;th&gt;LinkedHashSet&lt;/th&gt;
&lt;th&gt;TreeSet&lt;/th&gt;
&lt;th&gt;EnumSet&lt;/th&gt;
&lt;th&gt;CopyOnWriteArraySet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;底层&lt;/td&gt;
&lt;td&gt;HashMap&lt;/td&gt;
&lt;td&gt;LinkedHashMap&lt;/td&gt;
&lt;td&gt;TreeMap（红黑树）&lt;/td&gt;
&lt;td&gt;位向量&lt;/td&gt;
&lt;td&gt;COW 数组&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;顺序&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;插入顺序&lt;/td&gt;
&lt;td&gt;自然 / 比较器&lt;/td&gt;
&lt;td&gt;枚举声明顺序&lt;/td&gt;
&lt;td&gt;插入顺序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;增删查&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;读 O(1)、写 O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;允许 null&lt;/td&gt;
&lt;td&gt;是（1 个）&lt;/td&gt;
&lt;td&gt;是（1 个）&lt;/td&gt;
&lt;td&gt;否（取决于比较器）&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是（1 个）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;线程安全&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;适用场景&lt;/td&gt;
&lt;td&gt;默认去重&lt;/td&gt;
&lt;td&gt;需要保留插入顺序&lt;/td&gt;
&lt;td&gt;有序去重、范围查询&lt;/td&gt;
&lt;td&gt;枚举集合&lt;/td&gt;
&lt;td&gt;读多写少&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;四、Map 接口详解&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Map&lt;/code&gt; 用于存储&lt;strong&gt;键值对（key-value）&lt;/strong&gt;，key 不允许重复（基于 &lt;code&gt;equals&lt;/code&gt;），一个 key 最多映射一个 value。&lt;code&gt;Map&lt;/code&gt; 不是 &lt;code&gt;Collection&lt;/code&gt; 的子接口，但提供了 &lt;code&gt;Collection&lt;/code&gt; 视图（keySet、values、entrySet）以使用 Collection 的 API。&lt;/p&gt;
&lt;h3&gt;4.1 Map 接口核心 API&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 基本 CRUD
V put(K key, V value);
V get(Object key);
V remove(Object key);
boolean containsKey(Object key);
boolean containsValue(Object value);
int size();
boolean isEmpty();
void clear();

// 批量
void putAll(Map&amp;lt;? extends K, ? extends V&amp;gt; m);

// 默认方法（Java 8+）
default V getOrDefault(Object key, V defaultValue);
default void forEach(BiConsumer&amp;lt;? super K, ? super V&amp;gt; action);
default void replaceAll(BiFunction&amp;lt;? super K, ? super V, ? extends V&amp;gt; function);
default V putIfAbsent(K key, V value);
default boolean remove(Object key, Object value);          // 同时匹配才删
default boolean replace(K key, V oldValue, V newValue);    // CAS 替换
default V compute(K key, BiFunction&amp;lt;? super K, ? super V, ? extends V&amp;gt; remappingFunction);
default V computeIfAbsent(K key, Function&amp;lt;? super K, ? extends V&amp;gt; mappingFunction);
default V computeIfPresent(K key, BiFunction&amp;lt;? super K, ? super V, ? extends V&amp;gt; remappingFunction);
default V merge(K key, V value, BiFunction&amp;lt;? super V, ? super V, ? extends V&amp;gt; remappingFunction);

// 视图（重要）
Set&amp;lt;K&amp;gt; keySet();
Collection&amp;lt;V&amp;gt; values();
Set&amp;lt;Map.Entry&amp;lt;K, V&amp;gt;&amp;gt; entrySet();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.2 Map.Entry 接口&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;interface Entry&amp;lt;K, V&amp;gt; {
    K getKey();
    V getValue();
    V setValue(V value);
    static &amp;lt;K, V&amp;gt; Map.Entry&amp;lt;K, V&amp;gt; entry(K k, V v);  // Java 9+
    // Java 8+ 比较器
    static &amp;lt;K, V extends Comparable&amp;lt;? super V&amp;gt;&amp;gt; Comparator&amp;lt;Map.Entry&amp;lt;K, V&amp;gt;&amp;gt; comparingByValue();
    static &amp;lt;K, V&amp;gt; Comparator&amp;lt;Map.Entry&amp;lt;K, V&amp;gt;&amp;gt; comparingByKey();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.3 HashMap（JDK 8，面试核心）&lt;/h3&gt;
&lt;h4&gt;4.3.1 底层结构&lt;/h4&gt;
&lt;p&gt;HashMap 是基于&lt;strong&gt;哈希表&lt;/strong&gt;实现的，采用&lt;strong&gt;数组 + 链表 + 红黑树&lt;/strong&gt;三种结构混合：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌────────────────────┐
│   table (Node[])   │
│  ┌────┐ ┌────┐    │
│  │ [] │ │[N1]│→[N2]│→[N3]│   链表长度 &amp;lt; 8
│  └────┘ └────┘    │
│  ┌────┐ ┌────┐    │
│  │ [] │ │[T1]│─→[T2]│        红黑树 (Node 数 &amp;gt;= 8 且 table 长度 &amp;gt;= 64)
│  └────┘ └────┘    │
└────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键字段：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static final int DEFAULT_INITIAL_CAPACITY = 1 &amp;lt;&amp;lt; 4;   // 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int TREEIFY_THRESHOLD = 8;                // 链表转红黑树阈值
static final int UNTREEIFY_THRESHOLD = 6;              // 红黑树退化为链表阈值
static final int MIN_TREEIFY_CAPACITY = 64;            // 树化最小表长度

transient Node&amp;lt;K, V&amp;gt;[] table;
transient int size;
int threshold;             // 扩容阈值 = capacity * loadFactor
final float loadFactor;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Node&lt;/code&gt; 是 &lt;code&gt;Map.Entry&lt;/code&gt; 的实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static class Node&amp;lt;K, V&amp;gt; implements Map.Entry&amp;lt;K, V&amp;gt; {
    final int hash;
    final K key;
    V value;
    Node&amp;lt;K, V&amp;gt; next;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.3.2 hash 算法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h &amp;gt;&amp;gt;&amp;gt; 16);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;设计要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;key.hashCode()&lt;/code&gt; 高 16 位与低 16 位异或，&lt;strong&gt;让高位也参与到下标计算中&lt;/strong&gt;，减少哈希冲突&lt;/li&gt;
&lt;li&gt;容量是 2 的幂时，&lt;code&gt;(n - 1) &amp;amp; hash&lt;/code&gt; 相当于取模（位运算比 &lt;code&gt;%&lt;/code&gt; 快很多）&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;static int indexFor(int h, int length) {
    return h &amp;amp; (length - 1);  // 等价于 h % length，length 必为 2 的幂
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.3.3 put 流程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node&amp;lt;K, V&amp;gt;[] tab; Node&amp;lt;K, V&amp;gt; p; int n, i;
    // 1. 表为空则初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 2. 计算下标，桶为空则直接放
    if ((p = tab[i = (n - 1) &amp;amp; hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node&amp;lt;K, V&amp;gt; e; K k;
        // 3. 桶不为空：先比较头节点
        if (p.hash == hash &amp;amp;&amp;amp;
            ((k = p.key) == key || (key != null &amp;amp;&amp;amp; key.equals(k))))
            e = p;
        // 4. 红黑树节点则走树插入
        else if (p instanceof TreeNode)
            e = ((TreeNode&amp;lt;K, V&amp;gt;) p).putTreeVal(this, tab, hash, key, value);
        // 5. 链表：遍历到末尾追加，必要时树化
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount &amp;gt;= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);  // 链表长度 &amp;gt;= 8 时尝试树化
                    break;
                }
                if (e.hash == hash &amp;amp;&amp;amp; ((k = e.key) == key || key.equals(k)))
                    break;
                p = e;
            }
        }
        // 6. 已存在的 key：替换值
        if (e != null) {
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);  // LinkedHashMap 用
            return oldValue;
        }
    }
    ++modCount;
    if (++size &amp;gt; threshold)
        resize();
    afterNodeInsertion(evict);   // LinkedHashMap 用
    return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.3.4 扩容机制（resize）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;final Node&amp;lt;K, V&amp;gt;[] resize() {
    Node&amp;lt;K, V&amp;gt;[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap &amp;gt; 0) {
        if (oldCap &amp;gt;= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap &amp;lt;&amp;lt; 1) &amp;lt; MAXIMUM_CAPACITY &amp;amp;&amp;amp;
                 oldCap &amp;gt;= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr &amp;lt;&amp;lt; 1;  // 阈值翻倍
    }
    else if (oldThr &amp;gt; 0)
        newCap = oldThr;
    else {
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float) newCap * loadFactor;
        newThr = (newCap &amp;lt; MAXIMUM_CAPACITY &amp;amp;&amp;amp; ft &amp;lt; (float)MAXIMUM_CAPACITY
                  ? (int) ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({&quot;rawtypes&quot;,&quot;unchecked&quot;})
    Node&amp;lt;K, V&amp;gt;[] newTab = (Node&amp;lt;K, V&amp;gt;[]) new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        for (int j = 0; j &amp;lt; oldCap; ++j) {
            Node&amp;lt;K, V&amp;gt; e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash &amp;amp; (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    ((TreeNode&amp;lt;K, V&amp;gt;) e).split(this, newTab, j, oldCap);
                else {
                    // 链表拆分：JDK 8 优化，避免重新计算 hash
                    Node&amp;lt;K, V&amp;gt; loHead = null, loTail = null;
                    Node&amp;lt;K, V&amp;gt; hiHead = null, hiTail = null;
                    Node&amp;lt;K, V&amp;gt; next;
                    do {
                        next = e.next;
                        // 关键：根据 hash &amp;amp; oldCap == 0 决定节点位置
                        if ((e.hash &amp;amp; oldCap) == 0) {
                            if (loTail == null) loHead = e;
                            else loTail.next = e;
                            loTail = e;
                        } else {
                            if (hiTail == null) hiHead = e;
                            else hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;容量翻倍&lt;/strong&gt;：容量与阈值均翻倍（仍保持 2 的幂）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JDK 8 链表拆分优化&lt;/strong&gt;：每个节点根据 &lt;code&gt;e.hash &amp;amp; oldCap == 0&lt;/code&gt; 决定留在原下标或迁移到 &lt;code&gt;j + oldCap&lt;/code&gt;，&lt;strong&gt;不需要重新计算 hash&lt;/strong&gt;，只需 1 次位运算&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;树拆分&lt;/strong&gt;：红黑树拆分为低位树和高位树，必要时退化为链表&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;4.3.5 get 流程&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public V get(Object key) {
    Node&amp;lt;K, V&amp;gt; e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

final Node&amp;lt;K, V&amp;gt; getNode(int hash, Object key) {
    Node&amp;lt;K, V&amp;gt;[] tab; Node&amp;lt;K, V&amp;gt; first, e; int n; K k;
    if ((tab = table) != null &amp;amp;&amp;amp; (n = tab.length) &amp;gt; 0 &amp;amp;&amp;amp;
        (first = tab[(n - 1) &amp;amp; hash]) != null) {
        // 1. 先查头节点
        if (first.hash == hash &amp;amp;&amp;amp;
            ((k = first.key) == key || (key != null &amp;amp;&amp;amp; key.equals(k))))
            return first;
        // 2. 链表 / 树继续查找
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode&amp;lt;K, V&amp;gt;) first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &amp;amp;&amp;amp; ((k = e.key) == key || key.equals(k)))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.3.6 为什么容量是 2 的幂&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;桶下标计算 &lt;code&gt;hash &amp;amp; (n - 1)&lt;/code&gt; 等价于模运算，&lt;strong&gt;位运算比取模快得多&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;扩容时只需判断&lt;strong&gt;一位&lt;/strong&gt;（&lt;code&gt;e.hash &amp;amp; oldCap&lt;/code&gt;）就能确定节点的新位置，&lt;strong&gt;避免重新计算哈希&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.3.7 为什么负载因子默认 0.75&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;太高&lt;/strong&gt;：冲突概率增大，链表变长，查找变慢&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;太低&lt;/strong&gt;：浪费空间，扩容频繁&lt;/li&gt;
&lt;li&gt;0.75 是&lt;strong&gt;空间与时间&lt;/strong&gt;的折中（泊松分布下，桶中元素数符合期望 ≤ 0.5 时冲突率低）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.3.8 树化的两个条件&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;if (binCount &amp;gt;= TREEIFY_THRESHOLD - 1)  // 链表长度 &amp;gt;= 8
    treeifyBin(tab, hash);

if (tab.length &amp;lt; MIN_TREEIFY_CAPACITY)  // 表长度 &amp;lt; 64
    resize();   // 优先扩容而不是树化
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;链表长度 ≥ 8 &lt;strong&gt;且&lt;/strong&gt; table 长度 ≥ 64 才转红黑树；否则优先扩容降低冲突。退化阈值为 6，扩容拆分时如果节点数 ≤ 6 会退化成链表。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;4.3.9 使用建议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;预分配容量&lt;/strong&gt;：&lt;code&gt;new HashMap&amp;lt;&amp;gt;(expectedSize / 0.75f + 1)&lt;/code&gt;，避免扩容&lt;/li&gt;
&lt;li&gt;key 必须正确实现 &lt;code&gt;hashCode&lt;/code&gt; 与 &lt;code&gt;equals&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不允许用可变对象作为 key（修改后哈希变化导致查不到、内存泄漏）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.4 LinkedHashMap&lt;/h3&gt;
&lt;h4&gt;4.4.1 底层结构&lt;/h4&gt;
&lt;p&gt;继承自 &lt;code&gt;HashMap&lt;/code&gt;，在 &lt;code&gt;Node&lt;/code&gt; 基础上扩展为 &lt;code&gt;Entry&lt;/code&gt;，新增 &lt;code&gt;before&lt;/code&gt; / &lt;code&gt;after&lt;/code&gt; 指针维护&lt;strong&gt;双向链表&lt;/strong&gt;，可按&lt;strong&gt;插入顺序&lt;/strong&gt;或&lt;strong&gt;访问顺序&lt;/strong&gt;遍历。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static class Entry&amp;lt;K, V&amp;gt; extends HashMap.Node&amp;lt;K, V&amp;gt; {
    Entry&amp;lt;K, V&amp;gt; before, after;
    Entry(int hash, K key, V value, Node&amp;lt;K, V&amp;gt; next) {
        super(hash, key, value, next);
    }
}

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;accessOrder = false&lt;/code&gt;（默认）：按插入顺序&lt;/li&gt;
&lt;li&gt;&lt;code&gt;accessOrder = true&lt;/code&gt;：按访问顺序，每次 &lt;code&gt;get&lt;/code&gt; 都会把节点移到尾部（&lt;strong&gt;实现 LRU 的核心&lt;/strong&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.4.2 LRU 实现&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class LRU&amp;lt;K, V&amp;gt; extends LinkedHashMap&amp;lt;K, V&amp;gt; {
    private final int capacity;
    public LRU(int capacity) {
        super(16, 0.75f, true);
        this.capacity = capacity;
    }
    @Override
    protected boolean removeEldestEntry(Map.Entry&amp;lt;K, V&amp;gt; eldest) {
        return size() &amp;gt; capacity;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4.5 TreeMap&lt;/h3&gt;
&lt;h4&gt;4.5.1 底层结构&lt;/h4&gt;
&lt;p&gt;基于&lt;strong&gt;红黑树&lt;/strong&gt;实现，&lt;strong&gt;key 有序&lt;/strong&gt;（自然顺序或自定义 &lt;code&gt;Comparator&lt;/code&gt;），增删改查 &lt;strong&gt;O(log n)&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;4.5.2 NavigableMap API&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Map.Entry&amp;lt;K, V&amp;gt; lowerEntry(K key);
Map.Entry&amp;lt;K, V&amp;gt; floorEntry(K key);
Map.Entry&amp;lt;K, V&amp;gt; ceilingEntry(K key);
Map.Entry&amp;lt;K, V&amp;gt; higherEntry(K key);

Map.Entry&amp;lt;K, V&amp;gt; firstEntry();
Map.Entry&amp;lt;K, V&amp;gt; lastEntry();
Map.Entry&amp;lt;K, V&amp;gt; pollFirstEntry();
Map.Entry&amp;lt;K, V&amp;gt; pollLastEntry();

NavigableMap&amp;lt;K, V&amp;gt; subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.5.3 使用场景&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;需要 &lt;strong&gt;有序 + key 范围查询 / 找最值&lt;/strong&gt;：首选 TreeMap&lt;/li&gt;
&lt;li&gt;子树迭代性能比遍历 HashMap 转 list + sort 略慢，但能持续维护有序&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.6 Hashtable&lt;/h3&gt;
&lt;h4&gt;4.6.1 特性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;早期线程安全 Map，方法都用 &lt;code&gt;synchronized&lt;/code&gt; 修饰&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不允许 key / value 为 null&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;默认容量 11，扩容 &lt;code&gt;2 * old + 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;已被 &lt;code&gt;ConcurrentHashMap&lt;/code&gt; 取代&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.7 ConcurrentHashMap（重点）&lt;/h3&gt;
&lt;h4&gt;4.7.1 设计目标&lt;/h4&gt;
&lt;p&gt;提供&lt;strong&gt;高并发&lt;/strong&gt;的线程安全 Map，相比 &lt;code&gt;Hashtable&lt;/code&gt; 的全表锁和 &lt;code&gt;Collections.synchronizedMap&lt;/code&gt; 的单一互斥锁，CHM 通过&lt;strong&gt;分段锁 / CAS&lt;/strong&gt; 实现更高的并发度。&lt;/p&gt;
&lt;h4&gt;4.7.2 JDK 7 vs JDK 8&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;版本&lt;/th&gt;
&lt;th&gt;JDK 7&lt;/th&gt;
&lt;th&gt;JDK 8+&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;结构&lt;/td&gt;
&lt;td&gt;Segment 数组 + HashEntry 数组 + 链表&lt;/td&gt;
&lt;td&gt;Node 数组 + 链表 / 红黑树（与 HashMap 类似）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;并发控制&lt;/td&gt;
&lt;td&gt;Segment 锁（默认 16 段）&lt;/td&gt;
&lt;td&gt;CAS + &lt;code&gt;synchronized&lt;/code&gt;（只锁桶头节点）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;size 统计&lt;/td&gt;
&lt;td&gt;多次统计取最优（避免一致性开销）&lt;/td&gt;
&lt;td&gt;baseCount + counterCells（类似 LongAdder）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;扩容&lt;/td&gt;
&lt;td&gt;Segment 内独立扩容&lt;/td&gt;
&lt;td&gt;多线程协同扩容（transfer）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;4.7.3 关键字段（JDK 8+）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;transient volatile Node&amp;lt;K, V&amp;gt;[] table;        // 桶数组，volatile 保持可见性
private transient volatile Node&amp;lt;K, V&amp;gt;[] nextTable; // 扩容时的新表
private transient volatile int sizeCtl;       // 控制扩容标识
private transient volatile long baseCount;
private transient volatile CounterCell[] counterCells;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4.7.4 put 流程概要&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;表为空则 &lt;code&gt;initTable()&lt;/code&gt;（CAS）&lt;/li&gt;
&lt;li&gt;桶为空则 CAS 写入新节点&lt;/li&gt;
&lt;li&gt;桶非空：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MOVED&lt;/code&gt; → 协助扩容（&lt;code&gt;helpTransfer&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;红黑树 → 树插入&lt;/li&gt;
&lt;li&gt;链表 → &lt;code&gt;synchronized&lt;/code&gt; 锁头节点后遍历插入&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;链表节点数达到阈值 → 树化&lt;/li&gt;
&lt;li&gt;更新元素计数（&lt;code&gt;addCount&lt;/code&gt;）&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;4.7.5 get 流程&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;不需要加锁（&lt;code&gt;volatile&lt;/code&gt; 保证可见性）&lt;/li&gt;
&lt;li&gt;沿桶链表 / 树查找&lt;/li&gt;
&lt;li&gt;弱一致性：可能读到中途正在扩容的数据，但&lt;strong&gt;不会抛 &lt;code&gt;ConcurrentModificationException&lt;/code&gt;&lt;/strong&gt;，是 &lt;strong&gt;fail-safe&lt;/strong&gt; 迭代器&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;4.7.6 为什么不允许 null key / value&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;多线程下 &lt;code&gt;containsKey&lt;/code&gt; + &lt;code&gt;put&lt;/code&gt; 组合不能保证原子，null 无法明确表示&quot;该 key 不存在&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;4.8 Map 实现类对比&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;HashMap&lt;/th&gt;
&lt;th&gt;LinkedHashMap&lt;/th&gt;
&lt;th&gt;TreeMap&lt;/th&gt;
&lt;th&gt;Hashtable&lt;/th&gt;
&lt;th&gt;ConcurrentHashMap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;底层&lt;/td&gt;
&lt;td&gt;哈希表&lt;/td&gt;
&lt;td&gt;哈希表 + 链表&lt;/td&gt;
&lt;td&gt;红黑树&lt;/td&gt;
&lt;td&gt;哈希表&lt;/td&gt;
&lt;td&gt;哈希表（分段/CAS）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;顺序&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;插入/访问&lt;/td&gt;
&lt;td&gt;自然/比较器&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;增删查&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;td&gt;O(1) 均摊&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;允许 null&lt;/td&gt;
&lt;td&gt;KV 都允许&lt;/td&gt;
&lt;td&gt;KV 都允许&lt;/td&gt;
&lt;td&gt;不允许 key&lt;/td&gt;
&lt;td&gt;都不允许&lt;/td&gt;
&lt;td&gt;都不允许&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;线程安全&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是（粗粒度）&lt;/td&gt;
&lt;td&gt;是（细粒度）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;迭代器&lt;/td&gt;
&lt;td&gt;fail-fast&lt;/td&gt;
&lt;td&gt;fail-fast&lt;/td&gt;
&lt;td&gt;fail-fast&lt;/td&gt;
&lt;td&gt;fail-fast&lt;/td&gt;
&lt;td&gt;fail-safe（弱一致）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推荐度&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;★★★★&lt;/td&gt;
&lt;td&gt;★★★★&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;五、Queue / Deque 接口详解&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Queue&lt;/code&gt; 是一种&lt;strong&gt;先进先出（FIFO）&lt;/strong&gt; 的数据结构。&lt;code&gt;Deque&lt;/code&gt;（Double Ended Queue）是双端队列，支持在两端插入和删除元素。&lt;/p&gt;
&lt;h3&gt;5.1 Queue 接口方法分组&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;行为&lt;/th&gt;
&lt;th&gt;抛异常&lt;/th&gt;
&lt;th&gt;返回特殊值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;队尾插入&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offer(e)&lt;/code&gt;（推荐）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;队头取并删除&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;poll()&lt;/code&gt;（推荐）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;队头取不删&lt;/td&gt;
&lt;td&gt;&lt;code&gt;element()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peek()&lt;/code&gt;（推荐）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;5.2 Deque 接口方法&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;位置&lt;/th&gt;
&lt;th&gt;抛异常&lt;/th&gt;
&lt;th&gt;返回特殊值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;头插&lt;/td&gt;
&lt;td&gt;&lt;code&gt;addFirst(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offerFirst(e)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;尾插&lt;/td&gt;
&lt;td&gt;&lt;code&gt;addLast(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offerLast(e)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;头取删&lt;/td&gt;
&lt;td&gt;&lt;code&gt;removeFirst()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pollFirst()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;尾取删&lt;/td&gt;
&lt;td&gt;&lt;code&gt;removeLast()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pollLast()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;头查看&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getFirst()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peekFirst()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;尾查看&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getLast()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peekLast()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;栈压入&lt;/td&gt;
&lt;td&gt;&lt;code&gt;push(e)&lt;/code&gt;（= addFirst）&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;栈弹出&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pop()&lt;/code&gt;（= removeFirst）&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;5.3 ArrayDeque&lt;/h3&gt;
&lt;h4&gt;5.3.1 底层结构&lt;/h4&gt;
&lt;p&gt;基于&lt;strong&gt;循环数组&lt;/strong&gt;实现的双端队列，可作为&lt;strong&gt;栈&lt;/strong&gt;（比 &lt;code&gt;Stack&lt;/code&gt; 快）或&lt;strong&gt;队列&lt;/strong&gt;（比 &lt;code&gt;LinkedList&lt;/code&gt; 紧凑）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class ArrayDeque&amp;lt;E&amp;gt; extends AbstractCollection&amp;lt;E&amp;gt;
        implements Deque&amp;lt;E&amp;gt;, Cloneable, Serializable {

    transient Object[] elements;
    transient int head;
    transient int tail;
    private static final int MIN_INITIAL_CAPACITY = 8;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.3.2 关键特性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;容量始终为 2 的幂&lt;/li&gt;
&lt;li&gt;&lt;code&gt;head&lt;/code&gt; 与 &lt;code&gt;tail&lt;/code&gt; 在数组两端向中间扩展，到达边界时循环&lt;/li&gt;
&lt;li&gt;扩容时翻倍，并重新映射头尾位置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不允许 null 元素&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;5.3.3 用法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 当作栈
Deque&amp;lt;Integer&amp;gt; stack = new ArrayDeque&amp;lt;&amp;gt;();
stack.push(1);
stack.push(2);
stack.push(3);
while (!stack.isEmpty()) {
    System.out.println(stack.pop());  // 3 2 1
}

// 当作队列
Deque&amp;lt;Integer&amp;gt; queue = new ArrayDeque&amp;lt;&amp;gt;();
queue.offer(1);
queue.offer(2);
queue.offer(3);
while (!queue.isEmpty()) {
    System.out.println(queue.poll());  // 1 2 3
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.4 PriorityQueue&lt;/h3&gt;
&lt;h4&gt;5.4.1 底层结构&lt;/h4&gt;
&lt;p&gt;基于&lt;strong&gt;二叉小顶堆&lt;/strong&gt;（数组实现）实现的优先级队列。&lt;strong&gt;默认自然顺序&lt;/strong&gt;（最小堆），可通过 &lt;code&gt;Comparator&lt;/code&gt; 自定义。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class PriorityQueue&amp;lt;E&amp;gt; extends AbstractQueue&amp;lt;E&amp;gt;
        implements java.io.Serializable {

    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    transient Object[] queue;
    int size;
    private final Comparator&amp;lt;? super E&amp;gt; comparator;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.4.2 堆操作&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 上浮：插入时保证堆序
private void siftUpComparable(int k, E x) {
    Comparable&amp;lt;? super E&amp;gt; key = (Comparable&amp;lt;? super E&amp;gt;) x;
    while (k &amp;gt; 0) {
        int parent = (k - 1) &amp;gt;&amp;gt;&amp;gt; 1;
        Object e = queue[parent];
        if (key.compareTo((E) e) &amp;gt;= 0) break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = key;
}

// 下沉：删除堆顶时维持堆序
private void siftDownComparable(int k, E x) {
    int half = size &amp;gt;&amp;gt;&amp;gt; 1;
    while (k &amp;lt; half) {
        int child = (k &amp;lt;&amp;lt; 1) + 1;
        Object c = queue[child];
        int right = child + 1;
        if (right &amp;lt; size &amp;amp;&amp;amp; ((Comparable&amp;lt;? super E&amp;gt;) c).compareTo((E) queue[right]) &amp;gt; 0)
            c = queue[child = right];
        if (key.compareTo((E) c) &amp;lt;= 0) break;
        queue[k] = c;
        k = child;
    }
    queue[k] = key;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;5.4.3 时间复杂度&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;时间复杂度&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;offer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;poll&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;peek&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove(Object)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;5.4.4 使用建议&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;默认小顶堆：每次 &lt;code&gt;poll&lt;/code&gt; 取出最小元素&lt;/li&gt;
&lt;li&gt;大顶堆：传入 &lt;code&gt;Comparator.reverseOrder()&lt;/code&gt; 或 &lt;code&gt;Collections.reverseOrder()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不允许 null 元素&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非线程安全&lt;/strong&gt;，需要线程安全请用 &lt;code&gt;PriorityBlockingQueue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.5 BlockingQueue（并发队列入口）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;java.util.concurrent.BlockingQueue&lt;/code&gt; 增加了阻塞能力，是生产者-消费者模式的经典工具：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;实现类&lt;/th&gt;
&lt;th&gt;数据结构&lt;/th&gt;
&lt;th&gt;容量&lt;/th&gt;
&lt;th&gt;特点&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ArrayBlockingQueue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;数组&lt;/td&gt;
&lt;td&gt;有界（必填）&lt;/td&gt;
&lt;td&gt;单锁实现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LinkedBlockingQueue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;链表&lt;/td&gt;
&lt;td&gt;可选有界（默认 Integer.MAX_VALUE）&lt;/td&gt;
&lt;td&gt;双锁（take/put 各一锁）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PriorityBlockingQueue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;堆&lt;/td&gt;
&lt;td&gt;无界&lt;/td&gt;
&lt;td&gt;元素按优先级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SynchronousQueue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;不存储元素&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;插入必须等待取出&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DelayQueue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;堆&lt;/td&gt;
&lt;td&gt;无界&lt;/td&gt;
&lt;td&gt;元素到期才能取出&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LinkedTransferQueue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;链表&lt;/td&gt;
&lt;td&gt;无界&lt;/td&gt;
&lt;td&gt;&lt;code&gt;transfer&lt;/code&gt; 直接交付给消费者&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;BlockingQueue&amp;lt;Integer&amp;gt; queue = new ArrayBlockingQueue&amp;lt;&amp;gt;(10);
queue.put(1);     // 满则阻塞
Integer x = queue.take();  // 空则阻塞
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;六、集合工具类&lt;/h2&gt;
&lt;h3&gt;6.1 Collections&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Collections&lt;/code&gt; 提供了一组操作集合的&lt;strong&gt;静态方法&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;6.1.1 排序与查找&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 排序
Collections.sort(list);                          // 自然顺序
Collections.sort(list, comparator);              // 自定义比较器
Collections.reverse(list);                       // 倒序
Collections.shuffle(list);                       // 随机打乱
Collections.swap(list, i, j);                    // 交换
Collections.rotate(list, distance);               // 旋转

// 二分查找（要求已排序）
int idx = Collections.binarySearch(list, key);
int idx = Collections.binarySearch(list, key, comparator);

// 最大最小值
T max = Collections.max(coll);
T min = Collections.min(coll);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.1.2 同步包装（装饰器模式）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;T&amp;gt; syncList = Collections.synchronizedList(new ArrayList&amp;lt;&amp;gt;());
Set&amp;lt;T&amp;gt; syncSet  = Collections.synchronizedSet(new HashSet&amp;lt;&amp;gt;());
Map&amp;lt;K,V&amp;gt; syncMap = Collections.synchronizedMap(new HashMap&amp;lt;&amp;gt;());

// 用 synchronized 块包裹迭代
synchronized (syncList) {
    Iterator&amp;lt;T&amp;gt; it = syncList.iterator();
    while (it.hasNext()) { ... }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.1.3 不可变包装&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;T&amp;gt; unmodList = Collections.unmodifiableList(list);
Set&amp;lt;T&amp;gt; unmodSet  = Collections.unmodifiableSet(set);
Map&amp;lt;K,V&amp;gt; unmodMap = Collections.unmodifiableMap(map);

// 直接生成不可变集合
List&amp;lt;String&amp;gt; emptyList = Collections.emptyList();
Set&amp;lt;String&amp;gt; singletonSet = Collections.singleton(&quot;x&quot;);
List&amp;lt;String&amp;gt; nCopies = Collections.nCopies(10, &quot;x&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;不可变集合对任何修改操作都会抛 &lt;code&gt;UnsupportedOperationException&lt;/code&gt;。JDK 9+ 推荐用 &lt;code&gt;List.of(...)&lt;/code&gt; / &lt;code&gt;Map.of(...)&lt;/code&gt; / &lt;code&gt;Set.of(...)&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;6.1.4 单元素集合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; single = Collections.singletonList(&quot;only&quot;);
Set&amp;lt;String&amp;gt; singleSet = Collections.singleton(&quot;only&quot;);
Map&amp;lt;String, Integer&amp;gt; singleMap = Collections.singletonMap(&quot;k&quot;, 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.1.5 类型检查&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Collections.checkedList(list, String.class);  // 防止绕过泛型插入错误类型
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.2 Arrays&lt;/h3&gt;
&lt;h4&gt;6.2.1 数组与 List 互转&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = Arrays.asList(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);  // 固定大小 List
String[] arr = list.toArray(new String[0]);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Arrays.asList(...)&lt;/code&gt; 返回的是 &lt;code&gt;Arrays.ArrayList&lt;/code&gt;（内部类），&lt;strong&gt;不能 add / remove&lt;/strong&gt;，否则抛 &lt;code&gt;UnsupportedOperationException&lt;/code&gt;。想要可变 List 用 &lt;code&gt;new ArrayList&amp;lt;&amp;gt;(Arrays.asList(...))&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;6.2.2 排序与搜索&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Arrays.sort(arr);                          // 自然顺序
Arrays.sort(arr, fromIndex, toIndex);      // 部分排序
Arrays.sort(arr, comparator);              // 自定义
Arrays.parallelSort(arr);                  // 并行排序（JDK 8+，大数据量时性能更好）

int idx = Arrays.binarySearch(arr, key);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.2.3 填充 / 比较 / 复制&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Arrays.fill(arr, 0);                       // 全填
Arrays.fill(arr, fromIndex, toIndex, 0);   // 部分填

boolean eq = Arrays.equals(arr1, arr2);    // 元素相等
boolean deepEq = Arrays.deepEquals(o1, o2);// 多维数组

int[] copy = Arrays.copyOf(arr, newLength);
int[] range = Arrays.copyOfRange(arr, from, to);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.2.4 JDK 8+ Stream 互转&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;IntStream stream = Arrays.stream(arr);
String[] arr = stream.toArray(String[]::new);

List&amp;lt;String&amp;gt; list = Arrays.stream(arr).collect(Collectors.toList());
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6.2.5 JDK 9+ 便捷方法&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;int[] arr = { 3, 1, 4, 1, 5 };
int lo = Arrays.stream(arr).min().getAsInt();
int sum = Arrays.stream(arr).sum();
int[] sorted = Arrays.stream(arr).sorted().toArray();
boolean anyGt4 = Arrays.stream(arr).anyMatch(x -&amp;gt; x &amp;gt; 4);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6.3 不可变集合工厂（JDK 9+）&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; list = List.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);        // 不允许 null
Set&amp;lt;Integer&amp;gt; set = Set.of(1, 2, 3);
Map&amp;lt;String, Integer&amp;gt; map = Map.of(&quot;a&quot;, 1, &quot;b&quot;, 2);
Map&amp;lt;String, Integer&amp;gt; m2 = Map.ofEntries(
    Map.entry(&quot;a&quot;, 1),
    Map.entry(&quot;b&quot;, 2)
);

// JDK 10+ 提供 copyOf
List&amp;lt;String&amp;gt; copy = List.copyOf(list);              // null 不允许
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不可变集合优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;节省内存（紧凑表示）&lt;/li&gt;
&lt;li&gt;线程安全&lt;/li&gt;
&lt;li&gt;防止误修改&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;七、Java 8 Stream API&lt;/h2&gt;
&lt;p&gt;Stream 是对集合（Collection / 数组）的&lt;strong&gt;函数式操作抽象&lt;/strong&gt;，让链式转换、过滤、聚合、并行化变得简洁。&lt;strong&gt;Stream 不存储数据、不修改源集合&lt;/strong&gt;（除非明确终止操作设计如此）。&lt;/p&gt;
&lt;h3&gt;7.1 创建 Stream&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// 从集合
list.stream();                 // 顺序流
list.parallelStream();         // 并行流

// 从数组
Arrays.stream(arr);
Stream.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);     // Stream.of 静态方法

// 从文件 / 网络
Files.lines(Paths.get(&quot;a.txt&quot;), StandardCharsets.UTF_8);

// 数值流（避免装箱）
IntStream.range(0, 100);
IntStream.rangeClosed(0, 100);
LongStream, DoubleStream 类似

// 构建器
Stream.Builder&amp;lt;String&amp;gt; b = Stream.builder();
b.add(&quot;a&quot;).add(&quot;b&quot;);
Stream&amp;lt;String&amp;gt; s = b.build();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 中间操作（Intermediate）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;惰性求值&lt;/strong&gt;：不会立即执行，直到终止操作触发。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filter(Predicate)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;过滤元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;map(Function)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;一对一转换&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mapToInt / mapToLong / mapToDouble&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;转数值流&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flatMap(Function)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;一对多展平&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;distinct()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;去重（依赖 equals）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sorted()&lt;/code&gt; / &lt;code&gt;sorted(Comparator)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;排序&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;peek(Consumer)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;调试用消费，不改变元素&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;截取前 n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;skip(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;跳过前 n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;takeWhile(Predicate)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JDK 9+ 满足条件时取&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dropWhile(Predicate)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JDK 9+ 满足条件时丢弃&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;7.3 终止操作（Terminal）&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;触发整个流水线的执行&lt;/strong&gt;。&lt;/p&gt;
&lt;h4&gt;7.3.1 遍历与匹配&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;forEach(Consumer);
forEachOrdered(Consumer);  // 并行流中保证顺序
boolean anyMatch(Predicate);
boolean allMatch(Predicate);
boolean noneMatch(Predicate);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7.3.2 聚合&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;long count();
Optional&amp;lt;T&amp;gt; min(Comparator);
Optional&amp;lt;T&amp;gt; max(Comparator);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7.3.3 归约&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Optional&amp;lt;T&amp;gt; reduce(BinaryOperator&amp;lt;T&amp;gt; accumulator);
T reduce(T identity, BinaryOperator&amp;lt;T&amp;gt; accumulator);   // 含初始值
&amp;lt;U&amp;gt; U reduce(U identity, BiFunction&amp;lt;U, ? super T, U&amp;gt; accumulator, BinaryOperator&amp;lt;U&amp;gt; combiner);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7.3.4 收集（Collectors）&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// 转集合
List&amp;lt;T&amp;gt; list = stream.collect(Collectors.toList());
Set&amp;lt;T&amp;gt; set = stream.collect(Collectors.toSet());
Map&amp;lt;K, T&amp;gt; map = stream.collect(Collectors.toMap(t -&amp;gt; t.getKey(), t -&amp;gt; t));

// 拼接
String s = stream.collect(Collectors.joining(&quot;, &quot;, &quot;[&quot;, &quot;]&quot;));

// 分组
Map&amp;lt;K, List&amp;lt;T&amp;gt;&amp;gt; groups = stream.collect(Collectors.groupingBy(t -&amp;gt; t.getCategory()));
Map&amp;lt;K, Long&amp;gt; counts = stream.collect(Collectors.groupingBy(t -&amp;gt; t.getCategory(), Collectors.counting()));

// 分区
Map&amp;lt;Boolean, List&amp;lt;T&amp;gt;&amp;gt; parts = stream.collect(Collectors.partitioningBy(t -&amp;gt; t.isActive()));

// 汇总（数值）
DoubleSummaryStatistics stats = stream.collect(Collectors.summarizingDouble(T::getScore));
// sum / avg / min / max / count

// 自定义收集器
Collector.of(supplier, accumulator, combiner, finisher);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.4 数值流&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;IntStream is = IntStream.rangeClosed(1, 100);
int sum = is.sum();                       // 5050
OptionalDouble avg = is.average();
IntSummaryStatistics stat = is.summaryStatistics();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数值流提供 sum / average / max / min / range 等方法，避免装箱开销。&lt;/p&gt;
&lt;h3&gt;7.5 并行流&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;list.parallelStream().filter(x -&amp;gt; x &amp;gt; 0).forEach(System.out::println);

// 顺序保持
list.parallelStream().forEachOrdered(...);
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：并行流底层是 &lt;code&gt;ForkJoinPool.commonPool()&lt;/code&gt;。盲目使用反而更慢（线程切换开销），仅在数据量大、计算密集、源集合易切分（&lt;code&gt;ArrayList&lt;/code&gt; / &lt;code&gt;数组&lt;/code&gt;）时考虑。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;7.6 Stream 完整示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; names = Arrays.asList(&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;Dave&quot;);

// 找出长度 &amp;gt; 3 的名字，按长度升序，转大写，用逗号拼接
String result = names.stream()
    .filter(n -&amp;gt; n.length() &amp;gt; 3)
    .sorted(Comparator.comparingInt(String::length))
    .map(String::toUpperCase)
    .collect(Collectors.joining(&quot;, &quot;));
// &quot;ALICE, CHARLIE, DAVE&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.7 Optional&lt;/h3&gt;
&lt;p&gt;Stream 常与 &lt;code&gt;Optional&lt;/code&gt; 搭配：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Optional&amp;lt;String&amp;gt; opt = list.stream().findFirst();
opt.ifPresent(System.out::println);
String v = opt.orElse(&quot;default&quot;);
String v2 = opt.orElseGet(() -&amp;gt; &quot;lazy default&quot;);
String v3 = opt.orElseThrow(() -&amp;gt; new IllegalStateException(&quot;empty&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;八、并发集合（java.util.concurrent）&lt;/h2&gt;
&lt;h3&gt;8.1 整体分类&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Concurrent
├── ConcurrentMap
│   └── ConcurrentHashMap
├── ConcurrentQueue
│   ├── ConcurrentLinkedQueue   (无锁非阻塞)
│   └── ConcurrentLinkedDeque
├── BlockingQueue
│   ├── ArrayBlockingQueue
│   ├── LinkedBlockingQueue
│   ├── PriorityBlockingQueue
│   ├── SynchronousQueue
│   ├── DelayQueue
│   └── LinkedTransferQueue
├── CopyOnWrite
│   ├── CopyOnWriteArrayList
│   └── CopyOnWriteArraySet
└── 并发 List 接口包装
    └── Collections.synchronizedList
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.2 CopyOnWriteArrayList&lt;/h3&gt;
&lt;h4&gt;8.2.1 原理&lt;/h4&gt;
&lt;p&gt;写操作时把原数组&lt;strong&gt;整体复制一份&lt;/strong&gt;，在新数组上修改，然后用 CAS 替换引用。读操作&lt;strong&gt;完全无锁&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public boolean add(E e) {
    synchronized (lock) {
        Object[] es = getArray();
        int len = es.length;
        es = Arrays.copyOf(es, len + 1);
        es[len] = e;
        setArray(es);
        return true;
    }
}

private E get(Object[] a, int index) {
    return (E) a[index];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8.2.2 特性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;读极快、写极慢&lt;/strong&gt;（O(n) 拷贝）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;线程安全&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;迭代器是 &lt;strong&gt;快照式&lt;/strong&gt;，遍历时数据不变 → fail-safe&lt;/li&gt;
&lt;li&gt;适合&lt;strong&gt;读多写极少&lt;/strong&gt;、监听器列表、白名单等&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;8.2.3 CopyOnWriteArraySet&lt;/h4&gt;
&lt;p&gt;基于 &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;，特性类似，但内部去重需要遍历比对，开销更大。一般并发场景优先用 &lt;code&gt;ConcurrentHashMap.newKeySet()&lt;/code&gt; 替代。&lt;/p&gt;
&lt;h3&gt;8.3 ConcurrentLinkedQueue&lt;/h3&gt;
&lt;p&gt;无锁（CAS）的&lt;strong&gt;非阻塞线程安全队列&lt;/strong&gt;，基于 Michael &amp;amp; Scott 算法实现的无界链表。&lt;code&gt;size()&lt;/code&gt; 较慢（需要遍历），&lt;code&gt;offer&lt;/code&gt; / &lt;code&gt;poll&lt;/code&gt; 是 O(1)。&lt;/p&gt;
&lt;h3&gt;8.4 BlockingQueue 用法&lt;/h3&gt;
&lt;h4&gt;8.4.1 核心方法&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;行为&lt;/th&gt;
&lt;th&gt;抛异常&lt;/th&gt;
&lt;th&gt;返回特殊值&lt;/th&gt;
&lt;th&gt;阻塞&lt;/th&gt;
&lt;th&gt;超时&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;插入&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offer(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;put(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;offer(e, timeout, unit)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;取出&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;poll()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;take()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;poll(timeout, unit)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;检查&lt;/td&gt;
&lt;td&gt;&lt;code&gt;element()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peek()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4&gt;8.4.2 生产者-消费者模板&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;BlockingQueue&amp;lt;Task&amp;gt; queue = new LinkedBlockingQueue&amp;lt;&amp;gt;(1000);

// 生产者
new Thread(() -&amp;gt; {
    while (running) {
        Task t = produce();
        try { queue.put(t); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
    }
}).start();

// 消费者
new Thread(() -&amp;gt; {
    while (running) {
        try {
            Task t = queue.take();
            handle(t);
        } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
    }
}).start();
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8.4.3 DelayQueue&lt;/h4&gt;
&lt;p&gt;元素实现 &lt;code&gt;Delayed.getDelay(TimeUnit)&lt;/code&gt;，&lt;strong&gt;到期才能取出&lt;/strong&gt;，常用于定时任务、缓存过期。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class DelayedTask implements Delayed {
    private final long deadline;
    public long getDelay(TimeUnit unit) { return unit.convert(deadline - System.nanoTime(), NANOSECONDS); }
    public int compareTo(Delayed o) { return Long.compare(this.deadline, ((DelayedTask) o).deadline); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;8.4.4 SynchronousQueue&lt;/h4&gt;
&lt;p&gt;容量为 0，每个插入必须等待对应取出。常用于线程间直接交付任务。&lt;/p&gt;
&lt;h3&gt;8.5 ConcurrentSkipListMap / ConcurrentSkipListSet&lt;/h3&gt;
&lt;p&gt;基于&lt;strong&gt;跳表（SkipList）&lt;/strong&gt; 的并发有序 Map / Set，提供 O(log n) 的增删查，并发友好（无锁化分段 CAS）。&lt;/p&gt;
&lt;p&gt;适用场景：需要并发 + 有序 + 范围查询。&lt;/p&gt;
&lt;h3&gt;8.6 集合并发方案速查&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;推荐&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;高并发 KV 读写&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ConcurrentHashMap&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;并发有序 KV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ConcurrentSkipListMap&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;高并发读多写少 List&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CopyOnWriteArrayList&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;并发 Set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ConcurrentHashMap.newKeySet()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;生产者-消费者&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LinkedBlockingQueue&lt;/code&gt; / &lt;code&gt;ArrayBlockingQueue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;定时 / 延迟任务&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DelayQueue&lt;/code&gt; / &lt;code&gt;ScheduledExecutorService&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;线程间直接交换数据&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SynchronousQueue&lt;/code&gt; / &lt;code&gt;Exchanger&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;单线程&lt;/td&gt;
&lt;td&gt;不用并发行列，会拖慢性能&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;九、选型指南（一图速查）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;需要存什么？
├── 单列元素
│   ├── 允许重复 + 有序
│   │   ├── 随机访问多 → ArrayList
│   │   ├── 头尾操作多 → ArrayDeque
│   │   └── 高并发读少写 → CopyOnWriteArrayList
│   ├── 不可重复
│   │   ├── 无序 → HashSet
│   │   ├── 插入顺序 → LinkedHashSet
│   │   ├── 自然/比较器顺序 → TreeSet
│   │   ├── 枚举 → EnumSet
│   │   └── 高并发 → ConcurrentHashMap.newKeySet()
│   ├── FIFO 队列
│   │   ├── 单线程 → ArrayDeque
│   │   ├── 多线程无阻塞 → ConcurrentLinkedQueue
│   │   └── 多线程阻塞 → LinkedBlockingQueue / ArrayBlockingQueue
│   ├── LIFO 栈 → ArrayDeque
│   └── 优先级 → PriorityQueue / PriorityBlockingQueue
│
└── 键值对
    ├── 单线程
    │   ├── 无序 → HashMap
    │   ├── 插入顺序 → LinkedHashMap
    │   ├── LRU → LinkedHashMap(accessOrder=true)
    │   └── 有序 → TreeMap
    ├── 多线程 → ConcurrentHashMap
    ├── 多线程有序 → ConcurrentSkipListMap
    └── 已被淘汰 → Hashtable / Collections.synchronizedMap
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9.1 通用最佳实践&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;面向接口编程&lt;/strong&gt;：变量声明为 &lt;code&gt;List&lt;/code&gt; / &lt;code&gt;Map&lt;/code&gt;，按需选实现&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;预分配容量&lt;/strong&gt;：构造时给出预期大小，避免扩容&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;正确实现 hashCode / equals&lt;/strong&gt;：自定义 key / Set 元素必备&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不可变对象做 key&lt;/strong&gt;：String / Integer / Long 等是好的 key&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免在并发场景使用非线程安全集合&lt;/strong&gt;：尤其注意 HashMap / ArrayList 在多线程下可能死循环（扩容链表环）、数据丢失&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迭代时不要结构性修改&lt;/strong&gt;：需要则用 &lt;code&gt;Iterator.remove()&lt;/code&gt; 或 &lt;code&gt;ListIterator&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先使用 &lt;code&gt;ArrayDeque&lt;/code&gt; 替代 &lt;code&gt;Stack&lt;/code&gt; 和 &lt;code&gt;LinkedList&lt;/code&gt;&lt;/strong&gt; 作为栈/队列&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先使用 JDK 9+ &lt;code&gt;List.of(...)&lt;/code&gt; / &lt;code&gt;Map.of(...)&lt;/code&gt;&lt;/strong&gt; 表达不可变集合&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大数据 + 简单操作考虑 Stream&lt;/strong&gt;，但避免在循环中频繁创建 Stream&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;并行流谨慎使用&lt;/strong&gt;，先 benchmark&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;9.2 常见容量规划公式&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// Guava 的 Maps.newHashMapWithExpectedSize 同款
int expected = 1000;
int cap = (int) (expected / 0.75f + 1);
Map&amp;lt;K, V&amp;gt; map = new HashMap&amp;lt;&amp;gt;(cap);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;十、高频面试题汇总&lt;/h2&gt;
&lt;h3&gt;10.1 ArrayList vs LinkedList&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;ArrayList&lt;/th&gt;
&lt;th&gt;LinkedList&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;底层&lt;/td&gt;
&lt;td&gt;动态数组&lt;/td&gt;
&lt;td&gt;双向链表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;随机访问&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;头插&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;尾插&lt;/td&gt;
&lt;td&gt;均摊 O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;中间插删&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)（定位 + 改指针）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内存占用&lt;/td&gt;
&lt;td&gt;紧凑&lt;/td&gt;
&lt;td&gt;大（前后指针 + 包装对象）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;缓存局部性&lt;/td&gt;
&lt;td&gt;优&lt;/td&gt;
&lt;td&gt;差&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;实际工程中&lt;strong&gt;ArrayList 在绝大多数场景下更优&lt;/strong&gt;，因为 CPU 缓存命中率高、GC 压力小、连续内存访问快。&lt;/p&gt;
&lt;h3&gt;10.2 ArrayList 扩容机制&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;初始空数组，首次 add 才扩容到 10&lt;/li&gt;
&lt;li&gt;每次扩容 1.5 倍（&lt;code&gt;oldCapacity + oldCapacity &amp;gt;&amp;gt; 1&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;Arrays.copyOf&lt;/code&gt; 复制，开销是 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.3 HashMap put 流程&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;计算 key 的 hash（高 16 位异或低 16 位）&lt;/li&gt;
&lt;li&gt;若 table 为空则 resize&lt;/li&gt;
&lt;li&gt;计算桶下标 &lt;code&gt;i = (n - 1) &amp;amp; hash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;桶为空 → 直接放&lt;/li&gt;
&lt;li&gt;桶非空：
&lt;ul&gt;
&lt;li&gt;头节点匹配 → 覆盖&lt;/li&gt;
&lt;li&gt;红黑树节点 → 树插入&lt;/li&gt;
&lt;li&gt;链表 → 遍历到末尾追加；长度 ≥ 8 且 table 长度 ≥ 64 时树化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;size &amp;gt; threshold → resize&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;10.4 HashMap 树化的两个条件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;链表长度 ≥ &lt;code&gt;TREEIFY_THRESHOLD&lt;/code&gt; (8)&lt;/li&gt;
&lt;li&gt;table 长度 ≥ &lt;code&gt;MIN_TREEIFY_CAPACITY&lt;/code&gt; (64)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;否则优先扩容而不是树化。&lt;/p&gt;
&lt;h3&gt;10.5 HashMap 为什么线程不安全&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;扩容时多线程并发可能导致&lt;strong&gt;链表环&lt;/strong&gt;（JDK 7 的 transfer 用头插法；JDK 8 改用尾插法降低了概率，但仍不保证）&lt;/li&gt;
&lt;li&gt;put 时可能&lt;strong&gt;值覆盖&lt;/strong&gt;（非原子）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解决方案：使用 &lt;code&gt;ConcurrentHashMap&lt;/code&gt; 或 &lt;code&gt;Collections.synchronizedMap&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;10.6 ConcurrentHashMap 1.7 vs 1.8&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;版本&lt;/th&gt;
&lt;th&gt;数据结构&lt;/th&gt;
&lt;th&gt;并发控制&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1.7&lt;/td&gt;
&lt;td&gt;Segment + HashEntry + 链表&lt;/td&gt;
&lt;td&gt;Segment 锁（ReentrantLock）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.8&lt;/td&gt;
&lt;td&gt;Node + 链表/红黑树&lt;/td&gt;
&lt;td&gt;CAS + &lt;code&gt;synchronized&lt;/code&gt; 锁桶头节点&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;JDK 8 简化了实现、减少了锁粒度、引入红黑树优化极端冲突场景。&lt;/p&gt;
&lt;h3&gt;10.7 为什么 ConcurrentHashMap 不允许 null key/value&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;containsKey&lt;/code&gt; + &lt;code&gt;put&lt;/code&gt; 在并发下不能保证原子，若允许 null，无法区分&quot;key 不存在返回 null&quot;与&quot;key 存在但 value 是 null&quot;。&lt;/p&gt;
&lt;h3&gt;10.8 HashSet 怎么保证元素唯一&lt;/h3&gt;
&lt;p&gt;底层是 HashMap，元素作为 key、value 是固定常量 &lt;code&gt;PRESENT&lt;/code&gt;。&lt;code&gt;add(e)&lt;/code&gt; 通过 &lt;code&gt;map.put(e, PRESENT) == null&lt;/code&gt; 判断是否新增。&lt;/p&gt;
&lt;h3&gt;10.9 fail-fast vs fail-safe&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;机制&lt;/th&gt;
&lt;th&gt;触发&lt;/th&gt;
&lt;th&gt;表现&lt;/th&gt;
&lt;th&gt;典型代表&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fail-fast&lt;/td&gt;
&lt;td&gt;迭代时检测到 modCount 变化&lt;/td&gt;
&lt;td&gt;抛 &lt;code&gt;ConcurrentModificationException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ArrayList / HashMap / HashSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fail-safe&lt;/td&gt;
&lt;td&gt;迭代器在修改前的快照上工作&lt;/td&gt;
&lt;td&gt;不抛异常，但数据弱一致&lt;/td&gt;
&lt;td&gt;CopyOnWriteArrayList / ConcurrentHashMap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;10.10 Collection 与 Collections 区别&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;java.util.Collection&lt;/code&gt;：&lt;strong&gt;接口&lt;/strong&gt;，集合框架的根&lt;/li&gt;
&lt;li&gt;&lt;code&gt;java.util.Collections&lt;/code&gt;：&lt;strong&gt;工具类&lt;/strong&gt;，提供静态方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.11 Iterator 与 ListIterator&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;Iterator&lt;/th&gt;
&lt;th&gt;ListIterator&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;适用&lt;/td&gt;
&lt;td&gt;所有 Collection&lt;/td&gt;
&lt;td&gt;仅 List&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;遍历方向&lt;/td&gt;
&lt;td&gt;单向&lt;/td&gt;
&lt;td&gt;双向&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;修改&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove() / set() / add()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;索引&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nextIndex() / previousIndex()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;10.12 Arrays.asList 的坑&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;返回 &lt;code&gt;Arrays.ArrayList&lt;/code&gt;（内部类），&lt;strong&gt;固定大小&lt;/strong&gt;，不能 add/remove&lt;/li&gt;
&lt;li&gt;修改数组会影响 List（视图）&lt;/li&gt;
&lt;li&gt;期望可变用 &lt;code&gt;new ArrayList&amp;lt;&amp;gt;(Arrays.asList(...))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.13 equals 与 hashCode 契约&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;equals 相等&lt;/strong&gt; ⇒ &lt;strong&gt;hashCode 相等&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;hashCode 相等不一定 equals 相等（哈希冲突）&lt;/li&gt;
&lt;li&gt;自定义对象作为 HashMap key / HashSet 元素&lt;strong&gt;必须同时重写两者&lt;/strong&gt;，且参与 equals 比较的字段必须参与 hashCode 计算&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.14 HashMap 与 Hashtable 区别&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;HashMap&lt;/th&gt;
&lt;th&gt;Hashtable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;线程安全&lt;/td&gt;
&lt;td&gt;否&lt;/td&gt;
&lt;td&gt;是（全方法 synchronized）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;td&gt;允许 1 个 null key、多个 null value&lt;/td&gt;
&lt;td&gt;都不允许&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;性能&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推荐&lt;/td&gt;
&lt;td&gt;是&lt;/td&gt;
&lt;td&gt;已淘汰&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;10.15 Comparable vs Comparator&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;Comparable&lt;/th&gt;
&lt;th&gt;Comparator&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;位置&lt;/td&gt;
&lt;td&gt;类内部实现&lt;/td&gt;
&lt;td&gt;外部独立类 / Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;方法&lt;/td&gt;
&lt;td&gt;&lt;code&gt;compareTo(T)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;compare(T, T)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数量&lt;/td&gt;
&lt;td&gt;一个自然顺序&lt;/td&gt;
&lt;td&gt;多个独立策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collections.sort&lt;/td&gt;
&lt;td&gt;sort(list)&lt;/td&gt;
&lt;td&gt;sort(list, comparator)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;10.16 哪些集合是线程安全的&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;旧：&lt;code&gt;Vector&lt;/code&gt; / &lt;code&gt;Stack&lt;/code&gt; / &lt;code&gt;Hashtable&lt;/code&gt; / &lt;code&gt;Enumeration&lt;/code&gt; 相关&lt;/li&gt;
&lt;li&gt;工具包装：&lt;code&gt;Collections.synchronizedXxx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;并发包：&lt;code&gt;ConcurrentHashMap&lt;/code&gt; / &lt;code&gt;ConcurrentSkipListMap&lt;/code&gt; / &lt;code&gt;ConcurrentLinkedQueue&lt;/code&gt; / &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt; 等&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.17 集合的快速失败（fail-fast）原理&lt;/h3&gt;
&lt;p&gt;迭代器内部维护 &lt;code&gt;expectedModCount&lt;/code&gt;，每次 &lt;code&gt;next/remove&lt;/code&gt; 前检查 &lt;code&gt;modCount != expectedModCount&lt;/code&gt;，若不等则抛 &lt;code&gt;ConcurrentModificationException&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;10.18 优先级队列底层&lt;/h3&gt;
&lt;p&gt;二叉小顶堆（数组），父子通过下标计算：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;左子 = &lt;code&gt;2*i + 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;右子 = &lt;code&gt;2*i + 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;父 = &lt;code&gt;(i - 1) &amp;gt;&amp;gt; 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;10.19 BlockingQueue 核心方法&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;行为&lt;/th&gt;
&lt;th&gt;抛异常&lt;/th&gt;
&lt;th&gt;特殊值&lt;/th&gt;
&lt;th&gt;阻塞&lt;/th&gt;
&lt;th&gt;超时&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;插入&lt;/td&gt;
&lt;td&gt;add&lt;/td&gt;
&lt;td&gt;offer&lt;/td&gt;
&lt;td&gt;put&lt;/td&gt;
&lt;td&gt;offer(..., t, u)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;取出&lt;/td&gt;
&lt;td&gt;remove&lt;/td&gt;
&lt;td&gt;poll&lt;/td&gt;
&lt;td&gt;take&lt;/td&gt;
&lt;td&gt;poll(..., t, u)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;检查&lt;/td&gt;
&lt;td&gt;element&lt;/td&gt;
&lt;td&gt;peek&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;10.20 ConcurrentHashMap size 实现&lt;/h3&gt;
&lt;p&gt;类似 &lt;code&gt;LongAdder&lt;/code&gt;，用 &lt;code&gt;baseCount&lt;/code&gt; + &lt;code&gt;CounterCell[]&lt;/code&gt;。每个线程优先更新自己槽位的 &lt;code&gt;CounterCell.value&lt;/code&gt;，减少竞争，最后 sum 得到总数。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;十一、补充：Java 9~17 的新特性&lt;/h2&gt;
&lt;h3&gt;11.1 JDK 9&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;List.of(...)&lt;/code&gt; / &lt;code&gt;Set.of(...)&lt;/code&gt; / &lt;code&gt;Map.of(...)&lt;/code&gt; / &lt;code&gt;Map.ofEntries(...)&lt;/code&gt; 不可变集合工厂&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Stream.takeWhile&lt;/code&gt; / &lt;code&gt;dropWhile&lt;/code&gt; / &lt;code&gt;ofNullable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Optional.stream()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;11.2 JDK 10&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;List.copyOf(...)&lt;/code&gt; / &lt;code&gt;Map.copyOf(...)&lt;/code&gt; / &lt;code&gt;Set.copyOf(...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;局部变量类型推断 &lt;code&gt;var&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;11.3 JDK 11&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Collection.toArray(IntFunction)&lt;/code&gt; 默认方法&lt;/li&gt;
&lt;li&gt;集合 API 增强（如 &lt;code&gt;Set.of&lt;/code&gt; 已有接口签名）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;11.4 JDK 17&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Sealed Classes 增强模式匹配&lt;/li&gt;
&lt;li&gt;模式匹配 switch（预览）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这些特性在日常开发中可按需使用，最关键的还是集合本身的设计与原理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;十二、参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;《Effective Java》第 3 版 — Joshua Bloch（条目 45~58 集合与 lambda 最佳实践）&lt;/li&gt;
&lt;li&gt;《Java 核心技术》第 12 版 — Cay S. Horstmann&lt;/li&gt;
&lt;li&gt;《深入理解 Java 虚拟机》 — 周志明&lt;/li&gt;
&lt;li&gt;OpenJDK 源码 &lt;code&gt;java.util&lt;/code&gt; / &lt;code&gt;java.util.concurrent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Java 官方文档：&lt;a href=&quot;https://docs.oracle.com/en/java/javase/&quot;&gt;https://docs.oracle.com/en/java/javase/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;本文目标&lt;/strong&gt;：覆盖 Java 后端面试与日常开发中 90% 以上的集合知识。建议先理解各集合的&lt;strong&gt;底层数据结构&lt;/strong&gt;与&lt;strong&gt;复杂度&lt;/strong&gt;，再去记忆 API 与最佳实践。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>开学第一篇</title><link>http://72.155.89.172/posts/%E5%BC%80%E5%AD%A6%E7%AC%AC%E4%B8%80%E7%AF%87/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E5%BC%80%E5%AD%A6%E7%AC%AC%E4%B8%80%E7%AF%87/</guid><description>记录一下开学以来的感受</description><pubDate>Tue, 01 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;心态发生变化是第一个显著的感受&lt;/h1&gt;
&lt;p&gt;步入大三，心理感觉焦虑，同时拥有动力。我有了明确的方向：本科就业。&lt;/p&gt;
&lt;p&gt;背景：有java开发基础、平时也爱捣鼓一点小项目。&lt;/p&gt;
&lt;p&gt;优势：不用忍受做题，尽早步入社会，不付学费同时能去赚钱。&lt;/p&gt;
&lt;p&gt;缺点：没法进一步在学术圈深造，不能从事高端核心岗位。&lt;/p&gt;
&lt;h1&gt;感觉身心疲惫但很充实&lt;/h1&gt;
&lt;p&gt;开学前，暑假每天也没有完全闲着，每天都有在刷hot100，还有就是断断续续学一些课程，做了一些小项目，看了一些系统和网络的八股。一天可能没学多久，但总体来说是没闲着。自己完全独立做的项目是一个叫Study-Copilot的rag智能学习辅助程序，不过很多都是我不熟悉的技术栈，借助ai完成了很多。项目大概就是可以让agent看到飞书文档，文件等多源个人知识库，然后用户可以进行各种各样的提问，然后定制化生成用户的答案（例如复盘总结、评价、查找、或者可以帮助用户按照一定的规范整理文档），现在还在完善中。&lt;/p&gt;
&lt;p&gt;开学后，动力很足，每天都很充实，但这几天琐事还是太多了，算法和项目已经暂时停了。这几天在研究怎么在我服务器上搞一下小项目赚钱。虽然实现这个项目有点难度，同时还赚不到钱，但在这个过程中我学到了很多知识，只不过不是很深，但我的眼界变广了。而且有一个很重要的一点&lt;strong&gt;第一次把八股运用到实际中了&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个项目有关青龙面板，挂到服务器上自动做任务，但其中有很多困难，找cookie就是一个问题，有很多程序不开放cookie，个人很难拿到。需要很多计算机网络、系统相关知识（其实也并不需要，只不过教程里用到了，我恰好学过，于是就进一步深化了），例如CA证书等。同时了解到了客户端抓包工具的原理。&lt;/p&gt;
</content:encoded></item><item><title>git命令和pr操作</title><link>http://72.155.89.172/posts/git%E5%91%BD%E4%BB%A4%E5%92%8Cpr%E6%93%8D%E4%BD%9C/</link><guid isPermaLink="true">http://72.155.89.172/posts/git%E5%91%BD%E4%BB%A4%E5%92%8Cpr%E6%93%8D%E4%BD%9C/</guid><description>笔记：关于常用的git命令和pr操作</description><pubDate>Wed, 19 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[ ] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本人写的 &lt;a href=&quot;https://rcnujngjny8w.feishu.cn/wiki/JaCZweUVBixZ3DkVMmHc2m9Fn1c?from=from_copylink&quot;&gt;带图飞书文档&lt;/a&gt;、更加详细具体的可以参考&lt;a href=&quot;https://www.runoob.com/git/git-tutorial.html&quot;&gt;菜鸟&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;克隆远程仓库到本地&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git clone &amp;lt;仓库地址https或ssh（需要key）&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;展示当前仓库对应的远程仓库(展示推拉目的仓库链接)&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;展示当前分支名字&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git branch --show-current
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;创建新分支（注意分支命名规范）&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git checkout -b feature-login
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;展示本地文件跟踪、暂存情况&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;:::note
输出：&amp;lt;/br&amp;gt;
未跟踪（Untracked）： 新创建的文件最初是未跟踪的。它们存在于工作目录中，但没有被 Git 跟踪。&amp;lt;/br&amp;gt;
已跟踪（Tracked）： 通过 git add 命令将未跟踪的文件添加到暂存区后，文件变为已跟踪状态。&amp;lt;/br&amp;gt;
已修改（Modified）： 对已跟踪的文件进行更改后，这些更改会显示为已修改状态，但这些更改还未添加到暂存区。&amp;lt;/br&amp;gt;
已暂存（Staged）： 使用 git add 命令将修改过的文件添加到暂存区后，文件进入已暂存状态，等待提交。&amp;lt;/br&amp;gt;
已提交（Committed）： 使用 git commit 命令将暂存区的更改提交到本地仓库后，这些更改被记录下来，文件状态返回为已跟踪状态。&amp;lt;/br&amp;gt;
被 .gitignore 匹配的文件，即使存在于工作区，也不会显示为 Untracked。
:::&lt;/p&gt;
&lt;h1&gt;解释git的4个区域&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;工作区  (Working Directory)
实际修改文件的地方。在这里写代码、删删改改。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;暂存区 (Staging Area)
添加到暂存区指令：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;git add &amp;lt;filename or .&amp;gt; #意义：确认准备提交到本地仓库
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;本地仓库 (Local Repository)
提交到本地仓库指令：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;git commit -m &quot;feat: 添加登录功能的基础框架&quot; # 意义：改动正式成为了项目历史的一部分。即便你之后改乱了，也可以随时从这里找回。
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;远程仓库 (Remote)
在推送本地更改之前，最好从远程仓库拉取最新的更改，以避免冲突：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;git pull origin &amp;lt;main or feature-login&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提交指令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push -u origin feature-login #（第一次提交新分支要用 -u 创建） 意义：备份代码，并与团队共享进度。
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;合并更改&lt;/h1&gt;
&lt;h2&gt;1. 切换到目标分支（接收方）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git checkout main
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 确保目标分支是最新的（避免不必要的冲突）&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git pull origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 执行合并命令&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git merge feature-login
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;解决冲突&lt;/h1&gt;
&lt;p&gt;当合并过程中出现冲突时，Git 会标记冲突文件，你需要手动解决冲突。打开冲突文件，按照标记解决冲突。
标记冲突解决完成后暂存提交：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add &amp;lt;conflict-file-name&amp;gt;
git commit
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;删除分支&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git branch -d &amp;lt;branch name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;基础操作(核心汇总版)&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git reset # 撤销提交并取消暂存，改动还在工作区 回退到本地仓库的上一个版本
git rm filename.txt # 将文件从暂存区和工作区删除
git mv old_name.js new_name.js # 移动或重命名工作区文件
git checkout &amp;lt;branch-name&amp;gt; # 分支切换
git log # 查看提交记录
git pull # 下载远程代码并合并
git push # 推送并合并
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;分支管理&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;git checkout -b &amp;lt;branchname&amp;gt; # 创建并切换分支，没有-b 是切换
git branch # 查看所有分支
git branch -r # 查看远程分支
git branch -a # 查看所有本地和远程分支
git merge &amp;lt;branchname&amp;gt; # 将其他分支合并到当前分支
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;github贡献操作 pr（省略了常规git操作，包括分支等）&lt;/h1&gt;
&lt;h2&gt;先fork下来，再跟踪上游仓库（原作者仓库）upstream：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git remote add upstream https://github.com/agegr/pi-web.git 
#
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;（不要忘了新建分支）完成工作后对无关改动还原：&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git checkout upstream/main -- package-lock.json
# 解释：从远程追踪分支 upstream/main 中，强制恢复（或覆盖）当前工作区中的 package-lock.json 文件。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;review+跑测试&lt;/h2&gt;
&lt;p&gt;两套测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;node --experimental-strip-types --test lib/request-security.test.mjs 
npm test #（一般是作者设置的，看具体情况）
#命令分析
# 1. node --experimental-strip-types --test lib/request-security.test.mjs
# --experimental-strip-types: Node.js 的实验性标志，允许直接运行包含 TypeScript 类型注解的 .mjs 文件（无需编译）
# --test: 启用 Node.js 内置的测试运行器
# lib/request-security.test.mjs: 具体的测试文件
# 作用：直接使用 Node.js 原生测试框架运行一个特定的安全相关测试文件。
# 2. npm test
# 这是 npm 的标准脚本命令，会执行 package.json 中定义的 test 脚本。
# 作用：运行项目配置的完整测试套件。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;pr提交和issue提交：提交issue注意模版。到compare界面再提交pr&lt;/h2&gt;
&lt;p&gt;compare url：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://github.com/agegr/pi-web/compare/main...LCZcoding:fix/chromium-origin-port-stripping
# /agegr/pi-web/ 是原作者仓库位置，后面的是提交用户名字和提交内容标题
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>八股学习：探讨我学习八股的目的、方法</title><link>http://72.155.89.172/posts/%E5%85%AB%E8%82%A1%E5%AD%A6%E4%B9%A0%E6%8E%A2%E8%AE%A8%E6%88%91%E5%AD%A6%E4%B9%A0%E5%85%AB%E8%82%A1%E7%9A%84%E7%9B%AE%E7%9A%84%E6%96%B9%E6%B3%95/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E5%85%AB%E8%82%A1%E5%AD%A6%E4%B9%A0%E6%8E%A2%E8%AE%A8%E6%88%91%E5%AD%A6%E4%B9%A0%E5%85%AB%E8%82%A1%E7%9A%84%E7%9B%AE%E7%9A%84%E6%96%B9%E6%B3%95/</guid><description>探讨学习目的和方法论</description><pubDate>Tue, 18 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;目的&lt;/h1&gt;
&lt;p&gt;补全知识体系、拓展视野广度、应对面试&lt;/p&gt;
&lt;h1&gt;方法&lt;/h1&gt;
&lt;p&gt;八股来源：&lt;a href=&quot;https://xiaolincoding.com/&quot;&gt;小林coding&lt;/a&gt;、&lt;a href=&quot;https://javaguide.cn/&quot;&gt;javaguide&lt;/a&gt;、&lt;a href=&quot;https://www.nowcoder.com/&quot;&gt;牛客面经&lt;/a&gt;、&lt;a href=&quot;https://programmercarl.com/qita/4.zuiqiangbaguwen.html&quot;&gt;代码随想录的《最强八股文》&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;挑重要的精度、有疑问的搜索、总结记笔记写博客。-&amp;gt;费曼学习法&lt;/p&gt;
</content:encoded></item><item><title>如果说递归是新手学算法的第一道坎，那么回溯一定是第二道坎</title><link>http://72.155.89.172/posts/%E5%A6%82%E6%9E%9C%E8%AF%B4%E9%80%92%E5%BD%92%E6%98%AF%E6%96%B0%E6%89%8B%E5%AD%A6%E7%AE%97%E6%B3%95%E7%9A%84%E7%AC%AC%E4%B8%80%E9%81%93%E5%9D%8E%E9%82%A3%E4%B9%88%E5%9B%9E%E6%BA%AF%E4%B8%80%E5%AE%9A%E6%98%AF%E7%AC%AC%E4%BA%8C%E9%81%93%E5%9D%8E/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E5%A6%82%E6%9E%9C%E8%AF%B4%E9%80%92%E5%BD%92%E6%98%AF%E6%96%B0%E6%89%8B%E5%AD%A6%E7%AE%97%E6%B3%95%E7%9A%84%E7%AC%AC%E4%B8%80%E9%81%93%E5%9D%8E%E9%82%A3%E4%B9%88%E5%9B%9E%E6%BA%AF%E4%B8%80%E5%AE%9A%E6%98%AF%E7%AC%AC%E4%BA%8C%E9%81%93%E5%9D%8E/</guid><description>今天开始学回溯，刷了2道题，很难</description><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[ ] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;递归和回溯&lt;/h1&gt;
&lt;p&gt;递归是数学归纳法，不要去思考下一层，全程关注顶层就好。第一行一般是边界条件。怎么表示算法直觉或思想比较重要。&lt;/p&gt;
&lt;p&gt;回溯是高级版递归，套路（path、ans、带不带循环）、是否恢复现场&lt;/p&gt;
&lt;h1&gt;时空复杂度的思考方向&lt;/h1&gt;
&lt;p&gt;时间复杂度要从结果出发，一般是复制path的n*结果的个数&lt;/p&gt;
&lt;p&gt;空间复杂度不将结果的空间复杂度计算在内，计算的是辅助空间的复杂度，如果是引用也不计算在内。递归栈的深度+path的n一般就是空间复杂度&lt;/p&gt;
&lt;h1&gt;基础能力：如何编译通过&lt;/h1&gt;
&lt;p&gt;数据结构的选择、api的使用是算法的基础、是编译通过的保障。&lt;/p&gt;
</content:encoded></item><item><title>关于我把经纬度看成ip这件事</title><link>http://72.155.89.172/posts/%E5%85%B3%E4%BA%8E%E6%88%91%E6%8A%8A%E7%BB%8F%E7%BA%AC%E5%BA%A6%E7%9C%8B%E6%88%90ip%E8%BF%99%E4%BB%B6%E4%BA%8B/%E5%85%B3%E4%BA%8E%E6%88%91%E6%8A%8A%E7%BB%8F%E7%BA%AC%E5%BA%A6%E7%9C%8B%E6%88%90ip%E8%BF%99%E4%BB%B6%E4%BA%8B/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E5%85%B3%E4%BA%8E%E6%88%91%E6%8A%8A%E7%BB%8F%E7%BA%AC%E5%BA%A6%E7%9C%8B%E6%88%90ip%E8%BF%99%E4%BB%B6%E4%BA%8B/%E5%85%B3%E4%BA%8E%E6%88%91%E6%8A%8A%E7%BB%8F%E7%BA%AC%E5%BA%A6%E7%9C%8B%E6%88%90ip%E8%BF%99%E4%BB%B6%E4%BA%8B/</guid><pubDate>Sun, 16 Aug 2026 00:00:00 GMT</pubDate><content:encoded/></item><item><title>生活迷茫，丧失热爱，缺乏自律怎么办</title><link>http://72.155.89.172/posts/%E7%94%9F%E6%B4%BB%E8%BF%B7%E8%8C%AB%E4%B8%A7%E5%A4%B1%E7%83%AD%E7%88%B1%E7%BC%BA%E4%B9%8F%E8%87%AA%E5%BE%8B%E6%80%8E%E4%B9%88%E5%8A%9E/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E7%94%9F%E6%B4%BB%E8%BF%B7%E8%8C%AB%E4%B8%A7%E5%A4%B1%E7%83%AD%E7%88%B1%E7%BC%BA%E4%B9%8F%E8%87%AA%E5%BE%8B%E6%80%8E%E4%B9%88%E5%8A%9E/</guid><description>最近感觉迷茫，每天学习时长才不到2个小时，刷视频太多，思考一下人要怎么活</description><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[ ] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;链条：面临未来选择-&amp;gt;内心焦虑-&amp;gt;行动了但有人否认你的方向-&amp;gt;怀疑自己的方向-&amp;gt;沉溺享乐以逃避焦虑-&amp;gt;更加焦虑-&amp;gt;探寻意义&lt;/p&gt;
&lt;p&gt;于是，我打算静下心来好好看一个讲解的视频，相关链接：up主&lt;a href=&quot;https://www.bilibili.com/list/ml2796930533?oid=925570139&amp;amp;bvid=BV1zT4y13785&quot;&gt;SuccessDiary_帅帅&lt;/a&gt;，并在此写下一篇学习笔记。&lt;/p&gt;
&lt;h1&gt;剖析无法改变现状（或迷茫）的原因以及解决方案&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;想了很多，做了很少&lt;/li&gt;
&lt;li&gt;爱好无法养活自己&lt;/li&gt;
&lt;li&gt;经济条件不允许&lt;/li&gt;
&lt;li&gt;三分钟热度&lt;/li&gt;
&lt;li&gt;顾虑别人的看法&lt;/li&gt;
&lt;li&gt;问别人，但没得到答案&lt;/li&gt;
&lt;li&gt;学习或工作太忙了（这一点我暂时没有❌️）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;1. 想了很多，做了很少的原因和解决的代价&lt;/h2&gt;
&lt;p&gt;走出迷茫需要找到一个确定的指引，指引来自兴趣或选择。兴趣和选择都需要尝试。消耗时间金钱人际等成本。&lt;strong&gt;这意味着可能要承受风险，于是我们不敢迈出第一步，而待在迷茫的舒适圈中&lt;/strong&gt;。有点像《被讨厌的勇气》中的理论。&lt;/p&gt;
&lt;p&gt;:::important
沉溺舒适圈、害怕承担风险-&amp;gt;不敢迈出第一步 解决方案：自己渴望的真的是未来吗，如果是大胆做，勇敢承受可能到来的风险、尽自己所能降低风险。
:::&lt;/p&gt;
&lt;h2&gt;2. 爱好无法养活自己、经济条件不允许怎么办&lt;/h2&gt;
&lt;p&gt;:::important
爱好要寻找、要筛选、最关键的是要真正热爱。兴趣和生存和谐共生。量力而行
:::&lt;/p&gt;
&lt;h2&gt;3. 解决三分钟热度&lt;/h2&gt;
&lt;p&gt;缺少足够持久的驱动力、没有生活的主动权。
没有驱动力的原因：初期目标太大、太远。（付出不及预期）&lt;/p&gt;
&lt;p&gt;:::important
直接目标要可以累加（重复），而不是山峰的高度。将爬若干米作为直接目标，比一上来就将爬多高的山做为目标更重要。这只能是终极目标。
:::&lt;/p&gt;
&lt;h2&gt;4. 别人的答案、别人的看法的影响&lt;/h2&gt;
&lt;p&gt;任何人的观点，不能全部接受，要带有自己的见解，&lt;strong&gt;独立思考太重要&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;5. 学习或工作太忙了（这一点我暂时没有❌️未来可能会有吧，到时候再补充）&lt;/h2&gt;
&lt;h2&gt;❤ 心法态度&lt;/h2&gt;
&lt;p&gt;问自己一个问题：你担心的事可以解决吗？可以就去解决，担心什么；不可以就坦然接受，然后转向，没必要担心。&lt;/p&gt;
&lt;h2&gt;😊 建议&lt;/h2&gt;
&lt;h3&gt;1）. 理论和实践之间循环的做事方式（理论结合实践）&lt;/h3&gt;
&lt;p&gt;做事先有大致方向、目标，拆解目标，然后实践-补充理论-实践的循环。&lt;/p&gt;
&lt;h3&gt;2）. 慢下来找寻有趣的想法，并去实践。&lt;/h3&gt;
&lt;p&gt;离开短视频、游戏、工作等高强度、低发散的思维活动。静静地思考有哪些好玩的东西，然后去尝试实践，即使失败，在这个过程中，你也能学到很多有用的东西。&lt;/p&gt;
&lt;p&gt;学会乐观生活，感恩世界。创造和奉献。&lt;/p&gt;
&lt;h1&gt;一句话&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;万物唯心所现，唯识所变&lt;/strong&gt;                       --改编自《严华经》&lt;/p&gt;
&lt;p&gt;迷茫是心、行动、目标之间不匹配的结果。&lt;/p&gt;
</content:encoded></item><item><title>浅谈学习算法的感受（非大模型算法）</title><link>http://72.155.89.172/posts/%E6%B5%85%E8%B0%88%E5%AD%A6%E4%B9%A0%E7%AE%97%E6%B3%95%E7%9A%84%E6%84%9F%E5%8F%97%E9%9D%9E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%AE%97%E6%B3%95/</link><guid isPermaLink="true">http://72.155.89.172/posts/%E6%B5%85%E8%B0%88%E5%AD%A6%E4%B9%A0%E7%AE%97%E6%B3%95%E7%9A%84%E6%84%9F%E5%8F%97%E9%9D%9E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E7%AE%97%E6%B3%95/</guid><description>谈一下我学习力扣hot100的感受</description><pubDate>Fri, 14 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;[ ] 是否借助ai&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目前学了30多天，每天1-2道hot100，目前已经刷完了50多道，预计开学能刷到80道左右。&lt;/p&gt;
&lt;p&gt;之前因为有点想放松一下就耽搁了算法3天，今天刷第一题就感觉算法特别需要集中学习，一类题之间的关系特别紧密。应该长时间分类集中学习。复盘特别重要，算法直觉很重要，要同时关注算法思想和代码实现。&lt;/p&gt;
&lt;p&gt;具体的也不说了，总结几点建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;搞清楚自己为啥学算法：就业？喜欢？打比赛？ 其中每种目的都要有不同的学习算法的方法。hot100适合中大厂面试&lt;/li&gt;
&lt;li&gt;分类集中学习、思索同类之间的关联、复盘、算法直觉、代码实现、复杂度分析都很重要&lt;/li&gt;
&lt;li&gt;算法需要天赋，如果没有请及时看题解和大佬讲解。强推&lt;a href=&quot;https://space.bilibili.com/525438321?&quot;&gt;代码随想录&lt;/a&gt;和&lt;a href=&quot;https://leetcode.cn/u/jyd/&quot;&gt;K神&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;每天坚持1道找手感很重要&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>浅谈在AI时代学习计算机技术的心态调整和学习方式(此篇之前发表的都是模版草稿，请忽略)</title><link>http://72.155.89.172/posts/first/</link><guid isPermaLink="true">http://72.155.89.172/posts/first/</guid><description>进入陌生领域，80%的时间都是在选择、学习并适应新技术!</description><pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;[ ] 是否借助ai&lt;/p&gt;
&lt;p&gt;写在最前面 : 我认为啥也不会的情况下完全借助ai来学习新技术会有很多痛苦。 我对ai已经有点祛魅了，或者说我发自内心地肯定了AI时代下个人永远保持学习的重要意义。AI最适合干的事是提高效率，而&lt;strong&gt;不是从无到有&lt;/strong&gt;。
&amp;lt;/br&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;引入例子：搭建一个个人博客，面临的困难&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;更新：引入CICD，git push后github帮助服务器自动部署&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最近刷到了一个白嫖服务器时长的视频，然后就学着领取了一下。&lt;/p&gt;
&lt;p&gt;领取完要做点什么东西好呢? 和ai聊了很长时间发现我的服务器内存很有限，只能用不熟悉的技术部署完整的前后端服务.&amp;lt;/br&amp;gt;
然后就各种折腾，结果最后发现了很多需要权衡取舍的地方，比如博客外观优美的框架基本上就很难二次开发，因为我不是很懂前端技术.&lt;/p&gt;
&lt;p&gt;有几个核心问题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;服务器的内存有限，只有1g不能部署我熟悉的java后端.&lt;/li&gt;
&lt;li&gt;想部署后端，建议用 Go 和 SQLlite 我不熟悉而且暂时还没走到这一步.&lt;/li&gt;
&lt;li&gt;不想写前端，想抄模版，抄的模版基本上很难进行大的改变，也有可能是我的模版找的不合适。比较难接入后端。二创比较困难。要懂一点Astro，还要把框架的架构理解.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果从根本原因出发就是在工作量、复杂度、外观、可扩展性、性能、学习难度中进行&lt;strong&gt;架构上取舍&lt;/strong&gt;.&amp;lt;/br&amp;gt;
其中学习难度是以人的角度出发考虑的，其他的角度AI可以帮助你，但你和AI的对话中，AI必然不清楚你的技术水平，你也无法判断AI给的方案是否可行，而且有时候在设计提示词的时候很可能会忽略一些重要因素，忽略自己的接受能力.&lt;/p&gt;
&lt;p&gt;最后我怎么办了? 放弃了复杂的后端，专注于博客这一单一目标，&lt;strong&gt;减少AI带领我走迷宫&lt;/strong&gt;的机会，多提出确定性强的问题。其他的技术，等我慢慢渗透，&lt;strong&gt;一次只做一件事&lt;/strong&gt;，而不是一上来就从了解跳跃到做好。&lt;/p&gt;
&lt;h1&gt;浅谈AI时代的技术学习和心态调整&lt;/h1&gt;
&lt;h2&gt;心态调整&lt;/h2&gt;
&lt;p&gt;网上都在唱衰技术，唱衰计算机，我认为这并不可取，反而&lt;strong&gt;徒增焦虑&lt;/strong&gt;。现在明确的是AI可以提效，AI让懂某一技术的人干的更快甚至更好。如果需求不变的情况下，也就是工作任务不变的情况下，计算机就业环境会艰难很多。但不同的是，AI带来了更多和&lt;strong&gt;更加精细化&lt;/strong&gt;的需求，AI正在赋能各行各业。和土木不同（国内的土木人才需求可能有点少？这个专业出国应该好点？）计算机需求减小可能有，但还是一个以技术为王的专业，完全靠技术吃饭。计算机的供需比在下降这是网上唱衰计算机的主要原因，我认为计算机确实是‘衰’了，但只不过是从高高在上的版本之子变成了&lt;strong&gt;普通的、有竞争压力的、需要卷的专业&lt;/strong&gt;。踏踏实实认真学习依旧是最重要的一点。&lt;/p&gt;
&lt;h2&gt;技术学习&lt;/h2&gt;
&lt;h3&gt;夯实基础还重不重要？&lt;/h3&gt;
&lt;p&gt;我认为重要或者说有必要。&lt;/p&gt;
&lt;p&gt;有些才入行的程序员说很多技术没用了，我认为只是暂时没用到，或者他们没有真正深入的理解他们的技术，&lt;strong&gt;没有真正理性评估基础的重要性&lt;/strong&gt;。况且，基础的练习是入行的第一步。&lt;/p&gt;
&lt;h3&gt;该怎样学习？（仅供参考）&lt;/h3&gt;
&lt;p&gt;不推荐的学习方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;完全的培训班式学习（诚然，这种方法能够找到工作，不至于毕业就失业。但他有一个核心的问题就是这种学习方式可能忽略了人的主体性，人有自己的兴趣，有自己的选择。如果完全按照标准路线走可能会面临高度的&lt;strong&gt;同质化竞争&lt;/strong&gt;，最后导向学历竞争等其他非核心方向。）&lt;/li&gt;
&lt;li&gt;完全AI指导学习（具体原因可见最上面的博客例子）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;推荐的学习方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;以兴趣为导向&lt;/strong&gt;（比如你喜欢玩什么游戏，你不要只去玩游戏，你可以寻找相关的开源项目，插件什么的，然后学习，使用，然后二次开发，然后完全独立开发.以开发这一目的为导向，&lt;strong&gt;不要过度在意自己学的东西到底有没有用&lt;/strong&gt;。比如你喜欢看视频，那么有没有什么插件，可以优化你看视频的这一过程，比如一些互动，什么的。）&lt;/li&gt;
&lt;li&gt;参考常见路线（这一点培训班做的比较好。&lt;strong&gt;技术栈要广，要杂，更要全&lt;/strong&gt;! 假如你的兴趣就是做做游戏插件，写写小demo或个人用的项目，这很可能不足以在业界站稳脚跟。推荐大家去好好的系统学习一门编程语言，包括业界需要的大部分知识。系统的背一下常见八股，写一些基础算法。还有最重要的一点是&lt;strong&gt;拥抱新技术&lt;/strong&gt;。）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我认为&lt;strong&gt;企业真正需要的是有能力,能够持续学习并且热爱这一行的人。&lt;/strong&gt;&lt;/p&gt;
</content:encoded></item><item><title>Markdown Extended Features</title><link>http://72.155.89.172/posts/markdown-extended/</link><guid isPermaLink="true">http://72.155.89.172/posts/markdown-extended/</guid><description>Read more about Markdown features in Fuwari</description><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;GitHub Repository Cards&lt;/h2&gt;
&lt;p&gt;You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API.&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Fabrizz/MMM-OnSpotify&quot;}&lt;/p&gt;
&lt;p&gt;Create a GitHub repository card with the code &lt;code&gt;::github{repo=&quot;&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;&quot;}&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;::github{repo=&quot;saicaca/fuwari&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Admonitions&lt;/h2&gt;
&lt;p&gt;Following types of admonitions are supported: &lt;code&gt;note&lt;/code&gt; &lt;code&gt;tip&lt;/code&gt; &lt;code&gt;important&lt;/code&gt; &lt;code&gt;warning&lt;/code&gt; &lt;code&gt;caution&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;:::note
Highlights information that users should take into account, even when skimming.
:::&lt;/p&gt;
&lt;p&gt;:::tip
Optional information to help a user be more successful.
:::&lt;/p&gt;
&lt;p&gt;:::important
Crucial information necessary for users to succeed.
:::&lt;/p&gt;
&lt;p&gt;:::warning
Critical content demanding immediate user attention due to potential risks.
:::&lt;/p&gt;
&lt;p&gt;:::caution
Negative potential consequences of an action.
:::&lt;/p&gt;
&lt;h3&gt;Basic Syntax&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;:::note
Highlights information that users should take into account, even when skimming.
:::

:::tip
Optional information to help a user be more successful.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom Titles&lt;/h3&gt;
&lt;p&gt;The title of the admonition can be customized.&lt;/p&gt;
&lt;p&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GitHub Syntax&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;a href=&quot;https://github.com/orgs/community/discussions/16925&quot;&gt;The GitHub syntax&lt;/a&gt; is also supported.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [!NOTE]
&amp;gt; The GitHub syntax is also supported.

&amp;gt; [!TIP]
&amp;gt; The GitHub syntax is also supported.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Spoiler&lt;/h3&gt;
&lt;p&gt;You can add spoilers to your text. The text also supports &lt;strong&gt;Markdown&lt;/strong&gt; syntax.&lt;/p&gt;
&lt;p&gt;The content :spoiler[is hidden &lt;strong&gt;ayyy&lt;/strong&gt;]!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The content :spoiler[is hidden **ayyy**]!

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Expressive Code Example</title><link>http://72.155.89.172/posts/expressive-code/</link><guid isPermaLink="true">http://72.155.89.172/posts/expressive-code/</guid><description>How code blocks look in Markdown using Expressive Code.</description><pubDate>Wed, 10 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here, we&apos;ll explore how code blocks look using &lt;a href=&quot;https://expressive-code.com/&quot;&gt;Expressive Code&lt;/a&gt;. The provided examples are based on the official documentation, which you can refer to for further details.&lt;/p&gt;
&lt;h2&gt;Expressive Code&lt;/h2&gt;
&lt;h3&gt;Syntax Highlighting&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/syntax-highlighting/&quot;&gt;Syntax Highlighting&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Regular syntax highlighting&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;This code is syntax highlighted!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Rendering ANSI escape sequences&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ANSI colors:
- Regular: [31mRed[0m [32mGreen[0m [33mYellow[0m [34mBlue[0m [35mMagenta[0m [36mCyan[0m
- Bold:    [1;31mRed[0m [1;32mGreen[0m [1;33mYellow[0m [1;34mBlue[0m [1;35mMagenta[0m [1;36mCyan[0m
- Dimmed:  [2;31mRed[0m [2;32mGreen[0m [2;33mYellow[0m [2;34mBlue[0m [2;35mMagenta[0m [2;36mCyan[0m

256 colors (showing colors 160-177):
[38;5;160m160 [38;5;161m161 [38;5;162m162 [38;5;163m163 [38;5;164m164 [38;5;165m165[0m
[38;5;166m166 [38;5;167m167 [38;5;168m168 [38;5;169m169 [38;5;170m170 [38;5;171m171[0m
[38;5;172m172 [38;5;173m173 [38;5;174m174 [38;5;175m175 [38;5;176m176 [38;5;177m177[0m

Full RGB colors:
[38;2;34;139;34mForestGreen - RGB(34, 139, 34)[0m

Text formatting: [1mBold[0m [2mDimmed[0m [3mItalic[0m [4mUnderline[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Editor &amp;amp; Terminal Frames&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/frames/&quot;&gt;Editor &amp;amp; Terminal Frames&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Code editor frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Title attribute example&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- src/content/index.html --&amp;gt;
&amp;lt;div&amp;gt;File name comment example&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Terminal frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;This terminal frame has no title&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;Write-Output &quot;This one has a title!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Overriding frame types&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Look ma, no frame!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;# Without overriding, this would be a terminal frame
function Watch-Tail { Get-Content -Tail 20 -Wait $args }
New-Alias tail Watch-Tail
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Text &amp;amp; Line Markers&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/text-markers/&quot;&gt;Text &amp;amp; Line Markers&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Marking full lines &amp;amp; line ranges&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range &quot;7-8&quot;
// Line 8 - targeted by range &quot;7-8&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting line marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;this line is marked as deleted&apos;)
  // This line and the next one are marked as inserted
  console.log(&apos;this is the second inserted line&apos;)

  return &apos;this line uses the neutral default marker type&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding labels to line markers&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}
  value={value}
  className={buttonClassName}
  disabled={disabled}
  active={active}
&amp;gt;
  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding long labels on their own lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}

  value={value}
  className={buttonClassName}

  disabled={disabled}
  active={active}
&amp;gt;

  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Using diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;+this line will be marked as inserted
-this line will be marked as deleted
this is a regular line
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+this is an actual diff file
-all contents will remain unmodified
 no whitespace will be removed either
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Combining syntax highlighting with diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  function thisIsJavaScript() {
    // This entire block gets highlighted as JavaScript,
    // and we can still add diff markers to it!
-   console.log(&apos;Old code to be removed&apos;)
+   console.log(&apos;New and shiny code!&apos;)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Marking individual text inside lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  // Mark any given text inside lines
  return &apos;Multiple matches of the given text are supported&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Regular expressions&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;The words yes and yep will be marked.&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Escaping forward slashes&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Test&quot; &amp;gt; /home/test.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting inline marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;These are inserted and deleted marker types&apos;);
  // The return statement uses the default marker type
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Word Wrap&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/word-wrap/&quot;&gt;Word Wrap&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Configuring word wrap per block&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Configuring indentation of wrapped lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent (enabled by default)
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Collapsible Sections&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/collapsible-sections/&quot;&gt;Collapsible Sections&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// All this boilerplate setup code will be collapsed
import { someBoilerplateEngine } from &apos;@example/some-boilerplate&apos;
import { evenMoreBoilerplate } from &apos;@example/even-more-boilerplate&apos;

const engine = someBoilerplateEngine(evenMoreBoilerplate())

// This part of the code will be visible by default
engine.doSomething(1, 2, 3, calcFn)

function calcFn() {
  // You can have multiple collapsed sections
  const a = 1
  const b = 2
  const c = a + b

  // This will remain visible
  console.log(`Calculation result: ${a} + ${b} = ${c}`)
  return c
}

// All this code until the end of the block will be collapsed again
engine.closeConnection()
engine.freeMemory()
engine.shutdown({ reason: &apos;End of example boilerplate code&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Line Numbers&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/line-numbers/&quot;&gt;Line Numbers&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Displaying line numbers per block&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// This code block will show line numbers
console.log(&apos;Greetings from line 2!&apos;)
console.log(&apos;I am on line 3&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Line numbers are disabled for this block
console.log(&apos;Hello?&apos;)
console.log(&apos;Sorry, do you know what line I am on?&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Changing the starting line number&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Greetings from line 5!&apos;)
console.log(&apos;I am on line 6&apos;)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Simple Guides for Fuwari</title><link>http://72.155.89.172/posts/guide/</link><guid isPermaLink="true">http://72.155.89.172/posts/guide/</guid><description>How to use this blog template.</description><pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover image source: &lt;a href=&quot;https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This blog template is built with &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;. For the things that are not mentioned in this guide, you may find the answers in the &lt;a href=&quot;https://docs.astro.build/&quot;&gt;Astro Docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Front-matter of Posts&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;---
title: My First Blog Post
published: 2023-09-09
description: This is the first post of my new Astro blog.
image: ./cover.jpg
tags: [Foo, Bar]
category: Front-end
draft: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The title of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The date the post was published.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A short description of the post. Displayed on index page.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The cover image path of the post.&amp;lt;br/&amp;gt;1. Start with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;: Use web image&amp;lt;br/&amp;gt;2. Start with &lt;code&gt;/&lt;/code&gt;: For image in &lt;code&gt;public&lt;/code&gt; dir&amp;lt;br/&amp;gt;3. With none of the prefixes: Relative to the markdown file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The tags of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The category of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draft&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If this post is still a draft, which won&apos;t be displayed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Where to Place the Post Files&lt;/h2&gt;
&lt;p&gt;Your post files should be placed in &lt;code&gt;src/content/posts/&lt;/code&gt; directory. You can also create sub-directories to better organize your posts and assets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/content/posts/
├── post-1.md
└── post-2/
    ├── cover.png
    └── index.md
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Markdown Example</title><link>http://72.155.89.172/posts/markdown/</link><guid isPermaLink="true">http://72.155.89.172/posts/markdown/</guid><description>A simple example of a Markdown blog post.</description><pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;An h1 header&lt;/h1&gt;
&lt;p&gt;Paragraphs are separated by a blank line.&lt;/p&gt;
&lt;p&gt;2nd paragraph. &lt;em&gt;Italic&lt;/em&gt;, &lt;strong&gt;bold&lt;/strong&gt;, and &lt;code&gt;monospace&lt;/code&gt;. Itemized lists
look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this one&lt;/li&gt;
&lt;li&gt;that one&lt;/li&gt;
&lt;li&gt;the other one&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that --- not considering the asterisk --- the actual text
content starts at 4-columns in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Block quotes are
written like so.&lt;/p&gt;
&lt;p&gt;They can span multiple paragraphs,
if you like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., &quot;it&apos;s all
in chapters 12--14&quot;). Three dots ... will be converted to an ellipsis.
Unicode is supported. ☺&lt;/p&gt;
&lt;h2&gt;An h2 header&lt;/h2&gt;
&lt;p&gt;Here&apos;s a numbered list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;first item&lt;/li&gt;
&lt;li&gt;second item&lt;/li&gt;
&lt;li&gt;third item&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note again how the actual text starts at 4 columns in (4 characters
from the left side). Here&apos;s a code sample:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Let me re-iterate ...
for i in 1 .. 10 { do-something(i) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you probably guessed, indented 4 spaces. By the way, instead of
indenting the block, you can use delimited blocks, if you like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;define foobar() {
    print &quot;Welcome to flavor country!&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(which makes copying &amp;amp; pasting easier). You can optionally mark the
delimited block for Pandoc to syntax highlight it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import time
# Quick, count to ten!
for i in range(10):
    # (but not *too* quick)
    time.sleep(0.5)
    print i
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;An h3 header&lt;/h3&gt;
&lt;p&gt;Now a nested list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, get these ingredients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;carrots&lt;/li&gt;
&lt;li&gt;celery&lt;/li&gt;
&lt;li&gt;lentils&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boil some water.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dump everything in the pot and follow
this algorithm:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; find wooden spoon
 uncover pot
 stir
 cover pot
 balance wooden spoon precariously on pot handle
 wait 10 minutes
 goto first step (or shut off burner when done)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do not bump wooden spoon or it will fall.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice again how text always lines up on 4-space indents (including
that last line which continues item 3 above).&lt;/p&gt;
&lt;p&gt;Here&apos;s a link to &lt;a href=&quot;http://foo.bar&quot;&gt;a website&lt;/a&gt;, to a &lt;a href=&quot;local-doc.html&quot;&gt;local
doc&lt;/a&gt;, and to a &lt;a href=&quot;#an-h2-header&quot;&gt;section heading in the current
doc&lt;/a&gt;. Here&apos;s a footnote [^1].&lt;/p&gt;
&lt;p&gt;[^1]: Footnote text goes here.&lt;/p&gt;
&lt;p&gt;Tables can look like this:&lt;/p&gt;
&lt;p&gt;size material color&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;9 leather brown
10 hemp canvas natural
11 glass transparent&lt;/p&gt;
&lt;p&gt;Table: Shoes, their sizes, and what they&apos;re made of&lt;/p&gt;
&lt;p&gt;(The above is the caption for the table.) Pandoc also supports
multi-line tables:&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;keyword text&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;red Sunsets, apples, and
other red or reddish
things.&lt;/p&gt;
&lt;p&gt;green Leaves, grass, frogs
and other things it&apos;s
not easy being.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;A horizontal rule follows.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here&apos;s a definition list:&lt;/p&gt;
&lt;p&gt;apples
: Good for making applesauce.
oranges
: Citrus!
tomatoes
: There&apos;s no &quot;e&quot; in tomatoe.&lt;/p&gt;
&lt;p&gt;Again, text is indented 4 spaces. (Put a blank line between each
term/definition pair to spread things out more.)&lt;/p&gt;
&lt;p&gt;Here&apos;s a &quot;line block&quot;:&lt;/p&gt;
&lt;p&gt;| Line one
| Line too
| Line tree&lt;/p&gt;
&lt;p&gt;and images can be specified like so:&lt;/p&gt;
&lt;p&gt;Inline math equations go in like so: $\omega = d\phi / dt$. Display
math should get its own line and be put in in double-dollarsigns:&lt;/p&gt;
&lt;p&gt;$$I = \int \rho R^{2} dV$$&lt;/p&gt;
&lt;p&gt;$$
\begin{equation*}
\pi
=3.1415926535
;8979323846;2643383279;5028841971;6939937510;5820974944
;5923078164;0628620899;8628034825;3421170679;\ldots
\end{equation*}
$$&lt;/p&gt;
&lt;p&gt;And note that you can backslash-escape any punctuation characters
which you wish to be displayed literally, ex.: `foo`, *bar*, etc.&lt;/p&gt;
</content:encoded></item><item><title>Include Video in the Posts</title><link>http://72.155.89.172/posts/video/</link><guid isPermaLink="true">http://72.155.89.172/posts/video/</guid><description>This post demonstrates how to include embedded video in a blog post.</description><pubDate>Tue, 01 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just copy the embed code from YouTube or other platforms, and paste it in the markdown file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: Include Video in the Post
published: 2023-10-19
// ...
---

&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;YouTube&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h2&gt;Bilibili&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1fK4y1s7Qf&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>