<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>数据结构 on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/</link><description>Recent content in 数据结构 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 11 Apr 2026 10:50:21 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/index.xml" rel="self" type="application/rss+xml"/><item><title>让Agent快上100倍的秘密，其实藏在一本大一计算机教科书里</title><link>https://xiaobox.github.io/p/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/</link><pubDate>Sat, 11 Apr 2026 10:50:21 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/cover.jpg" alt="Featured image of post 让Agent快上100倍的秘密，其实藏在一本大一计算机教科书里" /&gt;&lt;p&gt;事情是这样的。&lt;/p&gt;
&lt;p&gt;最近我几乎每天都在用Claude Code写东西。用得越多，我越产生一种奇怪的感觉。&lt;/p&gt;
&lt;p&gt;就是你给它下完一个任务之后，它开始一步一步地干活。先是啪，读了一个文件。然后哒，想了几秒。然后啪，又打开一个文件。再想几秒。再打开一个文件。就这么一直持续下去。&lt;/p&gt;
&lt;p&gt;你坐在椅子上看着进度条一格一格地亮起来，心里清楚得不能再清楚，这十个文件它明明可以一起读的，它们之间根本没有任何依赖关系。&lt;/p&gt;
&lt;p&gt;但它就是不。它就是要一个接一个地来。&lt;/p&gt;
&lt;p&gt;一时间无语凝噎。&lt;/p&gt;
&lt;p&gt;后来我跟几个同样重度用Agent的朋友聊了一下，他们也都有这个感受。说真的我始终觉得这是现在所有Agent产品共同的一个病，不管是 &lt;code&gt;Claude Code&lt;/code&gt;、&lt;code&gt;Cursor&lt;/code&gt;、&lt;code&gt;Manus&lt;/code&gt;还是那些MCP插件，只要你让它干稍微复杂一点的活，你就会看到它在那里慢悠悠地一步一步走，像一个做事非常有耐心但完全不会一心二用的老实人。&lt;/p&gt;
&lt;p&gt;前两天跟朋友吐槽这事的时候，我又想起了两年前Berkeley那帮人写的一篇论文。论文叫LLMCompiler，2024年就发在ICML上了，现在回头看它也不算新东西。但每次我被Agent气到的时候都会想起它，觉得它的思路到今天都没过时，甚至越品越有味道。&lt;/p&gt;
&lt;p&gt;它当时就已经把这个病的根源讲得很清楚了，这个慢不是LLM的错，也不是任务复杂度的错，是我们给它用的那套调度系统，还停留在1960年代的水平。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/001-5d4b1967.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;这篇论文的名字挺干的，叫**《An LLM Compiler for Parallel Function Calling》**，ICML 2024。作者是Sehoon Kim、Amir Gholami那帮人，都在Berkeley和LBNL。它不是今年的新论文，但在我心里一直是Agent方向上最被低估的几篇之一。&lt;/p&gt;
&lt;p&gt;它在做的事其实非常cool。&lt;/p&gt;
&lt;p&gt;它在把大学一年级《计算机组成原理》那本书里的东西，原样搬到LLM的世界里。&lt;/p&gt;
&lt;p&gt;坦率的讲，你想想看过去60年整个计算机体系结构的历史，其实就是一部「怎么让本来是串行的指令跑得更并行」的历史。指令流水线、乱序执行、超标量、分支预测，这些听着就头大的名词，说到底都是在干一件事，就是让CPU不要一条指令一条指令傻乎乎地等，能同时干的活就一起干。&lt;/p&gt;
&lt;p&gt;这套东西人类已经研究得非常透了。透到什么程度呢？透到你今天买一颗普通的i5芯片，它每个时钟周期能同时发射的指令数，大概是80年代那种整栋楼的超级计算机的水平。&lt;/p&gt;
&lt;p&gt;但是。&lt;/p&gt;
&lt;p&gt;当我们把LLM当成一种新型处理器去用的时候，这套智慧全忘了。&lt;/p&gt;
&lt;p&gt;现在几乎所有的Agent框架，底层都是一个叫ReAct的东西。它是Yao等人2022年提的，全称是Reason + Act。工作方式非常朴素，想一步，做一步，看结果，再想一步，再做一步，再看结果。它是一个循环。&lt;/p&gt;
&lt;p&gt;听着很自然对吧？它确实自然。但你仔细看就会发现，这玩意从执行效率上来说，跟那种每次只能执行一条指令、做完一条才开始下一条的远古处理器，是一样的。&lt;/p&gt;
&lt;p&gt;一次一条。干等。&lt;/p&gt;
&lt;p&gt;而且这个问题在越来越多的Agent场景里暴露得越来越厉害，因为我们现在给Agent的活越来越复杂，一次要调用的工具越来越多。ReAct的串行执行就成了一个越来越重的镣铐。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;回到LLMCompiler这块。&lt;/p&gt;
&lt;p&gt;作者的思路简单粗暴，既然Agent执行工具调用的过程跟CPU执行指令长得一样，那就直接套编译器的架构好了。他们搞了三个组件。&lt;/p&gt;
&lt;p&gt;第一个叫 &lt;strong&gt;Function Calling Planner&lt;/strong&gt;，函数调用规划器。你可以把它想象成编译器里那个分析语义、构建依赖图的部分。用户给了一个问题，比如论文里举的那个例子，「微软的市值需要涨多少才能超过苹果？」，Planner要做的事情是先把这个问题拆成几个独立的任务，再搞清楚这些任务之间谁依赖谁。&lt;/p&gt;
&lt;p&gt;它会拆成三步。&lt;/p&gt;
&lt;p&gt;一，去查微软的市值。 二，去查苹果的市值。 三，用一个数学工具做减法，把差值算出来。&lt;/p&gt;
&lt;p&gt;然后它会发现一件事，任务1和任务2，彼此没有任何关系。它们完全可以同时去查。只有任务3需要等前两个都拿到结果。&lt;/p&gt;
&lt;p&gt;这就是一张 &lt;strong&gt;DAG&lt;/strong&gt;，有向无环图，编译器里最核心的数据结构之一。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/002-04810f26.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;第二个组件叫 &lt;strong&gt;Task Fetching Unit&lt;/strong&gt;，任务获取单元。这个名字直接就是从CPU里偷来的。&lt;/p&gt;
&lt;p&gt;在现代CPU里有一个东西叫指令获取单元，它的任务是一旦前一条指令把某个寄存器的值算出来了，立刻把依赖这个寄存器的下一条指令发射出去，别等一整串指令都准备好再开搞，那样太慢。&lt;/p&gt;
&lt;p&gt;LLMCompiler的Task Fetching Unit做的事完全一样。Planner一吐出DAG，它就开始扫描，发现哪些任务的依赖已经解决了，立刻往下扔。任务1和任务2没有依赖？好，同时发射，两个搜索并行执行。任务3等着1和2的结果？好，等它们回来我再把结果塞进任务3里，然后发射。&lt;/p&gt;
&lt;p&gt;整个过程是流式的。Planner一边在吐计划，执行器一边在干活，中间没有「等Planner把所有计划全想完再开始」这种停顿。论文里专门做了个消融实验，流式处理本身就贡献了一个量级的加速。&lt;/p&gt;
&lt;p&gt;第三个组件叫 &lt;strong&gt;Executor&lt;/strong&gt;，执行器。这个没啥好说的，它就是真正去调工具的那个家伙。Task Fetching Unit告诉它哪个工具可以调了，它就调。&lt;/p&gt;
&lt;p&gt;三个东西加起来，整个架构就跟一台小号的CPU一模一样。有人分析程序，有人调度，有人执行。&lt;/p&gt;
&lt;p&gt;说到这我真的有点被打动。你知道我为啥被打动吗？因为这个思路其实任何一个学过编译原理的本科生都能想到。它没有任何复杂的数学，没有什么神秘的训练技巧，就是把一个用了60年的老配方，拿来炒一道新菜。&lt;/p&gt;
&lt;p&gt;但它偏偏有效。而且效果好到离谱。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;顺着上面的再聊聊，这篇论文最让我兴奋的其实是实验结果部分。&lt;/p&gt;
&lt;p&gt;作者用了四个benchmark来测试LLMCompiler。这四个测试排起来有一个隐藏的升番结构，从最简单的场景到最复杂的场景，效果一个比一个炸。我逐个说一下。&lt;/p&gt;
&lt;p&gt;第一个叫HotpotQA。这是个很经典的多跳问答数据集，论文的Figure 1就举了一个例子，「斯科特·德瑞克森和埃德·伍德是不是同一个国籍？」这种问题。用ReAct的话就是一步一步来，先搜A，拿到结果，再搜B，拿到结果，再对比。用LLMCompiler的话，A和B可以同时搜。&lt;/p&gt;
&lt;p&gt;速度快了1.8倍。成本降了3.37倍。准确率基本一样。&lt;/p&gt;
&lt;p&gt;就这个结果拎出来看已经很能打了，但它只是开胃菜。&lt;/p&gt;
&lt;p&gt;第二个叫Movie Recommendation。这个更有意思，它每次要你从8部电影里找出跟某部电影最像的那部。也就是要对8部电影分别做独立的搜索和分析。&lt;/p&gt;
&lt;p&gt;ReAct在这里干了一件特别傻的事。论文附录里有一张图我看完直接笑出声，它显示有大约85%的样本，ReAct根本没搜完8部就结束了。它搜到第五部就停下来，觉得「我好像够了」，然后给一个答案。&lt;/p&gt;
&lt;p&gt;你敢信？？？&lt;/p&gt;
&lt;p&gt;一个号称能干活的Agent，居然连把活干完都做不到。它会提前认输。&lt;/p&gt;
&lt;p&gt;LLMCompiler在这里就完全没这个问题，因为Planner一开始就把8个任务全部规划好了，Executor必须全部执行完才能汇总。结果是速度快了3.74倍，成本降了6.73倍，准确率还反超ReAct 7个多点。&lt;/p&gt;
&lt;p&gt;第三个叫Game of 24。这游戏你们可能玩过，给你4个数字让你用加减乘除搞出24。之前最强的解法叫Tree-of-Thoughts，让LLM自己去搜索各种可能的组合。LLMCompiler在这里做了一个很骚的事，它把「Tree-of-Thoughts的一次尝试」当成一个工具，然后让Planner去并行调度这些尝试。&lt;/p&gt;
&lt;p&gt;速度快了2倍。&lt;/p&gt;
&lt;p&gt;到这里我已经觉得够牛了。&lt;/p&gt;
&lt;p&gt;但是真正让我给整不会的是第四个benchmark，WebShop。这是一个模拟网上购物的环境，你要在一堆商品里找到符合某些需求的那一个。典型的操作是搜索→看结果→再搜索→再看结果。&lt;/p&gt;
&lt;p&gt;LLMCompiler在这里直接跑出了101.7倍的加速。&lt;/p&gt;
&lt;p&gt;不是10倍，不是50倍，是一百零一点七倍。&lt;/p&gt;
&lt;p&gt;而且成功率还比ReAct高了25.7个百分点。&lt;/p&gt;
&lt;p&gt;我第一次看到这个数字的时候真的愣住了。我来回看了好几遍论文的表格，生怕自己看错了小数点。101.7x。&lt;/p&gt;
&lt;p&gt;它的原因其实非常直观。WebShop里有大量「先广撒网再选最优」的搜索动作。LLMCompiler可以一口气把所有候选搜索并行发射出去，而ReAct得一个一个搜。你想想，如果你在淘宝上找一个东西，你是一次打开十几个标签页横向对比，还是一个一个点开再返回再点开？&lt;/p&gt;
&lt;p&gt;答案很明显。&lt;/p&gt;
&lt;p&gt;但前者需要你有一个「规划」的能力，得先知道哪十几个是值得看的。这恰好就是LLMCompiler在做的事。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/003-df4faf7b.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;这块需要注意一下。LLMCompiler的意义不只是快，还有一个更深的点，它顺手救了准确率。&lt;/p&gt;
&lt;p&gt;这个我刚才提到了一嘴，但值得展开说说。作者分析了ReAct失败的案例之后发现，这些失败的绝大多数其实跟智力无关，跟纪律有关。&lt;/p&gt;
&lt;p&gt;两种典型的失败场景。一种是提前收工，它只搜了部分信息就觉得够了，开始瞎答。另一种更惨，是它会在同一个查询上无限循环，因为Wikipedia返回的信息不够精确，它就一直搜一直搜一直搜，直到context window爆掉。&lt;/p&gt;
&lt;p&gt;这两种失败加起来，贡献了ReAct绝大部分的失败样本。&lt;/p&gt;
&lt;p&gt;为啥会这样？我自己的理解是，ReAct是一种即兴架构。它没有全局视野，每一步都是基于上一步的观察临时决策的。这种即兴决策模式很像我们人脑，但它也天然带着人脑即兴决策的毛病，容易累、容易放弃、容易走进死胡同。&lt;/p&gt;
&lt;p&gt;LLMCompiler强迫模型在一开始就把所有要做的事列出来，这等于逼着它做一次系统性的规划。规划好了之后，执行阶段就只负责执行，不再思考。&lt;/p&gt;
&lt;p&gt;我觉得这里有一个非常深的启发。我们过去几年一直在迷信让LLM多想一步，搞出了Chain-of-Thought、Tree-of-Thoughts、Self-Reflection各种花活，都是在鼓励模型「思考得更细、更久、更多」。但其实有时候反过来，让它先想一次然后别再想了，反而更管用。&lt;/p&gt;
&lt;p&gt;CPU的设计哲学其实也是这样。现代CPU里最快的指令是那些不需要跳转、不需要预测、不需要动态决策的指令。凡是涉及到走一步看一步的指令，都会拖慢整条流水线。&lt;/p&gt;
&lt;p&gt;计算机硬件的人早就发现了，即兴决策是昂贵的。&lt;/p&gt;
&lt;p&gt;而这个老道理，现在又回到了AI Agent这边。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;坦率的讲，我觉得LLMCompiler这篇论文本身可能不是最大的新闻。真正的新闻是它揭示的那个更大的趋势。&lt;/p&gt;
&lt;p&gt;我们正在把整个计算机体系结构，重新发明一遍。&lt;/p&gt;
&lt;p&gt;你仔细想想这几年LLM推理和Agent方向上那些最亮眼的突破，几乎每一个都能在老教科书里找到原型。&lt;/p&gt;
&lt;p&gt;Speculative decoding，是把CPU的分支预测搬到了LLM推理。 KV cache，是把CPU的cache机制搬到了LLM推理。 Continuous batching，是把操作系统的进程调度搬到了LLM推理。 现在LLMCompiler，是把编译器的指令调度搬到了LLM Agent。&lt;/p&gt;
&lt;p&gt;每一个都在发生。每一个都带来10倍甚至100倍的加速。每一个的核心创意都不是横空出世的神来之笔，而是一句「等等，这个问题我们在硬件/OS层面已经解决过了，直接拿来用就好」。&lt;/p&gt;
&lt;p&gt;卡帕西前阵子说过一句我记了很久的话，他说LLM是一种新的计算机，一种以自然语言为指令集的计算机。这句话如果你真的认真对待，那它的所有推论都是自洽的。既然它是一种新的计算机，那我们给旧计算机发明的所有优化技巧，理论上都应该能再用一次。&lt;/p&gt;
&lt;p&gt;我有时候会觉得，我们这一代做AI的人特别幸运。我们在亲眼看一部已经拍过一遍的电影，被用新的道具重新拍摄。剧本是一样的，角色是一样的，剧情走向都是一样的。但因为道具全换了，看起来就像一部全新的片子。而且你手里只要有一本原版的剧本，你就能提前知道下一幕会发生什么。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-11-rang-agent-kuai-shang-100-bei-de-mi-mi-qi-shi-cang-zai-yi-be/004-65e386c4.png"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;回到这篇论文本身。&lt;/p&gt;
&lt;p&gt;我觉得它最重要的贡献其实不是那些benchmark数字，而是它开了一个非常清晰的方向。那就是Agent的慢不是不可解决的。&lt;/p&gt;
&lt;p&gt;你今天用Claude Code等十分钟，不是因为LLM笨，也不是因为你的任务太复杂。是因为底下那套调度系统还在用ReAct这种20世纪60年代级别的执行模式。只要换上哪怕一个粗糙的编译器思路，立刻就能快10倍、快100倍。&lt;/p&gt;
&lt;p&gt;其实这两年已经有不少框架在往这个方向走了，LangGraph、LlamaIndex都陆陆续续搞过类似的planner组件，多Agent框架里的并发调度也都能看到这套思路的影子。但奇怪的是，我们日常在用的那些最主流的Agent产品，Claude Code、Cursor这些，还是没有把这套东西吃得特别透。你还是经常能看到它们在那里一步一步串行地跑，跑得你抓狂。&lt;/p&gt;
&lt;p&gt;我始终觉得这是一件很可惜的事。一个两年前就该被充分吸收的好思路，到今天还只在部分框架里存在，绝大多数用户还是在吃ReAct的苦。&lt;/p&gt;
&lt;p&gt;其实之前OpenAI做过一个简化版，它叫Parallel Function Calling。但这篇论文里也明确对比了，OpenAI那个只能处理最简单的、完全独立的并行任务，一碰到有依赖关系的就歇菜了。LLMCompiler能处理有依赖的完整DAG，这是质变。而且论文在ParallelQA这个他们自己造的benchmark上，直接把OpenAI的并行函数调用给干穿了。&lt;/p&gt;
&lt;p&gt;还有一个让我很开心的点，LLMCompiler不依赖特定模型。它能跑在闭源的GPT系列上，也能跑在开源的LLaMA-2 70B上，效果都很好。这意味着你要用它，不需要求爷爷告奶奶去办一个特殊API，自己拿个开源模型搭一套就能跑。对整个开源生态是实实在在的利好。&lt;/p&gt;
&lt;p&gt;论文的代码早就开源在 &lt;a class="link" href="https://github.com/SqueezeAILab/LLMCompiler" target="_blank" rel="noopener"
 &gt;https://github.com/SqueezeAILab/LLMCompiler&lt;/a&gt; ，这两年我零零散散跑过一些例子，整体感觉是它确实好使，但对Planner的prompt质量非常敏感，稍微写粗糙一点就容易崩。这大概也是为啥它没在主流产品里全面铺开的原因之一，论文里优雅的架构，落到工程上总会多出一堆脏活。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;最后说点题外话。&lt;/p&gt;
&lt;p&gt;我一直觉得AI这个行业最迷人的地方，就在于它需要你是一个杂食动物。你得懂一点机器学习，懂一点系统，懂一点产品，懂一点用户。因为AI正在跟所有领域发生化学反应，任何一个你以为已经过时的角落，都可能突然长出一个全新的方向。&lt;/p&gt;
&lt;p&gt;LLMCompiler这篇论文就是一个典型的例子。它既不需要你是最顶尖的ML研究员，也不需要你是最强的系统工程师。它需要你有一个能从「我的LLM Agent跑得好慢啊」跳到「诶等等，CPU当年也有这个问题，是怎么解决的来着？」的跨界联想能力。&lt;/p&gt;
&lt;p&gt;我始终觉得这种联想能力，比任何单一领域的深度都重要。&lt;/p&gt;
&lt;p&gt;很多朋友问我怎么跟上AI的发展。我有时候觉得，与其拼命去看最新的模型发布，不如回头去翻翻那些老的、经典的、看起来跟AI毫无关系的书。编译原理、操作系统、计算机网络、数据库系统、图形学。这些书里有太多你以为已经过时的东西，在LLM时代突然又活了过来。&lt;/p&gt;
&lt;p&gt;你读过的每一本旧书，都可能在未来某天变成一枚重新上膛的子弹。&lt;/p&gt;
&lt;p&gt;前提是你得先把枪挂在墙上。&lt;/p&gt;
&lt;p&gt;以上。&lt;/p&gt;</description></item><item><title>拒绝内卷！为什么我们应该抵制用 LeetCode 考查真实的工程师？</title><link>https://xiaobox.github.io/p/2026-03-04-ju-jue-nei-juan-wei-shen-me-wo-men-ying-gai-di-zhi-yong-leet/</link><pubDate>Wed, 04 Mar 2026 08:41:32 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-03-04-ju-jue-nei-juan-wei-shen-me-wo-men-ying-gai-di-zhi-yong-leet/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-04-ju-jue-nei-juan-wei-shen-me-wo-men-ying-gai-di-zhi-yong-leet/cover.jpg" alt="Featured image of post 拒绝内卷！为什么我们应该抵制用 LeetCode 考查真实的工程师？" /&gt;&lt;h1 id="拒绝内卷为什么我们应该抵制用-leetcode-考查真实的工程师"&gt;&lt;a href="#%e6%8b%92%e7%bb%9d%e5%86%85%e5%8d%b7%e4%b8%ba%e4%bb%80%e4%b9%88%e6%88%91%e4%bb%ac%e5%ba%94%e8%af%a5%e6%8a%b5%e5%88%b6%e7%94%a8-leetcode-%e8%80%83%e6%9f%a5%e7%9c%9f%e5%ae%9e%e7%9a%84%e5%b7%a5%e7%a8%8b%e5%b8%88" class="header-anchor"&gt;&lt;/a&gt;拒绝内卷！为什么我们应该抵制用 LeetCode 考查真实的工程师？
&lt;/h1&gt;&lt;p&gt;如果你要招募一位主刀医生，你会让他当场默写《人体解剖学》的第一章吗？如果你要找一位米其林大厨，你会蒙住他的眼睛，让他比赛在一分钟内切出多少根标准厚度的土豆丝吗？&lt;/p&gt;
&lt;p&gt;显然不会。但在如今的软件工程招聘中，我们却在做着同样荒谬的事情：让那些在复杂的业务泥潭中摸爬滚打、主导过千万级并发系统、熟练操纵复杂云原生架构的资深工程师，站在白板前，徒手写出一个“翻转二叉树”或者“接雨水”的最佳时间复杂度解法。&lt;/p&gt;
&lt;p&gt;不知从何时起，“刷 LeetCode”已经从一种思维训练，演变成了一场病态的军备竞赛。是时候戳破这个泡沫了：&lt;strong&gt;LeetCode 根本选拔不出优秀的软件工程师，它正在毁掉我们的行业生态。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="一-真实的工程世界从来不是一道闭卷考试"&gt;&lt;a href="#%e4%b8%80-%e7%9c%9f%e5%ae%9e%e7%9a%84%e5%b7%a5%e7%a8%8b%e4%b8%96%e7%95%8c%e4%bb%8e%e6%9d%a5%e4%b8%8d%e6%98%af%e4%b8%80%e9%81%93%e9%97%ad%e5%8d%b7%e8%80%83%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;一、 真实的工程世界，从来不是一道“闭卷考试”
&lt;/h3&gt;&lt;p&gt;让我们先来看看，一个现代软件工程师的真实一天是怎样度过的。&lt;/p&gt;
&lt;p&gt;你可能会花一整个上午，在一堆没有注释的“屎山”代码中追踪一个诡异的内存泄漏问题；你可能会在下午和产品经理反复拉扯，确定一个新功能在微服务架构下的 API 边界；你可能会在排查为什么 Kubernetes 集群里的 HPA（水平Pod自动扩缩容）没有按预期触发，或者研究 Istio 网关的流量路由策略。&lt;/p&gt;
&lt;p&gt;如果你身处最前沿的 AI 领域，你可能正在评估是用 LangGraph 还是 AutoGen 来构建多 Agent 协同流，或者在调试大模型 API 的 Top-p 采样参数，试图让生成的回答既准确又具有随机性。甚至，在业余时间，你可能在设计一款解决自己痛点的小工具——比如一个用来清理、分类和管理繁杂书签的浏览器插件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这些工作有一个共同点：它们都是极其复杂的、高度依赖上下文的、开放性的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而在真实的工作环境中，我们解决这些问题依靠的是什么？&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;查阅文档与搜索能力：&lt;/strong&gt; 我们有 Google、有官方文档、有开源社区，甚至现在还有 AI 助手。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;调试与试错能力：&lt;/strong&gt; 我们通过打日志、单步调试、看监控指标来定位问题。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;架构视野与经验直觉：&lt;/strong&gt; 我们知道什么时候该用单例模式，什么时候该用工厂方法；我们知道在高并发下如何设计缓存策略，如何保证数据一致性。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;沟通与协作：&lt;/strong&gt; 我们需要阅读别人的代码，也需要让别人看懂我们的设计。&lt;/p&gt;
&lt;p&gt;反观 LeetCode 面试，它创造了一个极其不真实的&lt;strong&gt;无菌实验室环境&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;●题目边界清晰，输入输出明确。&lt;/p&gt;
&lt;p&gt;●只有单一的“最优解”（通常是时间复杂度和空间复杂度的极限）。&lt;/p&gt;
&lt;p&gt;●不允许查阅文档，甚至不允许使用趁手的 IDE（有时只能在网页的纯文本框里写代码）。&lt;/p&gt;
&lt;p&gt;●偏离日常使用的技术栈（你可能用 Python 写了十几年业务，却要用 C++ 的思维去考虑指针和内存管理）。&lt;/p&gt;
&lt;p&gt;这就像是要求一个现代战争中的王牌飞行员，在面试时去比拼谁的射箭准头更好。它考察的不是“解决问题的能力”，而是“在极其受限条件下的默写能力”。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="二-刷题面试正在惩罚真正有经验的老兵"&gt;&lt;a href="#%e4%ba%8c-%e5%88%b7%e9%a2%98%e9%9d%a2%e8%af%95%e6%ad%a3%e5%9c%a8%e6%83%a9%e7%bd%9a%e7%9c%9f%e6%ad%a3%e6%9c%89%e7%bb%8f%e9%aa%8c%e7%9a%84%e8%80%81%e5%85%b5" class="header-anchor"&gt;&lt;/a&gt;二、 刷题面试，正在惩罚真正有经验的“老兵”
&lt;/h3&gt;&lt;p&gt;在软件开发领域，经验是一笔巨大的财富。一个拥有 10 年、15 年工作经验的研发架构师，他最大的价值并不在于写代码的速度有多快，而在于他&lt;strong&gt;踩过足够多的坑&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;资深工程师知道，一个系统最大的危机往往不是算法复杂度从 变成了 （很多时候硬件资源和缓存机制完全能弥补），而是：&lt;/p&gt;
&lt;p&gt;●数据库连接池配置不当导致的雪崩。&lt;/p&gt;
&lt;p&gt;●缺乏熔断降级机制导致的服务级联故障。&lt;/p&gt;
&lt;p&gt;●领域模型设计错误导致的后续需求无法扩展。&lt;/p&gt;
&lt;p&gt;●业务逻辑耦合过深导致的测试困难。&lt;/p&gt;
&lt;p&gt;然而，当这位资深架构师带着一身的实战本领走进面试房间时，等待他的却是一道“动态规划（DP）”的 Hard 题。&lt;/p&gt;
&lt;p&gt;这是一种极大的资源浪费。一个能在生产环境中稳稳掌控全局、能设计出高可用 AI 基础设施、能带领团队攻坚克难的资深人才，仅仅因为最近几个月忙于项目交付、或者忙于应对生活中的变故（比如寻找新机会、照顾家庭），没有抽出几百个小时去死记硬背算法题库，就被无情地贴上“技术不过关”的标签淘汰出局。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这种现象导致了一个极其荒谬的倒挂：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那些刚刚毕业、没有写过一行生产环境代码、不懂得什么是持续集成、不知道如何进行线上排障的学生，只要花三个月把 LeetCode 刷个滚瓜烂熟，就能在面试中大杀四方；而那些真正在一线扛过枪、打过仗，能够解决复杂工程灾难的老兵，却在白板前因为忘记了一个状态转移方程而涨红了脸。&lt;/p&gt;
&lt;p&gt;企业以为自己招到了“绝顶聪明”的天才，结果新人一入职，面对极其复杂的微服务依赖和一团乱麻的业务逻辑，立刻束手无策。因为真实的业务系统里，没有人会为你准备好整洁的 &lt;code&gt;ListNode&lt;/code&gt; 或者 &lt;code&gt;TreeNode&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="三-算法题面试的本质一场低效的智商服从性测试"&gt;&lt;a href="#%e4%b8%89-%e7%ae%97%e6%b3%95%e9%a2%98%e9%9d%a2%e8%af%95%e7%9a%84%e6%9c%ac%e8%b4%a8%e4%b8%80%e5%9c%ba%e4%bd%8e%e6%95%88%e7%9a%84%e6%99%ba%e5%95%86%e6%9c%8d%e4%bb%8e%e6%80%a7%e6%b5%8b%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;三、 算法题面试的本质：一场低效的“智商服从性测试”
&lt;/h3&gt;&lt;p&gt;为什么即便怨声载道，这么多公司依然痴迷于 LeetCode 面试？很多面试官会辩解说：“算法题能考察候选人的聪明程度和逻辑思维。”&lt;/p&gt;
&lt;p&gt;这其实是一个伪命题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 算法题早就不测智商了，它只测“准备度”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在互联网早期，用算法题面试确实能筛选出一些思维敏捷的人，因为那时没有题库。但现在，LeetCode 已经有上千道题，“面经”满天飞。面试不仅变成了开卷考试的闭卷化，更变成了一门应试产业。能解出 Hard 题，往往不意味着你绝顶聪明，只意味着你刷到过原题，或者你花了大把时间去背诵套路。这充其量是一场“服从性测试”——看候选人愿不愿意为了这份工作去吃毫无意义的苦。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 忽视了工程中最关键的“可维护性”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 LeetCode 的评价体系里，“代码跑得快”是唯一的真理。哪怕你的代码里全是 &lt;code&gt;i&lt;/code&gt;, &lt;code&gt;j&lt;/code&gt;, &lt;code&gt;k&lt;/code&gt;, &lt;code&gt;dp&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt; 这种毫无语义的变量名，哪怕你的逻辑晦涩难懂如天书，只要能 AC（Accepted），你就是赢家。&lt;/p&gt;
&lt;p&gt;但在实际工程中，这种代码是灾难。好的工程师写出的代码是给人看的，其次才是给机器执行的。如果你的代码在生产环境中出了 Bug，同事半夜被叫醒排查，看到满屏追求极致技巧却毫无注释的“炫技代码”，他大概率会在心里把你骂上一万遍。LeetCode 培养出的“做题家”思维，与团队协作所需的工程素养往往是背道而驰的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 面试官的“安全牌”与偷懒。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其实，很多面试官也根本不知道该怎么面试。对他们来说，从题库里随机抽一道题扔给候选人，是最省事、最没有风险的做法。如果你没写出来，那是你不行，面试官不需要承担招错人的责任。这种做法掩盖了面试官自身架构视野和识人能力的匮乏。要深入了解一个人的项目经验、技术深度和系统设计能力，需要面试官投入极大的精力和极高的技术水平去进行深度的技术探讨，而“考一道题”则轻易地把压力全抛给了候选人。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="四-如何打破僵局回归工程本质的面试方法"&gt;&lt;a href="#%e5%9b%9b-%e5%a6%82%e4%bd%95%e6%89%93%e7%a0%b4%e5%83%b5%e5%b1%80%e5%9b%9e%e5%bd%92%e5%b7%a5%e7%a8%8b%e6%9c%ac%e8%b4%a8%e7%9a%84%e9%9d%a2%e8%af%95%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;四、 如何打破僵局：回归工程本质的面试方法
&lt;/h3&gt;&lt;p&gt;批判之后，我们需要建设。如果不考 LeetCode，我们该怎么筛选优秀的软件工程师？真正的面试，应该是一场对日常工作的高度模拟。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 结对编程 (Pair Programming)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不要让候选人在白板上写代码，给他一台配置好 IDE 的电脑。面试官准备一个真实但简化过的业务小项目，或者直接在公司的一个开源代码分支上，两人结对协作。&lt;/p&gt;
&lt;p&gt;●“我们现在有一个 Python 的服务端，用 FastAPI 写的，现在需要增加一个中间件来做简单的限流，你打算怎么做？”&lt;/p&gt;
&lt;p&gt;●允许候选人查阅文档，允许使用 Google。&lt;/p&gt;
&lt;p&gt;●观察他的编码习惯、他对框架的熟悉程度、他如何拆解问题，以及更重要的——他如何与你沟通和协作。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 代码审查 (Code Review)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;给候选人一段存在各种“坑”的代码（可以是以前团队写出的真实烂代码，隐去敏感信息）。这段代码可能存在并发竞争、内存泄漏、或者设计模式的滥用。&lt;/p&gt;
&lt;p&gt;让候选人进行 Code Review。优秀的工程师能立刻嗅出代码中的“坏味道”，并提出合理的重构建议。这比让他默写快速排序要有效得多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 深度系统设计与项目复盘&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;抛弃那些假大空的“如何设计一个推特”的八股文。让候选人深度讲解他简历中最自豪的一个项目。&lt;/p&gt;
&lt;p&gt;●“你在简历中提到主导了容器化改造，能画一下当时的 Kubernetes 架构图吗？”&lt;/p&gt;
&lt;p&gt;●“在使用 Ingress 和服务网格（比如 APISIX 或 Istio）时，你们遇到了什么性能瓶颈？是如何排查的？”&lt;/p&gt;
&lt;p&gt;●“你提到在做 AI 相关的研发，在整合底层大模型接口时，你们是如何处理长上下文带来的延迟问题和 token 消耗的？”&lt;/p&gt;
&lt;p&gt;通过深度的追问，直到触及他的知识边界。真正的行家，在谈论自己亲手一砖一瓦建起来的系统时，眼里是有光的，细节是经得起推敲的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 聊聊他创造的“小玩意儿”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个真正的工程师，往往是对技术充满热情的创造者。与其问算法，不如问问他平时都在折腾什么。如果他告诉你，他因为受不了浏览器书签太乱，正在自己设计开发一个管理书签的插件；或者他为了解某种新技术栈，自己搭了一个爬虫和数据展示网站。请让他展示一下！这种对痛点的敏锐察觉和动手解决问题的能力，是任何算法题都无法衡量出的核心特质。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="五-结语放过工程师也放过企业自己"&gt;&lt;a href="#%e4%ba%94-%e7%bb%93%e8%af%ad%e6%94%be%e8%bf%87%e5%b7%a5%e7%a8%8b%e5%b8%88%e4%b9%9f%e6%94%be%e8%bf%87%e4%bc%81%e4%b8%9a%e8%87%aa%e5%b7%b1" class="header-anchor"&gt;&lt;/a&gt;五、 结语：放过工程师，也放过企业自己
&lt;/h3&gt;&lt;p&gt;技术招聘走到今天“无算法不面试”的地步，是整个行业的悲哀。它消耗了工程师们原本可以用来学习新框架、钻研底层原理、甚至陪伴家人的宝贵精力；它也让企业错失了大量踏实肯干、经验丰富的实战派人才。&lt;/p&gt;
&lt;p&gt;编程，是一门结合了逻辑、工程、设计甚至艺术的创造性活动。它不该被简化为一场机械的背诵比赛。&lt;/p&gt;
&lt;p&gt;作为面试官，下次当你准备掏出一道 LeetCode Hard 题时，不妨停下来问问自己：“这道题，真的能帮我找到那个能和我并肩作战、一起扛住双十一流量洪峰、一起在深夜排查诡异 Bug 的可靠队友吗？”&lt;/p&gt;
&lt;p&gt;如果不能，请放下那道该死的算法题，和候选人像真正的工程师一样，聊聊真实的架构，看看真实的代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;把时间还给工程，把尊严还给工程师。&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>程序员必备：最直观的数据结构图文手册</title><link>https://xiaobox.github.io/p/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/</link><pubDate>Tue, 19 Nov 2024 03:47:23 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/cover.jpg" alt="Featured image of post 程序员必备：最直观的数据结构图文手册" /&gt;&lt;p&gt;这篇文章为了方便以可视化的方式回顾那些最常用的数据结构，你可以用它做面试准备时的复习。希望这些可视化例子能够帮助大家了解这些数据结构。&lt;/p&gt;
&lt;h2 id="大-o-时间复杂度"&gt;&lt;a href="#%e5%a4%a7-o-%e6%97%b6%e9%97%b4%e5%a4%8d%e6%9d%82%e5%ba%a6" class="header-anchor"&gt;&lt;/a&gt;大 O &amp;ndash;时间复杂度
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/001-16aa0e79.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么大 O 复杂度很重要 ？&lt;/strong&gt;：对于小数据集，算法复杂度可能不会扮演非常重要的角色，但随着我们的数据量增大——算法的性能影响对响应时间有极大的影响。因此，关注复杂度在具有合理规模的任何应用领域中对于程序质量都起着至关重要的作用。&lt;/p&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;p&gt;假设我们的数据集有 100 万（1,000,000）个元素&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;O(1)&lt;/code&gt;算法将进行 1 次操作。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O(log(n))&lt;/code&gt;算法将进行 20 次操作&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O(n)&lt;/code&gt;算法将进行 1000000 次操作。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O(n * log(n))&lt;/code&gt;算法将进行 2000 万次操作。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O(n 2 )&lt;/code&gt;算法将进行 1 万亿次（1,000,000,000,000）操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以，你应该能看出算法复杂度的重要性。&lt;/p&gt;
&lt;h2 id="rum-权衡"&gt;&lt;a href="#rum-%e6%9d%83%e8%a1%a1" class="header-anchor"&gt;&lt;/a&gt;RUM 权衡
&lt;/h2&gt;&lt;p&gt;另一个在选择数据结构时需要注意的重要方面是 RUM 权衡&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;读取效率（R）：从数据结构中检索或访问数据有多快。&lt;/li&gt;
&lt;li&gt;更新效率（U）：在数据结构中插入、删除或修改数据有多快。&lt;/li&gt;
&lt;li&gt;内存效率（M）：数据结构使用的内存或空间大小。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/002-3c8c7427.png"&gt;&lt;/p&gt;
&lt;h2 id="那些最常用且重要的数据结构"&gt;&lt;a href="#%e9%82%a3%e4%ba%9b%e6%9c%80%e5%b8%b8%e7%94%a8%e4%b8%94%e9%87%8d%e8%a6%81%e7%9a%84%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;那些最常用且重要的数据结构
&lt;/h2&gt;&lt;h3 id="数组--链表"&gt;&lt;a href="#%e6%95%b0%e7%bb%84--%e9%93%be%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;数组 &amp;amp; 链表
&lt;/h3&gt;&lt;p&gt;数组在内存中连续存储，以快速查找而闻名，但更新/写入时间较慢；而链表非连续存储，以快速更新/写入而闻名，但查找较慢&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/003-95dddf47.png"&gt;&lt;/p&gt;
&lt;h3 id="队列"&gt;&lt;a href="#%e9%98%9f%e5%88%97" class="header-anchor"&gt;&lt;/a&gt;队列
&lt;/h3&gt;&lt;p&gt;线性数据结构，遵循先进先出（FIFO）原则：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/004-8f0f7eb3.png"&gt;&lt;/p&gt;
&lt;h3 id="堆栈"&gt;&lt;a href="#%e5%a0%86%e6%a0%88" class="header-anchor"&gt;&lt;/a&gt;堆栈
&lt;/h3&gt;&lt;p&gt;遵循后进先出（LIFO）原则，元素从顶部添加和移除：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/005-026b9a2b.png"&gt;&lt;/p&gt;
&lt;h3 id="哈希表"&gt;&lt;a href="#%e5%93%88%e5%b8%8c%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;哈希表
&lt;/h3&gt;&lt;p&gt;提供几乎即时的元素访问，通过使用哈希函数创建键值对来实现。插入、删除和查找的时间复杂度为 O(1)，代价是内存利用率&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/006-e236353a.png"&gt;&lt;/p&gt;
&lt;h2 id="树形数据结构"&gt;&lt;a href="#%e6%a0%91%e5%bd%a2%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;树形数据结构
&lt;/h2&gt;&lt;p&gt;树形数据结构常用于数据密集型应用。&lt;/p&gt;
&lt;p&gt;在列出所有数据结构之前，我们首先需要回顾一个在树结构中起着关键作用的算法——二分查找算法。&lt;/p&gt;
&lt;h3 id="二分查找"&gt;&lt;a href="#%e4%ba%8c%e5%88%86%e6%9f%a5%e6%89%be" class="header-anchor"&gt;&lt;/a&gt;二分查找
&lt;/h3&gt;&lt;p&gt;这是一个对排序元素的搜索，通过不断将搜索空间分成一半来完成。就像在字典中找到中间页面，检查我们的搜索词是在字典的左半部分还是右半部分，然后不断重复这个过程，直到找到元素。使用高效的 O(log(n)) 查找&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/007-7943b840.png"&gt;&lt;/p&gt;
&lt;h3 id="二叉搜索树"&gt;&lt;a href="#%e4%ba%8c%e5%8f%89%e6%90%9c%e7%b4%a2%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;二叉搜索树
&lt;/h3&gt;&lt;p&gt;一棵二叉树，其中每个节点最多有两个子节点，且左子节点的值小于父节点，右子节点的值大于父节点。如果二叉搜索树平衡（位于左边的节点数量不超过右边的节点数量很多），则可以进行高效的 O(log(n)) 查找。这是因为我们在遍历树时（从父节点到子节点）将搜索空间减半&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/008-c33cbbdc.png"&gt;&lt;/p&gt;
&lt;h3 id="红黑树"&gt;&lt;a href="#%e7%ba%a2%e9%bb%91%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;红黑树
&lt;/h3&gt;&lt;p&gt;二叉搜索树通过将节点分配颜色（红色或黑色）并遵循一组确保其保持平衡的规则来维护其平衡：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/009-b78ecec1.png"&gt;&lt;/p&gt;
&lt;h3 id="avl-树"&gt;&lt;a href="#avl-%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;AVL 树
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;AVL 树的全称是 Adelson-Velsky and Landis Tree，以其发明者 G. M. Adelson-Velsky 和 E. M. Landis 的名字命名。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;自平衡二叉搜索树，通过确保其平衡因子（所有左子树和右子树之间的高度差）至多为 1 来实现平衡。在插入和删除操作期间通过旋转自动重新平衡&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/010-2c19daa3.png"&gt;&lt;/p&gt;
&lt;h3 id="堆"&gt;&lt;a href="#%e5%a0%86" class="header-anchor"&gt;&lt;/a&gt;堆
&lt;/h3&gt;&lt;p&gt;树中每个父节点要么大于等于（最大堆）其子节点，要么小于等于（最小堆）其子节点。允许高效检索最小或最大值，常用于实现优先队列。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/011-95c0e4d2.png"&gt;&lt;/p&gt;
&lt;h3 id="跳表"&gt;&lt;a href="#%e8%b7%b3%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;跳表
&lt;/h3&gt;&lt;p&gt;跳表（Skip List）是链表的一种扩展结构，通过引入多级链表来加速查找、插入和删除操作。它的工作原理是允许通过“跳跃”多个链表元素来快速定位到目标节点，这通常是通过从父级链表向下遍历到合适的子级链表来实现的。这种结构类似于二叉搜索树，但具有一定的随机性，通常在效率和简单性上都有不错的表现。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/012-7eaa63ee.png"&gt;&lt;/p&gt;
&lt;h3 id="b-树"&gt;&lt;a href="#b-%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;B+ 树
&lt;/h3&gt;&lt;p&gt;常用于数据库存储。B+树是一种平衡树，其中所有数据都存储在叶节点中，这些叶节点按顺序链接在一起，以便快速顺序访问：&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/013-68f325e6.png"&gt;&lt;/p&gt;
&lt;h3 id="lsmlog-structured-merge树"&gt;&lt;a href="#lsmlog-structured-merge%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;LSM（Log-Structured Merge）树
&lt;/h3&gt;&lt;p&gt;数据应用中常用的写优化树，为了理解 LSM 树，我们需要熟悉另外两种数据结构：Memtables（内存表）和 SSTables（排序字符串表）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Memtable&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最初，数据被写入一个称为 Memtable 的内存结构中。这个 Memtable 在内存中保存数据，直到达到一定大小，通常通过使用平衡搜索树（如红黑树）、跳表或哈希表来实现，以提供高效的读取访问。当 Memtable 满时，其内容会被写入磁盘作为新的 SSTable。这个过程称为刷新。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/014-9ba50bba.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSTable&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SSTables 根据键的顺序存储数据。每个 SSTable 由一系列键值对组成，其中键是有序的。一旦创建 SSTable，它就不会被修改。相反，新的数据更新会写入新的 SSTable。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/015-244ce596.png"&gt;&lt;/p&gt;
&lt;p&gt;SSTables 通常使用 Bloom 过滤器、稀疏索引等辅助数据结构来快速确定键是否存在于 SSTable 中或定位值。&lt;/p&gt;
&lt;p&gt;随着时间的推移，由于频繁更新，可能会创建多个 SSTables。为了优化性能和回收空间，SSTables 会定期合并和压缩。这涉及到将多个 SSTables 中的数据合并成更少的新的 SSTables，同时丢弃过时的条目。&lt;/p&gt;
&lt;p&gt;下图是 LSM treee 的一个完整结构：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/016-708d5b75.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意：LSM-tree 不是一种数据结构，是数据组织的一种方式&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="二叉索引树斐波那契树"&gt;&lt;a href="#%e4%ba%8c%e5%8f%89%e7%b4%a2%e5%bc%95%e6%a0%91%e6%96%90%e6%b3%a2%e9%82%a3%e5%a5%91%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;二叉索引树/斐波那契树
&lt;/h3&gt;&lt;p&gt;一种紧凑且高效的数据结构，用于处理动态累积频率表或前缀和。换句话说，它非常适合用于区间查询。&lt;/p&gt;
&lt;p&gt;树结构存储在一个数组中，其中数组中每个 2 的幂次方索引位置保存其之前所有元素的累积和。举个例子，第 4 个元素（值为 22）存储的是前 4 个元素的和。为了获取树中每个区间的子数组和，我们使用位移操作，使得每次更新和读取的时间复杂度减少到对数级别 O(log(n))，从而提高区间查询的效率。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/017-39f41184.png"&gt;&lt;/p&gt;
&lt;h2 id="图数据结构"&gt;&lt;a href="#%e5%9b%be%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;图数据结构
&lt;/h2&gt;&lt;h3 id="邻接表与邻接矩阵图表示"&gt;&lt;a href="#%e9%82%bb%e6%8e%a5%e8%a1%a8%e4%b8%8e%e9%82%bb%e6%8e%a5%e7%9f%a9%e9%98%b5%e5%9b%be%e8%a1%a8%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;邻接表与邻接矩阵图表示
&lt;/h3&gt;&lt;p&gt;一个邻接表将图表示为一系列列表的集合——每个节点都有一个与之相连的节点集合。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/018-65d2c4e4.png"&gt;&lt;/p&gt;
&lt;p&gt;邻接矩阵将图表示为一个二维矩阵。如果我们的图有 N 个节点，我们将有一个 N×N 的矩阵，其中每个单元格(i, j)表示顶点 i 和顶点 j 之间是否存在边。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/019-bf45bf53.png"&gt;&lt;/p&gt;
&lt;p&gt;示例图:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/020-21a98545.png"&gt;&lt;/p&gt;
&lt;h2 id="字符串搜索数据结构"&gt;&lt;a href="#%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%90%9c%e7%b4%a2%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;字符串搜索数据结构
&lt;/h2&gt;&lt;h3 id="trie字典树"&gt;&lt;a href="#trie%e5%ad%97%e5%85%b8%e6%a0%91" class="header-anchor"&gt;&lt;/a&gt;Trie（字典树）
&lt;/h3&gt;&lt;p&gt;trie 是一种树形数据结构，用于高效地搜索字符串，其中每个节点代表一个字符，具有包括快速基于前缀的查询和插入等优势。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/021-8f5978f6.png"&gt;&lt;/p&gt;
&lt;h3 id="radix-tree"&gt;&lt;a href="#radix-tree" class="header-anchor"&gt;&lt;/a&gt;Radix Tree
&lt;/h3&gt;&lt;p&gt;它也可以被视为一个紧凑的 Trie。尽管 Trie 很棒，但它们可能会占用大量内存。Redix 树通过合并具有公共前缀的节点来解决这个问题&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/022-aa66d395.png"&gt;&lt;/p&gt;
&lt;h3 id="splay-tree"&gt;&lt;a href="#splay-tree" class="header-anchor"&gt;&lt;/a&gt;Splay Tree
&lt;/h3&gt;&lt;p&gt;伸展树是一种在数据访问频率不均时具有优秀性能的二叉搜索树。树在查找、插入和删除操作后自动调整。在树中访问一个项目后，树会重新排列，使访问的项目移动到顶部（根）。这使得对该项目的未来访问更快。伸展树在缓存中特别有用。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/023-253c3179.png"&gt;&lt;/p&gt;
&lt;h3 id="quadtree"&gt;&lt;a href="#quadtree" class="header-anchor"&gt;&lt;/a&gt;Quadtree
&lt;/h3&gt;&lt;p&gt;四叉树是一种空间数据结构，它递归地将二维空间划分为四个象限，使其在管理和查询如点或区域等空间数据时非常高效。如果一个节点包含太多点，它将被细分为四个子节点。四叉树通常用于处理碰撞检测。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/024-bb082c42.png"&gt;&lt;/p&gt;
&lt;h3 id="kd-tree"&gt;&lt;a href="#kd-tree" class="header-anchor"&gt;&lt;/a&gt;KD Tree
&lt;/h3&gt;&lt;p&gt;一棵二叉树，其中每个节点代表 k 维空间中的一个点。该树通过递归地在其中一个维度上分割空间来构建。在树的每一层，数据根据一个维度进行分割，后续的每一层交替使用维度。这使得它在范围查询和最近邻搜索中非常高效。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/025-08a899b6.png"&gt;&lt;/p&gt;
&lt;h3 id="r-tree"&gt;&lt;a href="#r-tree" class="header-anchor"&gt;&lt;/a&gt;R-Tree
&lt;/h3&gt;&lt;p&gt;R 树是一种用于高效索引多维空间数据的树形数据结构。它们将数据组织成最小边界矩形（MBR），这些矩形按层次分组，每个节点的 MBR 包含其子节点的 MBR。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/026-f0eb8de6.png"&gt;&lt;/p&gt;
&lt;h2 id="其他数据结构及图"&gt;&lt;a href="#%e5%85%b6%e4%bb%96%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84%e5%8f%8a%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;其他数据结构及图
&lt;/h2&gt;&lt;h3 id="布隆过滤器"&gt;&lt;a href="#%e5%b8%83%e9%9a%86%e8%bf%87%e6%bb%a4%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;布隆过滤器
&lt;/h3&gt;&lt;p&gt;布隆过滤器是一种空间高效的概率数据结构，用于测试一个元素是否是集合的成员，通常用于减少对不存在的键的昂贵磁盘（或网络）查找。它可以产生假阳性（报告元素在集合中而实际上不在），但永远不会产生假阴性（如果元素实际上在集合中，它永远不会错误地报告元素不在集合中）。&lt;/p&gt;
&lt;p&gt;它使用位数组来存储数据。为了将一个键映射到适当的位，它使用多个独立的哈希函数，每个函数将键映射到位数组中的不同位位置。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/027-513b7c78.png"&gt;&lt;/p&gt;
&lt;h3 id="二叉堆"&gt;&lt;a href="#%e4%ba%8c%e5%8f%89%e5%a0%86" class="header-anchor"&gt;&lt;/a&gt;二叉堆
&lt;/h3&gt;&lt;p&gt;二叉堆是一种高效管理元素集合的数据结构，支持快速插入、最小元素提取和堆合并。当需要处理频繁执行这些操作的动态元素集合时，它特别有用&lt;/p&gt;
&lt;p&gt;二叉堆由一系列二叉树组成，这些树是相互链接的特殊树&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二项式树（0 至 3 阶）：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/028-02e2da9e.png"&gt;&lt;/p&gt;
&lt;p&gt;每个堆中的二叉树都遵循最小堆属性：节点的键值大于或等于其父节点的键值。此外，每个顺序只能有一个或零个二叉树，包括零阶。&lt;/p&gt;
&lt;p&gt;以下示例&lt;strong&gt;二叉堆包含 13 个节点：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/029-b64ced71.png"&gt;&lt;/p&gt;
&lt;p&gt;二叉堆在实现优先队列等场景中很有用，在这些场景中，需要频繁地合并堆或对一组元素执行其他操作。&lt;/p&gt;
&lt;h3 id="hash-array-mapped-trie-hamt"&gt;&lt;a href="#hash-array-mapped-trie-hamt" class="header-anchor"&gt;&lt;/a&gt;Hash Array Mapped Trie (HAMT)
&lt;/h3&gt;&lt;p&gt;HAMT 是一种结合了哈希表和 Trie 的优点，用于高效存储和检索键值对的数据结构。它在计算机科学中常用于实现关联数组或字典。在 HAMT 中，键被哈希以确定其在数组中的存储位置，该数组称为哈希数组。哈希数组中的每个条目可以存储多个键值对，从而实现高效的内存利用。如果多个键哈希到相同的数组索引，则使用类似 Trie 的结构来解决冲突。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/030-0371cb4b.png"&gt;&lt;/p&gt;
&lt;h3 id="merkle-tree"&gt;&lt;a href="#merkle-tree" class="header-anchor"&gt;&lt;/a&gt;Merkle Tree
&lt;/h3&gt;&lt;p&gt;帮助高效、安全地验证大量数据。它通过将数据组织成树状结构，其中每个叶子节点包含数据块的哈希值，每个非叶子节点是其两个子节点的哈希值，一直向上到顶部的 Merkle 根。这种结构在区块链和其他系统中被广泛使用，以确保数据完整性。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/031-8ee0b831.png"&gt;&lt;/p&gt;
&lt;h2 id="最后8-个数据库中常用的数据结构"&gt;&lt;a href="#%e6%9c%80%e5%90%8e8-%e4%b8%aa%e6%95%b0%e6%8d%ae%e5%ba%93%e4%b8%ad%e5%b8%b8%e7%94%a8%e7%9a%84%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;最后：8 个数据库中常用的数据结构
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-19-cheng-xu-yuan-bi-bei-zui-zhi-guan-de-shu-ju-jie-gou-tu-wen-s/032-2674b76e.png"&gt;&lt;/p&gt;</description></item><item><title>Java 集合 API 的改进</title><link>https://xiaobox.github.io/p/2024-11-12-java-ji-he-api-de-gai-jin/</link><pubDate>Tue, 12 Nov 2024 04:13:20 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-12-java-ji-he-api-de-gai-jin/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-12-java-ji-he-api-de-gai-jin/cover.jpg" alt="Featured image of post Java 集合 API 的改进" /&gt;&lt;h2 id="简介"&gt;&lt;a href="#%e7%ae%80%e4%bb%8b" class="header-anchor"&gt;&lt;/a&gt;简介
&lt;/h2&gt;&lt;p&gt;本文我们将探讨不同 jdk 版本中各类的起源，以及新引入的类和接口背后的目的。我们将分析之前版本存在的问题，以及为何需要引入新的类或接口。此外，我们还将介绍集合类和接口中的新特性。文章将逐一解答这些问题。&lt;/p&gt;
&lt;p&gt;我们将逐步学习 Java 集合类的优化过程，并按版本逐一对比分析。主要讨论的焦点将包括 JDK 1.0、1.2、1.4、1.5、1.6、1.8、9、10、11 和 21 版本的 Java 集合功能&lt;/p&gt;
&lt;h2 id="java-集合-api-的改进"&gt;&lt;a href="#java-%e9%9b%86%e5%90%88-api-%e7%9a%84%e6%94%b9%e8%bf%9b" class="header-anchor"&gt;&lt;/a&gt;Java 集合 API 的改进
&lt;/h2&gt;&lt;p&gt;Java 集合 API 在多年中经历了显著改进，引入了新功能、增强和优化，以提高开发者的生产力、改善性能，并适应修订的编程模式和需求。它将帮助开发者利用 Java 集合的力量构建更健壮、高效和可维护的应用程序。&lt;/p&gt;
&lt;h2 id="jdk-10-中的集合类"&gt;&lt;a href="#jdk-10-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;JDK 1.0 中的集合类
&lt;/h2&gt;&lt;p&gt;在 JDK 1.0 中，有四个类 &lt;code&gt;Vector&lt;/code&gt;、&lt;code&gt;Stack&lt;/code&gt;、&lt;code&gt;Hashtable&lt;/code&gt; 和 &lt;code&gt;Properties&lt;/code&gt;。此外，还有一个名为“&lt;code&gt;Enumeration&lt;/code&gt;”的接口，用于以简单的方式遍历值。进一步分类，Stack 是 Vector 的子类，Properties 是 Hashtable 的子类。&lt;/p&gt;
&lt;h3 id="vector-类的问题"&gt;&lt;a href="#vector-%e7%b1%bb%e7%9a%84%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;Vector 类的问题
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Vector 是线程安全的，即 Vector 中的所有方法都是同步的。因此，它不适合单线程环境。&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;h3 id="hashtable-类的问题"&gt;&lt;a href="#hashtable-%e7%b1%bb%e7%9a%84%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;Hashtable 类的问题
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Hashtable 是线程安全的，即 Hashtable 中的所有方法都是同步的。因此，它不适合单线程环境。&lt;/li&gt;
&lt;li&gt;Hashtable 无法按顺序存储条目&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="enumeration-的问题"&gt;&lt;a href="#enumeration-%e7%9a%84%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;Enumeration 的问题
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;无法删除元素且方法名称过长&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jdk-12-中的集合类"&gt;&lt;a href="#jdk-12-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;JDK 1.2 中的集合类
&lt;/h2&gt;&lt;p&gt;在 JDK 1.2 中，Sun Micro-system 引入了 &lt;code&gt;ArrayList&lt;/code&gt;、&lt;code&gt;LinkedList&lt;/code&gt;、&lt;code&gt;HashSet&lt;/code&gt;、&lt;code&gt;TreeSet&lt;/code&gt;、&lt;code&gt;HashMap&lt;/code&gt;、&lt;code&gt;TreeMap&lt;/code&gt;、&lt;code&gt;Iterator&lt;/code&gt; 和 &lt;code&gt;ListIterator&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ArrayList：用于提供单线程环境下的解决方案，因为 ArrayList 中的方法不是同步的。&lt;/li&gt;
&lt;li&gt;LinkedList 用于提供更快的元素插入和删除。&lt;/li&gt;
&lt;li&gt;HashSet：不允许有重复元素。&lt;/li&gt;
&lt;li&gt;TreeSet：用于按排序顺序存储元素。&lt;/li&gt;
&lt;li&gt;HashMap：提供单线程环境下的解决方案，因为 HashMap 中的方法不是同步的。&lt;/li&gt;
&lt;li&gt;TreeMap：用于按顺序存储键值对。&lt;/li&gt;
&lt;li&gt;Iterator：用于解决枚举问题。同时还有一个专门处理列表的类 ListIterator。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;HashSet 的问题&lt;/strong&gt;：它不能保持插入顺序，即它不会按照元素添加到集合中的顺序存储元素。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HashMap 的问题&lt;/strong&gt;：像 HashSet 一样，它不能保持插入顺序。&lt;/p&gt;
&lt;h2 id="jdk-14-中的集合类"&gt;&lt;a href="#jdk-14-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;JDK 1.4 中的集合类
&lt;/h2&gt;&lt;p&gt;在 JDK 1.2 中，Sun Microsystems 引入了 &lt;code&gt;LinkedHashSet&lt;/code&gt; 和 &lt;code&gt;LinkedHashMap&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LinkedHashSet：用于解决 HashSet 中插入顺序的问题。它按照元素添加到集合中的顺序存储元素。&lt;/li&gt;
&lt;li&gt;LinkedHashMap：用于解决 HashMap 中插入顺序的问题。它还按照元素添加到集合中的顺序存储元素。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jdk-15-中的集合类"&gt;&lt;a href="#jdk-15-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;JDK 1.5 中的集合类
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;for-Each 循环：作为替代迭代器进行迭代的另一种方法&lt;/li&gt;
&lt;li&gt;CopyOnWriteArrayList：引入以允许在修改底层列表的情况下安全地迭代元素。&lt;/li&gt;
&lt;li&gt;CopyOnWriteArraySet：它使用内部 CopyOnWriteArrayList 进行所有操作。因此，它具有与该列表相同的基本属性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="jdk-16-中的集合类"&gt;&lt;a href="#jdk-16-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;JDK 1.6 中的集合类
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;NavigableSet：作为扩展了导航方法的有序集合，用于报告给定搜索目标的最近匹配。&lt;/li&gt;
&lt;li&gt;NavigableMap：作为扩展了导航方法的 SortedMap，返回给定搜索目标的最近匹配项。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-12-java-ji-he-api-de-gai-jin/001-b283acb5.jpg"&gt;&lt;/p&gt;
&lt;h2 id="jdk-18-中的集合类"&gt;&lt;a href="#jdk-18-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;JDK 1.8 中的集合类
&lt;/h2&gt;&lt;p&gt;Java 集合框架也有新更新，以支持 lambda 表达式、流和聚合操作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stream() 作为父接口 Collection 的默认方法：返回一个以该集合为源的顺序 Stream。&lt;/li&gt;
&lt;li&gt;parallelStream() 作为父接口 Collection 的默认方法：返回一个可能并行的 Stream，以这个集合作为其源。&lt;/li&gt;
&lt;li&gt;spliterator() 作为父接口 Collection 的一个默认方法：创建一个遍历此集合中元素的 Spliterator&lt;/li&gt;
&lt;li&gt;removeIf(Predicate filter) 作为父接口 Collection 的默认方法：移除满足给定谓词的所有元素。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同样重要的是，这里的一个显著点是所有新添加的方法都是接口 Collection 内部的默认方法。这是使用默认方法的最佳示例。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-12-java-ji-he-api-de-gai-jin/002-bf3a4631.png"&gt;&lt;/p&gt;
&lt;h2 id="java-9-中的集合增强"&gt;&lt;a href="#java-9-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e5%a2%9e%e5%bc%ba" class="header-anchor"&gt;&lt;/a&gt;Java 9 中的集合增强
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;新增用于创建不可变列表、集合和映射的 of() 静态工厂方法介绍。这些方法包括：&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;Arrays.mismatch()：新增方法以查找两个数组中第一个不匹配的索引。&lt;/li&gt;
&lt;li&gt;Arrays.compare()：添加了新方法来比较提供的两个数组中的元素。&lt;/li&gt;
&lt;li&gt;为 Arrays.equals() 添加了更多重载方法。&lt;/li&gt;
&lt;li&gt;Enumeration.asIterator()：添加了返回 java.util.Iterator 实例的新方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，在 &lt;code&gt;Stream&lt;/code&gt; API 中添加了一些方法，如 dropWhile、takeWhile 和 ofNullable。&lt;/p&gt;
&lt;h2 id="java-10-中的集合增强"&gt;&lt;a href="#java-10-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e5%a2%9e%e5%bc%ba" class="header-anchor"&gt;&lt;/a&gt;Java 10 中的集合增强
&lt;/h2&gt;&lt;p&gt;引入了 List.copyOf()、Set.copyOf() 和 Map.copyOf()，用于创建现有集合的不变副本。&lt;/p&gt;
&lt;h2 id="java-11-中的集合增强"&gt;&lt;a href="#java-11-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e5%a2%9e%e5%bc%ba" class="header-anchor"&gt;&lt;/a&gt;Java 11 中的集合增强
&lt;/h2&gt;&lt;p&gt;Collection.toArray(IntFunction)：添加了新的默认方法，允许将集合的元素转移到新创建的具有所需运行时类型的数组中。新方法是现有 toArray(…) 方法的重载变体。&lt;/p&gt;
&lt;h2 id="java-21-中的集合增强"&gt;&lt;a href="#java-21-%e4%b8%ad%e7%9a%84%e9%9b%86%e5%90%88%e5%a2%9e%e5%bc%ba" class="header-anchor"&gt;&lt;/a&gt;Java 21 中的集合增强
&lt;/h2&gt;&lt;p&gt;Java 21 在集合框架中引入了三个新接口：&lt;code&gt;SequencedCollection&lt;/code&gt;、&lt;code&gt;SequencedSet&lt;/code&gt; 和 &lt;code&gt;SequencedMap&lt;/code&gt;。这些新的集合接口通过新库提供的默认方法，使我们能够访问其第一个和最后一个元素。该功能还允许我们通过简单的调用方法来获取集合的反转视图。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-12-java-ji-he-api-de-gai-jin/003-e53f56fa.png"&gt;&lt;/p&gt;
&lt;h3 id="sequencedcollection--序列集合"&gt;&lt;a href="#sequencedcollection--%e5%ba%8f%e5%88%97%e9%9b%86%e5%90%88" class="header-anchor"&gt;&lt;/a&gt;SequencedCollection 序列集合
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;default void addFirst(E e)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;default void addLast(E e)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;default E getFirst()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;default E getLast()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;default E removeFirst()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;default E removeLast()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;SequencedCollection&amp;lt;E&amp;gt; reversed()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="sequencedset-序列集合"&gt;&lt;a href="#sequencedset-%e5%ba%8f%e5%88%97%e9%9b%86%e5%90%88" class="header-anchor"&gt;&lt;/a&gt;SequencedSet 序列集合
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;SequencedSet&amp;lt;E&amp;gt; reversed()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="sequencedmap-序列映射"&gt;&lt;a href="#sequencedmap-%e5%ba%8f%e5%88%97%e6%98%a0%e5%b0%84" class="header-anchor"&gt;&lt;/a&gt;SequencedMap 序列映射
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;default Map.Entry&amp;lt;K,V&amp;gt; firstEntry()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;default Map.Entry&amp;lt;K,V&amp;gt; lastEntry()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;default Map.Entry&amp;lt;K,V&amp;gt; pollFirstEntry()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;default Map.Entry&amp;lt;K,V&amp;gt; pollLastEntry()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;default V putFirst(K k, V v)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;default V putLast(K k, V v)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;SequencedMap&amp;lt;K,V&amp;gt; reversed()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;default SequencedSet&amp;lt;Map.Entry&amp;lt;K,V&amp;gt;&amp;gt; sequencedEntrySet()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;default SequencedSet&amp;lt;K&amp;gt; sequencedKeySet()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;default SequencedCollection&amp;lt;V&amp;gt; sequencedValues()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>九个技巧，让你的Python代码运行得更快！</title><link>https://xiaobox.github.io/p/2024-10-07-jiu-ge-ji-qiao-rang-ni-de-python-dai-ma-yun-xing-de-geng-kua/</link><pubDate>Mon, 07 Oct 2024 14:27:36 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-10-07-jiu-ge-ji-qiao-rang-ni-de-python-dai-ma-yun-xing-de-geng-kua/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-10-07-jiu-ge-ji-qiao-rang-ni-de-python-dai-ma-yun-xing-de-geng-kua/cover.jpg" alt="Featured image of post 九个技巧，让你的Python代码运行得更快！" /&gt;&lt;p&gt;在编程语言的讨论中，我们经常听到 “Python 太慢了” 的声音，这往往掩盖了 Python 的许多优点。但事实上，如果你能以 Pythonic 的方式编写代码，Python 可以非常快。&lt;/p&gt;
&lt;p&gt;细节决定成败。经验丰富的 Python 开发者拥有一系列微妙而强大的技巧，这些技巧可以显著提高代码的性能。&lt;/p&gt;
&lt;p&gt;这些技巧乍一看可能微不足道，但它们可以带来效率的大幅提升。让我们深入探讨这 9 种方法，改变你编写和优化 Python 代码的方式。&lt;/p&gt;
&lt;h2 id="1-更快的字符串连接巧妙选择-join-或-"&gt;&lt;a href="#1-%e6%9b%b4%e5%bf%ab%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2%e8%bf%9e%e6%8e%a5%e5%b7%a7%e5%a6%99%e9%80%89%e6%8b%a9-join-%e6%88%96-" class="header-anchor"&gt;&lt;/a&gt;1. 更快的字符串连接：巧妙选择 &amp;ldquo;join()&amp;rdquo; 或 &amp;ldquo;+&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;如果有很多字符串需要处理，字符串连接就会成为 Python 程序的瓶颈。&lt;/p&gt;
&lt;p&gt;在 Python 中，字符串连接基本上有两种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用&lt;code&gt;join()&lt;/code&gt;函数将一个字符串列表合并为一个&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;+&lt;/code&gt;或&lt;code&gt;+=&lt;/code&gt;符号将每个字符串添加到一个字符串中&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么哪种方式更快呢？&lt;/p&gt;
&lt;p&gt;让我们来定义 3 个不同的函数来连接相同的字符串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mylist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Yang&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Zhou&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;is&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;writing&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 使用&amp;#39;+&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;concat_plus&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;mylist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 使用&amp;#39;join()&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;concat_join&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mylist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 直接连接字符串，不使用列表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;concat_directly&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Yang&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Zhou&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;is&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;writing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据你的第一印象，你认为哪个函数最快，哪个最慢？&lt;/p&gt;
&lt;p&gt;实际结果可能会让你惊讶：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concat_plus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.002738415962085128&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concat_join&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.0008482920238748193&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;concat_directly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.00021425005979835987&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上所示，对于连接一个字符串列表，&lt;code&gt;join()&lt;/code&gt;方法比在 for 循环中逐个添加字符串要快。&lt;/p&gt;
&lt;p&gt;原因是直接的。一方面，字符串在 Python 中是不可变数据，在每次&lt;code&gt;+=&lt;/code&gt;操作中都会创建一个新的字符串并复制旧字符串，这在计算上是昂贵的。&lt;/p&gt;
&lt;p&gt;另一方面，&lt;code&gt;.join()&lt;/code&gt;方法专门优化了字符串的连接。它预先计算结果字符串的大小，然后一次性构建它。因此，它避免了在循环中&lt;code&gt;+=&lt;/code&gt;操作的开销，因此它更快。&lt;/p&gt;
&lt;p&gt;然而，在我们的测试中最快的函数是直接连接字符串字面量。它的高速度是由于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 解释器可以在编译时优化字符串字面量的连接，将它们变成单个字符串字面量。没有循环迭代或函数调用，这使得它是一个非常高效的操作。&lt;/li&gt;
&lt;li&gt;由于所有字符串在编译时都是已知的，Python 可以非常快速地执行此操作，比循环中的运行时连接或甚至优化的&lt;code&gt;.join()&lt;/code&gt;方法都要快得多。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总之，如果你需要连接一个字符串列表，请选择&lt;code&gt;join()&lt;/code&gt;而不是&lt;code&gt;+=&lt;/code&gt;。如果你想直接连接字符串，只需使用&lt;code&gt;+&lt;/code&gt;即可。&lt;/p&gt;
&lt;h2 id="2-更快的列表创建使用而不是list"&gt;&lt;a href="#2-%e6%9b%b4%e5%bf%ab%e7%9a%84%e5%88%97%e8%a1%a8%e5%88%9b%e5%bb%ba%e4%bd%bf%e7%94%a8%e8%80%8c%e4%b8%8d%e6%98%aflist" class="header-anchor"&gt;&lt;/a&gt;2. 更快的列表创建：使用&amp;quot;[]&amp;ldquo;而不是&amp;quot;list()&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;创建列表不是什么大不了的事。有两种常见的方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用&lt;code&gt;list()&lt;/code&gt;函数&lt;/li&gt;
&lt;li&gt;直接使用&lt;code&gt;[]&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;让我们使用一个简单的代码片段来测试它们的性能：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.1368238340364769&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.2958830420393497&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如结果所示，执行&lt;code&gt;list()&lt;/code&gt;函数比直接使用&lt;code&gt;[]&lt;/code&gt;要慢。&lt;/p&gt;
&lt;p&gt;这是因为&lt;code&gt;[]&lt;/code&gt;是字面量语法，而&lt;code&gt;list()&lt;/code&gt;是一个构造函数调用。调用函数无疑需要额外的时间。&lt;/p&gt;
&lt;p&gt;同样的逻辑，当创建字典时，我们也应该利用&lt;code&gt;{}&lt;/code&gt;而不是&lt;code&gt;dict()&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="3-更快的成员测试使用集合而不是列表"&gt;&lt;a href="#3-%e6%9b%b4%e5%bf%ab%e7%9a%84%e6%88%90%e5%91%98%e6%b5%8b%e8%af%95%e4%bd%bf%e7%94%a8%e9%9b%86%e5%90%88%e8%80%8c%e4%b8%8d%e6%98%af%e5%88%97%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;3. 更快的成员测试：使用集合而不是列表
&lt;/h2&gt;&lt;p&gt;成员检查操作的性能在很大程度上取决于底层数据结构：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;large_dataset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;search_element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2077&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;large_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;large_dataset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;large_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;large_dataset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_membership_test&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;search_element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;large_list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_membership_test&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;search_element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;large_set&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_membership_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.01112208398990333&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;set_membership_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3.27499583363533e-05&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上述代码所示，集合中的成员测试比列表中的要快得多。&lt;/p&gt;
&lt;p&gt;为什么会这样？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 Python 列表中，成员测试（&lt;code&gt;element in list&lt;/code&gt;）是通过遍历每个元素直到找到所需元素或到达列表末尾来完成的。因此，此操作的时间复杂度为 O(n)。&lt;/li&gt;
&lt;li&gt;Python 中的集合是作为哈希表实现的。当检查成员资格（&lt;code&gt;element in set&lt;/code&gt;）时，Python 使用哈希机制，其平均时间复杂度为 O(1)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里的要点是在编写程序时要仔细考虑底层数据结构。利用正确的数据结构可以显著加快我们的代码速度。&lt;/p&gt;
&lt;h2 id="4-更快的数据生成使用推导式而不是-for-循环"&gt;&lt;a href="#4-%e6%9b%b4%e5%bf%ab%e7%9a%84%e6%95%b0%e6%8d%ae%e7%94%9f%e6%88%90%e4%bd%bf%e7%94%a8%e6%8e%a8%e5%af%bc%e5%bc%8f%e8%80%8c%e4%b8%8d%e6%98%af-for-%e5%be%aa%e7%8e%af" class="header-anchor"&gt;&lt;/a&gt;4. 更快的数据生成：使用推导式而不是 for 循环
&lt;/h2&gt;&lt;p&gt;Python 中有四种类型的推导式：列表、字典、集合和生成器。它们不仅为创建相对数据结构提供了更简洁的语法，而且比使用 for 循环有更好的性能，因为它们在 Python 的 C 实现中进行了优化。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_squares_for_loop&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;squares&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_squares_comprehension&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generate_squares_for_loop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.2797503340989351&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;generate_squares_comprehension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.2364629579242319&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述代码是列表推导式和 for 循环之间的简单速度比较。如结果所示，列表推导式更快。&lt;/p&gt;
&lt;h2 id="5-更快的循环优先使用局部变量"&gt;&lt;a href="#5-%e6%9b%b4%e5%bf%ab%e7%9a%84%e5%be%aa%e7%8e%af%e4%bc%98%e5%85%88%e4%bd%bf%e7%94%a8%e5%b1%80%e9%83%a8%e5%8f%98%e9%87%8f" class="header-anchor"&gt;&lt;/a&gt;5. 更快的循环：优先使用局部变量
&lt;/h2&gt;&lt;p&gt;在 Python 中，访问局部变量比访问全局变量或对象的属性要快。&lt;/p&gt;
&lt;p&gt;这里有一个实例来证明这一点：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_dot_notation&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_local_variable&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_dot_notation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.036605041939765215&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test_local_variable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.024470250005833805&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这就是 Python 的工作方式。直观地说，当一个函数被编译时，里面的局部变量是已知的，但其他外部变量需要时间来检索。&lt;/p&gt;
&lt;p&gt;这是一个小问题，但我们可以利用它来优化我们在处理大量数据时的代码。&lt;/p&gt;
&lt;h2 id="6-更快的执行优先使用内置模块和库"&gt;&lt;a href="#6-%e6%9b%b4%e5%bf%ab%e7%9a%84%e6%89%a7%e8%a1%8c%e4%bc%98%e5%85%88%e4%bd%bf%e7%94%a8%e5%86%85%e7%bd%ae%e6%a8%a1%e5%9d%97%e5%92%8c%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;6. 更快的执行：优先使用内置模块和库
&lt;/h2&gt;&lt;p&gt;当工程师们说 Python 时，他们通常指的是 CPython。因为 CPython 是 Python 语言的默认和最广泛使用的实现。&lt;/p&gt;
&lt;p&gt;鉴于其大多数内置模块和库都是用 C 语言编写的，C 是一种更快的低级语言，我们应该利用内置的武器库，避免重新发明轮子。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_frequency_custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_frequency_builtin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;large_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;count_frequency_custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;large_list&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.005160166998393834&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;count_frequency_builtin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;large_list&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.002444291952997446&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上述程序比较了两种计算列表中元素频率的方法。我们可以看到，利用内置的&lt;code&gt;Counter&lt;/code&gt;来自&lt;code&gt;collections&lt;/code&gt;模块更快、更整洁、更好。&lt;/p&gt;
&lt;h2 id="7-更快的函数调用利用缓存装饰器进行简单的记忆化"&gt;&lt;a href="#7-%e6%9b%b4%e5%bf%ab%e7%9a%84%e5%87%bd%e6%95%b0%e8%b0%83%e7%94%a8%e5%88%a9%e7%94%a8%e7%bc%93%e5%ad%98%e8%a3%85%e9%a5%b0%e5%99%a8%e8%bf%9b%e8%a1%8c%e7%ae%80%e5%8d%95%e7%9a%84%e8%ae%b0%e5%bf%86%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;7. 更快的函数调用：利用缓存装饰器进行简单的记忆化
&lt;/h2&gt;&lt;p&gt;缓存是一种常用的技术，用于避免重复计算并加速程序。&lt;/p&gt;
&lt;p&gt;幸运的是，在大多数情况下，我们不需要编写自己的缓存处理代码，因为 Python 为此目的提供了一个现成的装饰器——&lt;code&gt;@functools.cache&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;例如，以下代码将执行两个斐波那契数生成函数，一个有缓存装饰器，另一个没有：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@functools.cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fibonacci_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fibonacci_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fibonacci_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 测试每个函数的执行时间&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.09499712497927248&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fibonacci_cached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 6.458023563027382e-06&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果证明了&lt;code&gt;functools.cache&lt;/code&gt;装饰器使我们的代码更快。&lt;/p&gt;
&lt;p&gt;基本的&lt;code&gt;fibonacci&lt;/code&gt;函数效率低下，因为在得到&lt;code&gt;fibonacci(30)&lt;/code&gt;的结果过程中，它多次重新计算相同的斐波那契数。&lt;/p&gt;
&lt;p&gt;缓存版本要快得多，因为它缓存了先前计算的结果。因此，它只计算一次每个斐波那契数，后续具有相同参数的调用从缓存中检索。&lt;/p&gt;
&lt;p&gt;仅仅添加一个内置装饰器就可以带来如此大的改进，这就是 Pythonic 的意思。😎&lt;/p&gt;
&lt;h2 id="8-更快的无限循环优先选择-while-1-而不是-while-true"&gt;&lt;a href="#8-%e6%9b%b4%e5%bf%ab%e7%9a%84%e6%97%a0%e9%99%90%e5%be%aa%e7%8e%af%e4%bc%98%e5%85%88%e9%80%89%e6%8b%a9-while-1-%e8%80%8c%e4%b8%8d%e6%98%af-while-true" class="header-anchor"&gt;&lt;/a&gt;8. 更快的无限循环：优先选择 &amp;ldquo;while 1&amp;rdquo; 而不是 &amp;ldquo;while True&amp;rdquo;
&lt;/h2&gt;&lt;p&gt;要创建一个无限 while 循环，我们可以使用&lt;code&gt;while True&lt;/code&gt;或&lt;code&gt;while 1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;它们性能的差异通常可以忽略不计。但有趣的是，&lt;code&gt;while 1&lt;/code&gt;稍微快一点。&lt;/p&gt;
&lt;p&gt;这是因为&lt;code&gt;1&lt;/code&gt;是字面量，但&lt;code&gt;True&lt;/code&gt;是一个需要在 Python 的全局作用域中查找的全局名称，因此需要一个微小的开销。&lt;/p&gt;
&lt;p&gt;让我们也在代码片段中检查这两种方式的实际比较：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;timeit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loop_with_true&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loop_with_one&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop_with_true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.1733035419601947&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loop_with_one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 0.16412191605195403&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如我们所见，&lt;code&gt;while 1&lt;/code&gt;确实稍微快一点。&lt;/p&gt;
&lt;p&gt;然而，现代 Python 解释器（如 CPython）高度优化，这种差异通常可以忽略不计。更不用说&lt;code&gt;while True&lt;/code&gt;比&lt;code&gt;while 1&lt;/code&gt;更易读。&lt;/p&gt;
&lt;h2 id="9-更快的启动智能导入-python-模块"&gt;&lt;a href="#9-%e6%9b%b4%e5%bf%ab%e7%9a%84%e5%90%af%e5%8a%a8%e6%99%ba%e8%83%bd%e5%af%bc%e5%85%a5-python-%e6%a8%a1%e5%9d%97" class="header-anchor"&gt;&lt;/a&gt;9. 更快的启动：智能导入 Python 模块
&lt;/h2&gt;&lt;p&gt;在 Python 脚本的顶部导入所有模块似乎是自然而然的事情。&lt;/p&gt;
&lt;p&gt;实际上，我们不必这么做。&lt;/p&gt;
&lt;p&gt;此外，如果一个模块太大，按需导入它是一个更好的主意。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_function&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;heavy_module&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 函数的其余部分&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上述代码，&lt;code&gt;heavy_module&lt;/code&gt;在函数内部导入。这是一种“懒加载”的思想，即导入被推迟到&lt;code&gt;my_function&lt;/code&gt;被调用时。&lt;/p&gt;
&lt;p&gt;这种方法的好处是，如果&lt;code&gt;my_function&lt;/code&gt;在脚本执行过程中从未被调用，那么&lt;code&gt;heavy_module&lt;/code&gt;就永远不会被加载，节省了资源并减少了脚本的启动时间。&lt;/p&gt;</description></item><item><title>数据库选型必看：MySQL 与 MariaDB 功能对比全解析</title><link>https://xiaobox.github.io/p/2024-09-09-shu-ju-ku-xuan-xing-bi-kan-mysql-yu-mariadb-gong-neng-dui-bi/</link><pubDate>Mon, 09 Sep 2024 04:12:18 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-09-09-shu-ju-ku-xuan-xing-bi-kan-mysql-yu-mariadb-gong-neng-dui-bi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-09-shu-ju-ku-xuan-xing-bi-kan-mysql-yu-mariadb-gong-neng-dui-bi/cover.jpg" alt="Featured image of post 数据库选型必看：MySQL 与 MariaDB 功能对比全解析" /&gt;&lt;h2 id="引言"&gt;&lt;a href="#%e5%bc%95%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;引言
&lt;/h2&gt;&lt;p&gt;在当今的数据驱动世界中，数据库的选择对任何企业和开发者来说都是一个至关重要的决策。MySQL 和 MariaDB，这两款数据库管理系统（DBMS）因其高性能、稳定性和广泛的应用场景而广受欢迎。尽管它们有着共同的起源，但随着时间的推移，两者在功能特性和发展路线上逐渐展现出差异。本文将深入探讨 MySQL 与 MariaDB 在表格定义和数据定义语言（DDL）方面的不同，并针对模式变更操作提供实用的指南。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-09-shu-ju-ku-xuan-xing-bi-kan-mysql-yu-mariadb-gong-neng-dui-bi/001-edac8409.png"&gt;&lt;/p&gt;
&lt;h2 id="一表格功能差异详解"&gt;&lt;a href="#%e4%b8%80%e8%a1%a8%e6%a0%bc%e5%8a%9f%e8%83%bd%e5%b7%ae%e5%bc%82%e8%af%a6%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;一、表格功能差异详解
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;JSON 列类型&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL 的 JSON 支持&lt;/strong&gt;：MySQL 从 5.7 版本开始引入了原生的 JSON 数据类型，这使得存储和查询 JSON 文档变得更加高效。这一特性对于需要处理复杂数据结构的现代 Web 应用来说尤为重要。MySQL 的 JSON 类型支持多种 JSON 函数，如 JSON_SET、JSON_INSERT、JSON_REPLACE 等，这些函数允许用户直接在数据库层面进行 JSON 文档的修改，无需将整个文档加载到应用层。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MariaDB 的 JSON 处理&lt;/strong&gt;：相比之下，MariaDB 采取了不同的策略。在 MariaDB 中，JSON 被视为 LONGTEXT 类型的一个别名，并通过 CHECK 约束来确保存储的数据是有效的 JSON 格式。这种方法虽然不如 MySQL 的原生 JSON 类型高效，但它提供了更高的灵活性。例如，用户可以在不更改表结构的情况下，将现有的 LONGTEXT 列转换为 JSON 类型。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;IP 地址和 UUID 列类型&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MariaDB 的创新&lt;/strong&gt;：MariaDB 在数据类型方面进行了一些创新，其中包括提供了专门的列类型来存储 IPv4 和 IPv6 地址，以及 UUID 值。这些类型分别为 INET_ATON、INET6_ATON 和 UUID。使用这些专用类型可以简化网络相关数据的存储和查询，同时提高性能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL 的传统处理&lt;/strong&gt;：在 MySQL 中，存储 IP 地址和 UUID 通常需要使用 VARCHAR 或 CHAR 类型，并依赖于应用层或数据库函数来进行格式验证和转换。这种方法虽然通用，但在处理大量网络数据时可能不够高效。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;&lt;strong&gt;数值列类型&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL 的简化&lt;/strong&gt;：从 MySQL 8.0 版本开始，数值列类型不再关注显示宽度。这意味着，例如，INT(11) 和 INT 的存储空间和范围是相同的。这一变化旨在简化数据类型的使用，避免用户对显示宽度的误解。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MariaDB 的传统保留&lt;/strong&gt;：与此相反，MariaDB 仍然保留了数值列类型的显示宽度。这意味着在 MariaDB 中，INT(11) 和 INT 可能具有不同的含义，尤其是在进行数据迁移或模式兼容性测试时需要特别注意。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;&lt;strong&gt;时间列类型&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;处理 Y2K38 问题&lt;/strong&gt;：Y2K38 问题是指 32 位时间戳在 2038 年 1 月 19 日将达到其最大值，从而导致日期和时间处理上的问题。MariaDB 通过提供 TIMESTAMP 类型的新存储格式来解决这个问题，该格式支持更大的时间范围。而 MySQL 则依赖于用户自行处理这个问题，例如通过使用 BIGINT 类型来存储时间戳。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="9"&gt;
&lt;li&gt;&lt;strong&gt;空间列类型&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;空间数据支持&lt;/strong&gt;：MySQL 和 MariaDB 都提供了空间列类型，如 POINT、LINESTRING、POLYGON 等，用于存储地理空间数据。然而，在空间参考系统（SRID）的支持上，MySQL 提供了更广泛的选择，这使得它在处理复杂的空间数据时更具优势。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="11"&gt;
&lt;li&gt;&lt;strong&gt;字符集和校对规则&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;差异显著&lt;/strong&gt;：字符集和校对规则是数据库国际化支持的重要组成部分。在这两个方面，MySQL 和 MariaDB 存在显著差异。MariaDB 提供了一些 MySQL 不支持的字符集和校对规则，这使得它在处理特定语言和字符集时更加灵活。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="二压缩功能对比"&gt;&lt;a href="#%e4%ba%8c%e5%8e%8b%e7%bc%a9%e5%8a%9f%e8%83%bd%e5%af%b9%e6%af%94" class="header-anchor"&gt;&lt;/a&gt;二、压缩功能对比
&lt;/h2&gt;&lt;p&gt;数据库压缩是提高存储效率、降低存储成本的重要手段。MySQL 和 MariaDB 在压缩功能上都进行了创新和优化，但各有侧重点。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-09-shu-ju-ku-xuan-xing-bi-kan-mysql-yu-mariadb-gong-neng-dui-bi/002-7e5a2535.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MySQL 的压缩技术&lt;/strong&gt;：MySQL 支持 InnoDB 存储引擎的传统压缩表，这种压缩可以显著减少磁盘空间的使用。在创建表时，可以通过指定&lt;code&gt;ROW_FORMAT=COMPRESSED&lt;/code&gt;来启用压缩。这种压缩技术在处理大量静态数据或归档数据时尤其有效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MariaDB 的列级压缩&lt;/strong&gt;：MariaDB 不仅支持 InnoDB 的压缩表，还引入了列级压缩功能。这意味着用户可以针对表中的特定列进行压缩，而不是整个行。这种精细化的压缩策略可以在节省存储空间的同时，减少对性能的影响。列级压缩特别适合于那些具有不同数据访问模式的大型表，可以针对不常访问或数据重复性高的列进行压缩。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="三默认值和生成列的差异"&gt;&lt;a href="#%e4%b8%89%e9%bb%98%e8%ae%a4%e5%80%bc%e5%92%8c%e7%94%9f%e6%88%90%e5%88%97%e7%9a%84%e5%b7%ae%e5%bc%82" class="header-anchor"&gt;&lt;/a&gt;三、默认值和生成列的差异
&lt;/h2&gt;&lt;p&gt;默认值和生成列是数据库设计中的重要概念，它们可以帮助确保数据的完整性和一致性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;默认值的使用&lt;/strong&gt;：在 MySQL 和 MariaDB 中，都可以为列指定默认值。这些默认值可以是常量，也可以是复杂的表达式。然而，两者在支持的函数和表达式方面存在差异。例如，MySQL 可能支持某些特定的内置函数作为默认值，而 MariaDB 则可能不支持。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成列的特性&lt;/strong&gt;：生成列是 MariaDB 5.2 版本引入的特性，MySQL 从 5.7 版本开始也支持这一特性。生成列的值是由表中其他列的值计算得出的，这意味着它们是虚拟的，不需要实际存储在磁盘上。生成列在处理计算字段时非常有用，可以减少应用层的计算负担。不过，MySQL 和 MariaDB 在生成列的实现细节上有所不同，例如在支持的函数和表达式方面。&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="四外键和-check-约束的应用"&gt;&lt;a href="#%e5%9b%9b%e5%a4%96%e9%94%ae%e5%92%8c-check-%e7%ba%a6%e6%9d%9f%e7%9a%84%e5%ba%94%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;四、外键和 CHECK 约束的应用
&lt;/h2&gt;&lt;p&gt;外键和 CHECK 约束是保证数据库数据完整性的重要工具。它们在 MySQL 和 MariaDB 中的实现和应用有所不同。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;外键约束&lt;/strong&gt;：MySQL 和 MariaDB 都支持外键约束，用于强制执行表之间的关系。然而，两者在外键约束的语法、性能和错误处理上存在差异。例如，MariaDB 在某些情况下可能提供了更灵活的外键约束选项。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CHECK 约束&lt;/strong&gt;：CHECK 约束用于限制列的取值范围。在 MySQL 8.0 之前，CHECK 约束是语法糖，并不实际执行。而从 MySQL 8.0 开始，CHECK 约束得到了实际的支持。MariaDB 则一直支持 CHECK 约束，并且在某些情况下提供了更丰富的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="五其他功能差异"&gt;&lt;a href="#%e4%ba%94%e5%85%b6%e4%bb%96%e5%8a%9f%e8%83%bd%e5%b7%ae%e5%bc%82" class="header-anchor"&gt;&lt;/a&gt;五、其他功能差异
&lt;/h2&gt;&lt;p&gt;除了上述差异外，MySQL 和 MariaDB 在许多其他功能上也存在差异，这些差异在某些特定场景下可能非常关键。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;系统版本化表&lt;/strong&gt;：MariaDB 提供了系统版本化表的功能，允许用户查询数据的历史版本。这对于需要跟踪数据变更历史的应用非常有用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用时间周期表&lt;/strong&gt;：这是 MariaDB 的一个独特功能，允许用户定义数据的有效时间范围。这种表对于处理具有时间限制的数据非常有用，例如合同、订阅和价格信息。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;二时态表&lt;/strong&gt;：MariaDB 的二时态表功能允许用户查询数据的历史状态，这对于历史数据分析非常有用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="六操作差异分析"&gt;&lt;a href="#%e5%85%ad%e6%93%8d%e4%bd%9c%e5%b7%ae%e5%bc%82%e5%88%86%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;六、操作差异分析
&lt;/h2&gt;&lt;p&gt;在实际操作中，MySQL 和 MariaDB 在执行 DDL 操作时存在一些差异，这些差异可能会影响到数据库的性能和可用性。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ALTER TABLE 操作&lt;/strong&gt;：ALTER TABLE 是数据库维护中常见的操作，用于修改表结构。MySQL 和 MariaDB 在执行 ALTER TABLE 操作时，尤其是在线 DDL 变更方面，存在差异。例如，MariaDB 的 ALGORITHM 选项允许用户控制 DDL 操作的执行方式，而 MySQL 则提供了 INSTANT 算法来减少 DDL 操作对性能的影响。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;索引构建&lt;/strong&gt;：在创建索引时，MySQL 8.0.27+支持并行构建索引，这可以显著提高索引创建的速度。而 MariaDB 在索引构建方面的优化则有所不同，它可能提供了不同的性能特点和选项。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DROP TABLE 操作&lt;/strong&gt;：在某些情况下，MySQL 在执行 DROP TABLE 操作时可能会遇到与 InnoDB 缓冲池大小相关的问题，导致系统停滞。MariaDB 则可能通过不同的机制来避免这些问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="七模式元数据差异"&gt;&lt;a href="#%e4%b8%83%e6%a8%a1%e5%bc%8f%e5%85%83%e6%95%b0%e6%8d%ae%e5%b7%ae%e5%bc%82" class="header-anchor"&gt;&lt;/a&gt;七、模式元数据差异
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-09-09-shu-ju-ku-xuan-xing-bi-kan-mysql-yu-mariadb-gong-neng-dui-bi/003-85a1cea8.png"&gt;&lt;/p&gt;
&lt;p&gt;数据库的模式元数据是数据库结构的信息，它对于数据库工具和监控系统的兼容性至关重要。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;信息模式表&lt;/strong&gt;：MySQL 和 MariaDB 在信息模式表（INFORMATION_SCHEMA）中提供的信息存在差异。这些差异可能会影响到依赖于这些信息的数据库工具和脚本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SHOW 查询&lt;/strong&gt;：SHOW 查询是获取数据库状态和配置信息的常用方法。在 MySQL 和 MariaDB 中，SHOW 查询返回的结果可能会有所不同，这可能会影响到数据库监控和管理工具。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="八结论与建议"&gt;&lt;a href="#%e5%85%ab%e7%bb%93%e8%ae%ba%e4%b8%8e%e5%bb%ba%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;八、结论与建议
&lt;/h2&gt;&lt;p&gt;通过上述分析，我们可以看到，尽管 MySQL 和 MariaDB 在许多方面都非常相似，但它们在表格定义、DDL 操作、功能特性和性能优化上都有各自的特点和优势。对于数据库管理员和开发者来说，了解这些差异对于选择合适的数据库系统至关重要。&lt;/p&gt;
&lt;p&gt;以下是一些基于本文分析的结论和建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;选择合适的数据库&lt;/strong&gt;：如果你的应用需要处理大量的 JSON 数据，或者你更倾向于使用原生的 JSON 类型，那么 MySQL 可能是更好的选择。相反，如果你的应用需要处理 IP 地址和 UUID 数据，并且希望使用列级压缩，MariaDB 可能更适合你的需求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;考虑兼容性问题&lt;/strong&gt;：在进行数据库迁移时，兼容性是一个重要的考虑因素。如果你的应用依赖于特定的字符集或校对规则，或者使用了特定的 DDL 操作，那么在迁移前进行详细的兼容性测试是非常重要的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能优化&lt;/strong&gt;：对于性能敏感的应用，了解不同数据库系统的性能特点是非常关键的。例如，MySQL 的并行索引构建和 MariaDB 的列级压缩都可以显著提高性能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;持续学习和关注&lt;/strong&gt;：数据库技术是不断发展的，新的版本可能会引入新的特性和改进。因此，持续学习和关注 MySQL 和 MariaDB 的发展动态，可以帮助你更好地利用这些数据库系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="结语"&gt;&lt;a href="#%e7%bb%93%e8%af%ad" class="header-anchor"&gt;&lt;/a&gt;结语
&lt;/h2&gt;&lt;p&gt;数据库的选择和管理是一个复杂的过程，需要综合考虑多种因素。希望本文能够为你提供关于 MySQL 和 MariaDB 在表格定义和数据定义语言方面的差异的深入理解，并在你的数据库设计和维护工作中提供帮助。无论是选择 MySQL 还是 MariaDB，关键是要确保所选的数据库系统能够满足你的业务需求，同时提供良好的性能和可扩展性。&lt;/p&gt;</description></item><item><title>探索 multipart：文件传输的新境界！</title><link>https://xiaobox.github.io/p/2024-08-29-tan-suo-multipart-wen-jian-chuan-shu-de-xin-jing-jie/</link><pubDate>Thu, 29 Aug 2024 09:11:45 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-08-29-tan-suo-multipart-wen-jian-chuan-shu-de-xin-jing-jie/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-29-tan-suo-multipart-wen-jian-chuan-shu-de-xin-jing-jie/cover.jpg" alt="Featured image of post 探索 multipart：文件传输的新境界！" /&gt;&lt;h3 id="http-multipart-介绍"&gt;&lt;a href="#http-multipart-%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;HTTP Multipart 介绍
&lt;/h3&gt;&lt;p&gt;在日常的网络编程和数据传输中，我们经常会遇到“multipart”或“form-encoded data”这样的术语。尽管这些术语在我日常工作中随处可见，但我却从未真正深入理解或使用过它们，因为 HTTP 库已经为我处理好了这些细节。然而，最近由于在工作中的需求，我不得不深入研究 multipart 的工作原理。我发现，正确地使用 multipart 可以显著提高文件上传的速度并减少内存消耗。接下来，我将详细解释 multipart 的工作原理，希望能帮助你在 HTTP 服务器/客户端中节省时间和内存。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-29-tan-suo-multipart-wen-jian-chuan-shu-de-xin-jing-jie/001-017d96b2.png"&gt;&lt;/p&gt;
&lt;h4 id="一什么是-mime-类型"&gt;&lt;a href="#%e4%b8%80%e4%bb%80%e4%b9%88%e6%98%af-mime-%e7%b1%bb%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;一、什么是 MIME 类型？
&lt;/h4&gt;&lt;p&gt;在深入探讨 multipart 之前，我们需要先了解什么是 MIME 类型。MIME（Multipurpose Internet Mail Extensions）是一种标准，用于描述文档的性质和格式。简单来说，MIME 类型就是文件的“身份证”，它告诉计算机这个文件是什么类型的，应该用什么样的程序来打开。例如，常见的 MIME 类型有&lt;code&gt;text/plain&lt;/code&gt;（纯文本）、&lt;code&gt;image/jpeg&lt;/code&gt;（JPEG 图片）和&lt;code&gt;application/pdf&lt;/code&gt;（PDF 文档）等。&lt;/p&gt;
&lt;h4 id="二为什么需要使用-multipart"&gt;&lt;a href="#%e4%ba%8c%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81%e4%bd%bf%e7%94%a8-multipart" class="header-anchor"&gt;&lt;/a&gt;二、为什么需要使用 Multipart？
&lt;/h4&gt;&lt;p&gt;在 multipart 出现之前，上传文件的标准是&lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;。这种方式要求客户端在上传文件之前对其进行 URL 编码。如果文件主要是 ASCII 文本，URL 编码是高效的；但如果文件主要是二进制数据，那么几乎每个字节都需要进行 URL 转义，这会非常低效。&lt;/p&gt;
&lt;p&gt;如果你想上传多个文件而不进行编码，你可以发送多个 HTTP 请求。但这样做的延迟会比在一个请求中发送所有文件更高。为了解决这个问题，1998 年的 RFC 2388 提出了一个新的标准——“multipart/form-data”。这个标准允许你在一个 HTTP 正文中发送多个文件，而无需对它们进行编码。无需编码意味着你可以节省大量的 CPU 周期，并保持总体正文大小较小。&lt;/p&gt;
&lt;p&gt;这个协议最初是为从 HTML 表单上传文件而设计的，因此得名。但实际上，你可以使用它从任何你想要的地方上传文件——规范中没有任何部分要求&lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;或任何 HTML。你可以使用它从任何 HTTP 客户端向任何 HTTP 服务器上传文件。&lt;/p&gt;
&lt;p&gt;当你上传文件时，如果你把文件打包成一个 JSON 对象，服务器需要先接收整个 JSON 对象，然后才能开始处理。这样会占用更多内存，而且要等所有文件都上传完毕才能开始处理。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 读取多个文件内容&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file1.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;file2.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;file3.txt&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;file_contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;file_contents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 将文件内容打包成 JSON 对象&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;files&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;file_contents&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 将 JSON 对象转换为字符串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;json_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 上传 JSON 对象到服务器&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://example.com/upload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JSON 格式不支持直接在数据结构中嵌入二进制数据（如图片、视频文件等）。由于 JSON 不能直接包含二进制数据，所以如果你想要通过 JSON 格式来上传一个图片或视频文件，你就需要将这个二进制文件转换成一种可以被 JSON 处理的格式。Base64 就是这样一种格式。&lt;/p&gt;
&lt;p&gt;如果你使用 multipart 格式上传，服务器可以一个接一个地接收文件，就像流水一样。这意味着服务器可以更早开始处理第一个文件，而不需要等待其他文件，这样更节省内存，速度也更快。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;DOCTYPE&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Multipart&lt;/span&gt; &lt;span class="n"&gt;Upload&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/upload&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;post&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;enctype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;multipart/form-data&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;file&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;file1&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;file&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;file2&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Upload&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/upload&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_files&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 获取名为 file1 和 file2 的文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;file1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;file2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;file2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 处理 file1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 处理 file2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Files processed successfully!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 这里是处理文件的地方&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 可以将文件保存到磁盘或其他操作&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;__main__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="三multipart-是什么"&gt;&lt;a href="#%e4%b8%89multipart-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;三、Multipart 是什么？
&lt;/h4&gt;&lt;p&gt;MIME 类型分为两类：离散型和多部分型。离散型包含一个文档，例如&lt;code&gt;application/&lt;/code&gt;（二进制）、&lt;code&gt;image/&lt;/code&gt;、&lt;code&gt;text/&lt;/code&gt;等。多部分型是包含多个部分的文档，这些部分可以有自己的 MIME 类型。有两种多部分类型：&lt;code&gt;message/&lt;/code&gt;和&lt;code&gt;multipart/&lt;/code&gt;——是的，有点让人困惑，multipart 既可以是一种类型，也可以是一个类别。&lt;code&gt;message/&lt;/code&gt;类型基本上不再用于任何东西了，但&lt;code&gt;multipart/&lt;/code&gt;仍然非常重要。你经常看到&lt;code&gt;multipart/form-data&lt;/code&gt;用于通过 HTML 表单从 Web 浏览器发送文件到服务器。multipart 中的“part”指的是一个文档。这个类型本可以叫做&lt;code&gt;multidocument&lt;/code&gt;！&lt;/p&gt;
&lt;p&gt;需要注意的是，它并不一定要包含多个文件。它可以只包含一个文件，使用 multipart 进行高效的二进制编码。&lt;/p&gt;
&lt;h4 id="四multipart-是如何实现的"&gt;&lt;a href="#%e5%9b%9bmultipart-%e6%98%af%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;四、Multipart 是如何实现的？
&lt;/h4&gt;&lt;p&gt;如果内容类型是&lt;code&gt;multipart/form-data&lt;/code&gt;，那么 HTTP 正文中包含多个部分（即文档）。每个部分由一个“边界分隔符”分隔。根 HTTP 消息有一个头部定义了边界分隔符，以便服务器知道每个部分之间的边界在哪里。每个部分也有一些头部：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Content-Disposition&lt;/code&gt;头部定义了每个部分的文件名或包含它的表单字段的名称（仅当你使用实际的 HTML 表单元素时才相关）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Type&lt;/code&gt;头部定义了每个部分的文件类型（技术上是它们的 MIME 类型，但这两者大致相当）。它默认为&lt;code&gt;text/plain&lt;/code&gt;。非结构化二进制数据应使用&lt;code&gt;application/octet-stream&lt;/code&gt;，但如果你知道类型，你应该使用例如&lt;code&gt;application/zip&lt;/code&gt;、&lt;code&gt;application/pdf&lt;/code&gt;等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其他头部不能使用！根据 RFC 7578 的说法：“&lt;code&gt;multipart/form-data&lt;/code&gt;媒体类型不支持除&lt;code&gt;Content-Type&lt;/code&gt;、&lt;code&gt;Content-Disposition&lt;/code&gt;和（在有限情况下）&lt;code&gt;Content-Transfer-Encoding&lt;/code&gt;之外的任何 MIME 头部字段。其他头部字段不得包含且必须被忽略。”&lt;/p&gt;
&lt;p&gt;以下是一个来自 Stack Overflow 的实际示例，展示了 HTTP 正文的样子。这个正文是一个包含 3 个 GIF 的多部分。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;POST /cgi-bin/qtest HTTP/1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;Content-Type: multipart/form-data; boundary=2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;Content-Length: 514
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;Content-Disposition: form-data; name=&amp;#34;datafile1&amp;#34;; filename=&amp;#34;r.gif&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;Content-Type: image/gif
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;GIF87a.D..;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;Content-Disposition: form-data; name=&amp;#34;datafile2&amp;#34;; filename=&amp;#34;g.gif&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;Content-Type: image/gif
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;GIF87a.D..;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;Content-Disposition: form-data; name=&amp;#34;datafile3&amp;#34;; filename=&amp;#34;b.gif&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;Content-Type: image/gif
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;GIF87a.D..;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f--
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="五压缩"&gt;&lt;a href="#%e4%ba%94%e5%8e%8b%e7%bc%a9" class="header-anchor"&gt;&lt;/a&gt;五、压缩
&lt;/h4&gt;&lt;p&gt;你可以对整个 Multipart 响应进行 gzip 压缩，但不能选择性地压缩特定部分。这是因为根 HTTP 正文定义了整个消息的压缩头部，包括 multipart 正文中的所有部分。因此，客户端无法告诉服务器“这个特定部分是压缩的，但那个不是”。&lt;/p&gt;
&lt;p&gt;如上所述，multipart 的文档中只允许使用 3 个特定的 HTTP 头部——而压缩头部不是其中之一。&lt;/p&gt;
&lt;h4 id="六为什么这很有趣"&gt;&lt;a href="#%e5%85%ad%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%99%e5%be%88%e6%9c%89%e8%b6%a3" class="header-anchor"&gt;&lt;/a&gt;六、为什么这很有趣？
&lt;/h4&gt;&lt;p&gt;所以，“multipart”或“form-encoded data”是一种包含多个文件的 MIME 类型。每个文件都有自己的 MIME 类型和名称。从历史上看，这比上传多个文件的其他方式是一个很大的改进，因为它可以发送原始二进制文件而无需额外的编码或转义。&lt;/p&gt;
&lt;p&gt;在我写这篇博客文章之前，我觉得 multipart 有点无聊。它似乎有点过时和落后——我的意思是，它是在 HTML 表单是 Web 技术的前沿时编写的。自从它的 RFC 首次发布以来已经有 25 年了。我们肯定有更好的上传文件的方法了！&lt;/p&gt;
&lt;p&gt;但实际上，这在抽象意义上是相当有趣的。它试图有效地组合多个文件上传，而“我们如何将许多文件组合在一起”的问题在计算机科学中总是一个有趣的问题。我喜欢 JSON 的原因之一是它很容易组合。如果每个文件上传是一个 JSON 正文，那么组合它们是微不足道的：只需将 n 个单独的 JSON 正文组合成一个包含 n 个字段的大正文。&lt;/p&gt;
&lt;p&gt;但这性能不佳：你必须将文件内容 base64 编码，因为 JSON 只能处理文本，不能处理二进制；服务器必须将整个 JSON 正文缓冲到 RAM 中才能解码。&lt;/p&gt;
&lt;p&gt;我认为&lt;code&gt;multipart/form-data&lt;/code&gt;是试图有效地组合多个文件上传的一种尝试。这种权衡带来了一些复杂性，比如边界和内容处置。我想知道现代解决这个问题的方案会是什么样子。&lt;/p&gt;
&lt;p&gt;显然，&lt;code&gt;multipart/form-data&lt;/code&gt;已经足够好了，因为它到处都在使用。但如果你知道任何解决这个问题的替代方案，请在评论中告诉我！&lt;/p&gt;
&lt;h4 id="七总结"&gt;&lt;a href="#%e4%b8%83%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;七、总结
&lt;/h4&gt;&lt;p&gt;通过本文的介绍，我们可以看到 multipart 在文件上传中的重要性和优势。它不仅提高了文件上传的效率，还减少了内存消耗。尽管 multipart 的设计初衷是为了处理 HTML 表单中的文件上传，但它的应用范围远不止于此。无论是 Rust 还是其他编程语言，都可以利用 multipart 来实现高效、稳定的文件传输。&lt;/p&gt;
&lt;p&gt;在实际应用中，我们可以通过选择合适的库和框架来简化 multipart 的处理过程。例如，在 Rust 中，我们可以使用 axum 或 reqwest 等库来轻松处理 multipart 请求。这些库提供了丰富的功能和良好的性能，可以帮助我们快速构建高效、可靠的文件上传功能。&lt;/p&gt;
&lt;p&gt;此外，了解 multipart 的工作原理也有助于我们更好地优化和调整文件上传的策略。例如，我们可以通过调整边界分隔符的选择、优化 Content-Disposition 和 Content-Type 头部的设置等方式来提高文件上传的效率和稳定性。&lt;/p&gt;
&lt;p&gt;最后，虽然 multipart 已经存在了很长时间，但它仍然是一个值得深入研究和探讨的话题。随着网络技术的不断发展和进步，我们可能会遇到更多新的挑战和需求。因此，持续学习和探索新的技术和方法是非常重要的。&lt;/p&gt;
&lt;p&gt;希望本文能为你提供一些关于 multipart 的有价值的信息和启发。如果你有任何疑问或建议，请随时在评论区留言交流。&lt;/p&gt;</description></item><item><title>Java 和 JVM 自 JDK 8 至 21 的特性概览</title><link>https://xiaobox.github.io/p/2024-03-15-java-he-jvm-zi-jdk-8-zhi-21-de-te-xing-gai-lan/</link><pubDate>Fri, 15 Mar 2024 16:48:40 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-03-15-java-he-jvm-zi-jdk-8-zhi-21-de-te-xing-gai-lan/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-03-15-java-he-jvm-zi-jdk-8-zhi-21-de-te-xing-gai-lan/cover.jpg" alt="Featured image of post Java 和 JVM 自 JDK 8 至 21 的特性概览" /&gt;&lt;h2 id="引言"&gt;&lt;a href="#%e5%bc%95%e8%a8%80" class="header-anchor"&gt;&lt;/a&gt;引言
&lt;/h2&gt;&lt;p&gt;Java，这门自 1995 年诞生以来就广受欢迎的编程语言，以其跨平台的特性、强大的生态系统和稳定的性能表现，成为了软件开发领域的一个重要里程碑。随着时间的推移，Java 不断演进，以适应新的技术挑战和市场需求。自 JDK 8 发布以来，Java 平台经历了一系列重大的更新，每一次更新都为开发者带来了新的工具和能力，以构建更加高效、安全和现代化的应用程序。&lt;/p&gt;
&lt;h3 id="java-的历史和发展"&gt;&lt;a href="#java-%e7%9a%84%e5%8e%86%e5%8f%b2%e5%92%8c%e5%8f%91%e5%b1%95" class="header-anchor"&gt;&lt;/a&gt;Java 的历史和发展
&lt;/h3&gt;&lt;p&gt;从最初的 Java 1.0 到现在的 JDK 21，Java 语言和其运行环境 JVM（Java 虚拟机）已经走过了一段漫长的道路。Java 1.0 引入了最基本的面向对象编程概念，而随后的版本则不断扩展其功能，包括引入了泛型、注解、枚举类型等。JDK 5 和 JDK 8 是两个特别重要的版本，它们分别引入了自动装箱/拆箱和 Lambda 表达式，极大地简化了 Java 代码的编写。&lt;/p&gt;
&lt;h3 id="jdk-8-至-21-的重要性"&gt;&lt;a href="#jdk-8-%e8%87%b3-21-%e7%9a%84%e9%87%8d%e8%a6%81%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;JDK 8 至 21 的重要性
&lt;/h3&gt;&lt;p&gt;JDK 8 是一个转折点，它标志着 Java 进入了一个新的时代。这个版本引入了函数式编程的特性，使得 Java 开发者能够以更加声明式的方式编写并发和事件驱动的代码。随后的版本，如 JDK 11、JDK 14 和 JDK 16，都在不断地扩展和深化这些特性，同时引入了许多其他的改进，如模块系统、记录类和模式匹配等。JDK 21 继续这一趋势，带来了更多的语言和 API 改进，以及对 JVM 的优化。&lt;/p&gt;
&lt;h3 id="本文的目的和结构"&gt;&lt;a href="#%e6%9c%ac%e6%96%87%e7%9a%84%e7%9b%ae%e7%9a%84%e5%92%8c%e7%bb%93%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;本文的目的和结构
&lt;/h3&gt;&lt;p&gt;本文旨在为 Java 开发者提供一个全面的概览，介绍自 JDK 8 至 JDK 21 期间引入的所有重要特性。我们将按照特性的类型和用途进行分类，包括新语言特性、新 APIs、性能改进、安全增强、启动和打包工具的更新、Javadoc 和字节码的变更，以及新支持的平台和版本方案。此外，我们还将讨论那些已被弃用或移除的特性，以及这些变化对 Java 生态系统的长期影响。&lt;/p&gt;
&lt;p&gt;通过本文，您将能够获得对 Java 最新特性的深入理解，无论您是 Java 新手还是经验丰富的开发者，都能从中获得宝贵的知识和见解。接下来，让我们开始探索 Java 自 JDK 8 以来的演变之旅。&lt;/p&gt;
&lt;h2 id="新语言特性详细解析"&gt;&lt;a href="#%e6%96%b0%e8%af%ad%e8%a8%80%e7%89%b9%e6%80%a7%e8%af%a6%e7%bb%86%e8%a7%a3%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;新语言特性详细解析
&lt;/h2&gt;&lt;h3 id="模式匹配pattern-matching"&gt;&lt;a href="#%e6%a8%a1%e5%bc%8f%e5%8c%b9%e9%85%8dpattern-matching" class="header-anchor"&gt;&lt;/a&gt;模式匹配（Pattern Matching）
&lt;/h3&gt;&lt;p&gt;模式匹配是 Java 语言中一项革命性的新特性，它首次作为预览特性在 JDK 12 中引入，并在 JDK 16 中正式成为 Java 语言的一部分。这一特性借鉴了函数式编程语言中的模式匹配概念，允许开发者以一种更加简洁和表达性强的方式来检查和处理不同类型的数据。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模式匹配的引入背景&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在模式匹配出现之前，Java 开发者通常使用&lt;code&gt;if-else&lt;/code&gt;语句或者&lt;code&gt;instanceof&lt;/code&gt;检查来处理不同类型的对象，这不仅使得代码变得冗长，而且可读性也较差。模式匹配的引入，为 Java 提供了一种新的、更加直观的方式来处理这些情况，特别是在处理复杂的对象结构时，它能够显著提高代码的清晰度和维护性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模式匹配的具体用法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;模式匹配在 Java 中的实现主要通过&lt;code&gt;instanceof&lt;/code&gt;和&lt;code&gt;switch&lt;/code&gt;表达式来完成。下面是一个使用模式匹配的简单例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getSomeObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;instanceof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 在这里可以直接使用变量 s，无需进行显式的类型转换&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;String length is &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;instanceof&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 对于整型对象 i，可以直接进行数学运算&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Integer value is &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unknown object type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上面的代码中，&lt;code&gt;instanceof&lt;/code&gt;关键字后面紧跟着的是一个模式变量&lt;code&gt;s&lt;/code&gt;或&lt;code&gt;i&lt;/code&gt;，当&lt;code&gt;obj&lt;/code&gt;的类型与模式匹配时，变量会被自动赋值，无需显式的类型转换。这种写法不仅简化了代码，还减少了出错的可能性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模式匹配的优势和实例分析&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;模式匹配的优势在于它的简洁性和表达性。它允许开发者用更少的代码来表达更复杂的意思，并且使得代码的意图更加明确。此外，模式匹配还支持更复杂的结构，如嵌套的模式和守卫条件，这在处理复杂的数据结构时非常有用。&lt;/p&gt;
&lt;p&gt;让我们来看一个更复杂的例子，其中使用了守卫条件和嵌套模式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getNumbers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;absNumber&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unsupported number type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;absNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们使用了一个&lt;code&gt;switch&lt;/code&gt;表达式来进行模式匹配。对于每种情况，我们都定义了一个模式，并在需要时使用守卫条件来进一步细化匹配的规则。这样，我们就能够根据不同的输入执行不同的操作，并且代码的结构依然保持清晰和简洁。&lt;/p&gt;
&lt;p&gt;总的来说，模式匹配为 Java 带来了一种新的、强大的数据处理方式，它不仅提高了代码的可读性和可维护性，而且还使得 Java 语言更加现代化，更接近于其他流行的函数式编程语言。随着 Java 语言的不断发展，我们可以期待模式匹配在未来的 Java 版本中将发挥更加重要的作用。&lt;/p&gt;
&lt;h3 id="未命名变量和未命名模式"&gt;&lt;a href="#%e6%9c%aa%e5%91%bd%e5%90%8d%e5%8f%98%e9%87%8f%e5%92%8c%e6%9c%aa%e5%91%bd%e5%90%8d%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;未命名变量和未命名模式
&lt;/h3&gt;&lt;p&gt;在 Java 14 中，作为预览特性引入的未命名变量（也称为“var”类型）和未命名模式（也称为“模式变量”），为 Java 编程带来了新的表达性和灵活性。这些特性旨在简化代码，特别是在处理复杂的数据结构和流操作时，它们允许开发者忽略不需要的值，并提供了一种新的数据解构方式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;未命名变量的概念&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;未命名变量是 Java 中一种新的局部变量声明方式，它允许开发者声明一个变量而不需要预先指定其类型。这种变量的类型将由编译器根据赋值表达式自动推断。未命名变量通常与&lt;code&gt;-&amp;gt;&lt;/code&gt;操作符一起使用，后者在&lt;code&gt;switch&lt;/code&gt;表达式中用于提供更复杂的逻辑分支。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;未命名变量的使用场景&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;未命名变量特别适用于以下场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当你只关心一个表达式的结果，而不打算在后续代码中使用变量时。&lt;/li&gt;
&lt;li&gt;当你需要从方法返回值中提取信息，但又不想显式声明所有组成部分时。&lt;/li&gt;
&lt;li&gt;在流操作中，当你需要处理流中的元素，但不需要存储任何中间变量时。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;未命名模式的概念&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;未命名模式是模式匹配的一个扩展，它允许开发者在&lt;code&gt;instanceof&lt;/code&gt;、&lt;code&gt;case&lt;/code&gt;标签或&lt;code&gt;catch&lt;/code&gt;块中声明一个模式变量，而不是一个具名变量。这在使用模式匹配解构复杂类型时非常有用，尤其是当你只需要访问类型的一部分数据时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;未命名模式的使用场景&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;未命名模式特别适用于以下场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当你使用模式匹配来检查对象的类型，但不需要访问对象的具体实例时。&lt;/li&gt;
&lt;li&gt;在解构复杂对象时，当你只需要对象的某些部分，而不是整个对象时。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;代码示例和实际应用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;下面是一个使用未命名变量和未命名模式的示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 使用未命名变量处理 Optional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;optStr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;optStr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Default&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 输出 &amp;#34;Hello&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 使用未命名变量在 try-catch 中忽略不需要的异常&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 可能会抛出 CheckedException 的代码&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 忽略异常，不进行处理&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 使用未命名模式在 switch 表达式中解构对象&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 只关心 x 的值，y 的值被忽略&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;X coordinate is &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述代码中，我们看到了未命名变量和未命名模式如何在不同场景下简化代码。未命名变量使得我们可以避免声明不必要的变量，而未命名模式则让我们能够更加灵活地处理复杂的数据结构。&lt;/p&gt;
&lt;p&gt;总的来说，未命名变量和未命名模式是 Java 语言中两项非常有用的新特性，它们通过减少代码冗余和提高表达性，使得 Java 代码更加简洁和易于理解。随着这些特性在未来的 Java 版本中逐渐成熟和稳定，我们可以预见它们将在 Java 编程实践中发挥越来越重要的作用。&lt;/p&gt;
&lt;h3 id="封闭类和记录类"&gt;&lt;a href="#%e5%b0%81%e9%97%ad%e7%b1%bb%e5%92%8c%e8%ae%b0%e5%bd%95%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;封闭类和记录类
&lt;/h3&gt;&lt;p&gt;随着 Java 语言不断发展，为了更好地支持函数式编程和数据建模，JDK 16 引入了两种新的类类型：封闭类（Sealed Classes）和记录类（Record Classes）。这些新特性旨在提供更丰富的类型安全保障和更简洁的代码表达，特别是在创建数据传输对象（DTOs）和限制继承结构时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;封闭类&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;封闭类是一种特殊的类，它限制了哪些其他类可以继承它。这一特性对于那些希望限制子类数量或者想要精确控制继承树的开发者来说非常有用。在 Java 中，封闭类通过&lt;code&gt;sealed&lt;/code&gt;关键字进行声明，并且可以指定一个允许的子类列表。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;封闭类的概念&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;封闭类的概念是为了在 Java 中引入更多的类型安全性和清晰性。它们允许开发者定义一个基类，同时限制哪些类可以扩展这个基类。这样做的好处是可以防止其他开发者创建不必要的或者不安全的子类，从而保护 API 的稳定性和可预测性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;封闭类的使用场景&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;封闭类适用于以下场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当你想要创建一个基类，但只想允许特定的子类时。&lt;/li&gt;
&lt;li&gt;当你想要限制类的继承结构，以避免类的滥用或错误扩展时。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;封闭类示例&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;public&lt;/span&gt; &lt;span class="n"&gt;abstract&lt;/span&gt; &lt;span class="n"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="ne"&gt;Shape&lt;/span&gt; &lt;span class="n"&gt;permits&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Triangle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="err"&gt;封闭类的基类代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Circle&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="ne"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="err"&gt;圆形的具体实现&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Rectangle&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="ne"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="err"&gt;矩形的具体实现&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="err"&gt;以下代码将无法编译，因为&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt; &lt;span class="err"&gt;没有在&lt;/span&gt; &lt;span class="ne"&gt;Shape&lt;/span&gt; &lt;span class="err"&gt;的&lt;/span&gt; &lt;span class="n"&gt;permits&lt;/span&gt; &lt;span class="err"&gt;子句中声明&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="ne"&gt;Shape&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="err"&gt;正方形的具体实现&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，&lt;code&gt;Shape&lt;/code&gt;是一个封闭类，它明确指定了哪些类可以作为其子类。这确保了&lt;code&gt;Shape&lt;/code&gt;的继承结构是受控的，并且防止了任何未授权的扩展。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;记录类&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;记录类是一种特殊的类，它主要用于创建不可变的数据传输对象（DTOs）。记录类的语法比传统的类更加简洁，它自动为所有字段生成构造函数、&lt;code&gt;equals&lt;/code&gt;、&lt;code&gt;hashCode&lt;/code&gt;和&lt;code&gt;toString&lt;/code&gt;方法。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;记录类的概念&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;记录类的概念是为了简化 Java 中不可变数据结构的创建。它们提供了一种快速定义类的方式，而无需编写大量的样板代码。记录类是不可变的，这意味着一旦创建，其状态就不能改变，这有助于避免并发问题和不必要的错误。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;记录类的使用场景&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;记录类适用于以下场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当你需要创建一个简单的数据结构，用于存储一组固定的值时。&lt;/li&gt;
&lt;li&gt;当你希望确保数据的不可变性，以提高代码的安全性和可维护性时。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;记录类示例&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 输出 &amp;#34;Point[x=10, y=20]&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们定义了一个名为&lt;code&gt;Point&lt;/code&gt;的记录类，它有两个字段：&lt;code&gt;x&lt;/code&gt;和&lt;code&gt;y&lt;/code&gt;。创建记录类时，Java 自动为我们生成了构造函数和&lt;code&gt;toString&lt;/code&gt;方法，使得代码非常简洁。此外，由于记录类是不可变的，我们可以安全地在多线程环境中共享&lt;code&gt;Point&lt;/code&gt;实例，而不必担心它们的内部状态会被改变。&lt;/p&gt;
&lt;p&gt;总的来说，封闭类和记录类为 Java 开发者提供了更多的选择，以适应不同的编程场景。封闭类通过限制继承结构来增强类型安全性，而记录类则通过简化数据结构的创建来提高开发效率。这些新特性的引入，进一步丰富了 Java 语言的功能，使其更加现代化和高效。随着这些特性在未来的 Java 版本中得到进一步的发展和完善，我们有理由相信它们将成为 Java 编程中不可或缺的一部分。&lt;/p&gt;
&lt;h3 id="其他重要语言特性"&gt;&lt;a href="#%e5%85%b6%e4%bb%96%e9%87%8d%e8%a6%81%e8%af%ad%e8%a8%80%e7%89%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;其他重要语言特性
&lt;/h3&gt;&lt;p&gt;除了前面提到的模式匹配、未命名变量和未命名模式等特性，Java 在 JDK 8 至 21 的版本中还引入了许多其他重要的语言特性。这些特性涵盖了从代码简化到性能优化的各个方面，极大地提升了 Java 编程的便捷性和表达能力。接下来，我们将详细探讨其中的一些关键特性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;String 模板&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 16 引入了字符串模板（String Templates），这是一种新的字符串字面量，它允许开发者以一种更加简洁和安全的方式来创建格式化的字符串。字符串模板通过&lt;code&gt;str&lt;/code&gt;前缀定义，并支持多行字符串和插值表达式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字符串模板的用法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;字符串模板提供了一种新的字符串创建方式，它结合了字符串字面量的简洁性和&lt;code&gt;String.format&lt;/code&gt;方法的格式化能力。开发者可以在模板中直接插入变量和表达式，而不需要额外的格式化步骤。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;World&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 使用字符串模板创建格式化的字符串&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 输出 &amp;#34;Hello, World! The value is 84.&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们使用&lt;code&gt;${}&lt;/code&gt;来插入变量和表达式的值，这使得字符串的创建和格式化过程变得更加直观和方便。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;字符串模板的优势&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;字符串模板的主要优势在于它的简洁性和易读性。它减少了字符串拼接和格式化的复杂性，使得代码更加清晰和易于维护。此外，由于字符串模板是编译时常量，它们在某些情况下还能提供更好的性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;文本块&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文本块（Text Blocks）是 Java 13 中引入的预览特性，它允许开发者以多行字符串的形式编写代码，而无需使用传统的转义序列。文本块通过三个双引号（&lt;code&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/code&gt;）定义，并且保持了字符串字面量的原始格式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;文本块的用法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文本块特别适用于创建多行的字符串，例如在编写正则表达式、HTML 模板或 SQL 查询时。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;lt;html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;lt;h1&amp;gt;Title&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;lt;p&amp;gt;Paragraph with line breaks
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; and multiple lines.&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们定义了一个包含 HTML 内容的文本块，所有的换行符和空格都被保留，而不需要使用传统的&lt;code&gt;\n&lt;/code&gt;转义序列。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;文本块的优势&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文本块的主要优势在于它们的易读性和编写效率。它们使得多行字符串的创建变得非常简单，同时避免了转义序列带来的混乱和错误。此外，文本块还支持跨多行的字符串连接，进一步提高了代码的灵活性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Helpful NullPointerExceptions&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 14 中引入了更加有用的&lt;code&gt;NullPointerException&lt;/code&gt;，它提供了关于哪个变量为&lt;code&gt;null&lt;/code&gt;的详细信息。这一特性通过&lt;code&gt;-XX:+ShowCodeDetailsInExceptionMessages&lt;/code&gt; JVM 选项启用，它使得空指针异常更加易于调试。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Helpful NullPointerExceptions 的用法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当程序抛出&lt;code&gt;NullPointerException&lt;/code&gt;时，JVM 会提供更多的堆栈跟踪信息，包括导致异常的变量名和代码位置。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;String message = null;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;int length = message.length(); // 这里会抛出 NullPointerException
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，如果启用了详细异常信息，我们将会得到类似于以下的输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Exception in thread &amp;#34;main&amp;#34; java.lang.NullPointerException:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; Cannot read field &amp;#34;length&amp;#34; because &amp;#34;message&amp;#34; is null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; at com.example.Main.main(Main.java:10)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Helpful NullPointerExceptions 的优势&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这一特性的主要优势在于它提供了更多的调试信息，使得开发者能够快速定位和修复空指针异常。这种增强的异常信息极大地提高了 Java 程序的可维护性和稳定性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Switch 表达式&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Switch 表达式是 Java 12 中引入的预览特性，它为&lt;code&gt;switch&lt;/code&gt;语句提供了一种更加简洁和灵活的替代方案。Switch 表达式使用&lt;code&gt;yield&lt;/code&gt;关键字返回值，并且可以与模式匹配结合使用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Switch 表达式的用法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Switch 表达式可以替代传统的&lt;code&gt;switch&lt;/code&gt;语句，特别是在处理枚举类型和表达式时。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Day&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MONDAY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numLetters&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MONDAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FRIDAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SUNDAY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TUESDAY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;yield&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numLetters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 输出 &amp;#34;5&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们使用了一个&lt;code&gt;switch&lt;/code&gt;表达式来根据&lt;code&gt;day&lt;/code&gt;的值计算字母的数量。与传统的&lt;code&gt;switch&lt;/code&gt;语句相比，Switch 表达式提供了一种更加简洁和函数式的方法来处理条件逻辑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Switch 表达式的优势&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Switch 表达式的主要优势在于它的简洁性和表达性。它减少了模板代码的数量，并且使得条件逻辑的编写更加直观和易于理解。此外，Switch 表达式还支持与模式匹配的结合使用，进一步提高了代码的灵活性和可读性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 语言在 JDK 8 至 21 的版本中引入了许多重要的新特性，这些特性不仅提高了代码的编写效率，还增强了程序的性能和安全性。从字符串模板和文本块的引入，到 Helpful NullPointerExceptions 和 Switch 表达式的改进，Java 不断地在进化，以满足现代软件开发的需求。随着这些特性在未来的 Java 版本中得到进一步的发展和完善，我们有理由相信它们将成为 Java 编程中不可或缺的一部分。&lt;/p&gt;
&lt;h2 id="新-apis-的探索"&gt;&lt;a href="#%e6%96%b0-apis-%e7%9a%84%e6%8e%a2%e7%b4%a2" class="header-anchor"&gt;&lt;/a&gt;新 APIs 的探索
&lt;/h2&gt;&lt;p&gt;Java 平台的不断更新带来了丰富的新 APIs，这些 APIs 旨在提高开发者的生产力，简化复杂任务的处理，并增强 Java 在各个领域的应用能力。从集合操作的增强到数学函数的扩展，新 APIs 为 Java 开发者提供了更多的工具来构建高效、健壮的应用程序。&lt;/p&gt;
&lt;h3 id="集合和数学函数-api"&gt;&lt;a href="#%e9%9b%86%e5%90%88%e5%92%8c%e6%95%b0%e5%ad%a6%e5%87%bd%e6%95%b0-api" class="header-anchor"&gt;&lt;/a&gt;集合和数学函数 API
&lt;/h3&gt;&lt;p&gt;集合 API 是 Java 中使用最广泛的 API 之一，它提供了一系列的数据结构和算法来存储和操作对象集合。数学函数 API 则为数值计算提供了支持，包括随机数生成、复数操作等。在 JDK 8 至 21 的更新中，这两个领域的 API 得到了显著的扩展和改进。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新增集合 API 的特性和用法&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 JDK 8 中引入的 Stream API 彻底改变了 Java 中集合的处理方式，而在后续的版本中，这一 API 继续得到了增强。例如，JDK 16 引入了&lt;code&gt;toList()&lt;/code&gt;方法，它简化了从 Stream 到 List 的转换过程，使得开发者可以更方便地进行数据收集。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;List&amp;lt;String&amp;gt; list = Stream.of(&amp;#34;apple&amp;#34;, &amp;#34;banana&amp;#34;, &amp;#34;cherry&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; .collect(Collectors.toList());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此外，JDK 16 还引入了&lt;code&gt;Map.of&lt;/code&gt;和&lt;code&gt;Set.of&lt;/code&gt;等静态工厂方法，它们用于创建不可变的 Map 和 Set 实例，这不仅提高了代码的可读性，还减少了创建空集合时的内存消耗。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;数学函数 API 的增强&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 8 引入了&lt;code&gt;java.util.function&lt;/code&gt;包，其中包括了一系列的函数式接口，为 Java 带来了函数式编程的特性。在此基础上，后续的 Java 版本继续增强数学函数 API。例如，JDK 11 中引入了&lt;code&gt;Math.log10&lt;/code&gt;方法，用于计算一个数的以 10 为底的对数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;double result = Math.log10(100); // 结果为 2.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JDK 16 进一步扩展了数学函数库，包括对&lt;code&gt;BigDecimal&lt;/code&gt;的改进，使得大数运算更加精确和高效。此外，JDK 16 还引入了&lt;code&gt;Random&lt;/code&gt;和&lt;code&gt;ThreadLocalRandom&lt;/code&gt;的新的 APIs，提供了更多的随机数生成选项。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实际应用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;新的集合和数学函数 API 在实际应用中极大地提高了开发效率。例如，在处理大量数据时，Stream API 的链式操作和新的集合工厂方法使得代码更加简洁和易于理解。在科学计算和金融分析领域，增强的数学函数 API 提供了更精确的数值计算能力，帮助开发者实现了更复杂的数学模型。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;集合和数学函数 API 的持续更新和改进，反映了 Java 平台对开发者需求的响应和对现代编程挑战的适应。这些新 APIs 不仅提高了 Java 语言的表达能力，还为解决复杂问题提供了更多的工具和选项。随着 Java 平台的不断发展，我们可以期待未来会有更多的创新和改进，进一步丰富 Java 生态系统。&lt;/p&gt;
&lt;h3 id="其他重要-api-更新"&gt;&lt;a href="#%e5%85%b6%e4%bb%96%e9%87%8d%e8%a6%81-api-%e6%9b%b4%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;其他重要 API 更新
&lt;/h3&gt;&lt;p&gt;随着 Java 平台的不断演进，除了集合和数学函数 API 之外，还有许多其他的 API 得到了更新和增强，以满足现代应用程序的需求。这些更新涵盖了文件和 IO 操作、网络编程、时间日期处理等多个方面，为 Java 开发者提供了更加强大和灵活的工具集。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;文件和 IO 操作的改进&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 的文件和 IO 操作 API 在 JDK 8 至 21 期间经历了显著的改进。例如，JDK 11 引入了&lt;code&gt;Files.walk&lt;/code&gt;方法，它提供了一种更加简洁的方式来遍历文件树。此外，JDK 14 增加了&lt;code&gt;Files.mismatch&lt;/code&gt;方法，用于比较两个文件的内容差异，这对于文件校验和数据恢复等场景非常有用。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;startPath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/path/to/start/directory&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startPath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Files&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;isRegularFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;网络编程的新特性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在网络编程方面，JDK 9 引入了&lt;code&gt;java.net.http.HttpClient&lt;/code&gt;，这是一个全新的、非阻塞的 HTTP 客户端 API，它提供了更加简洁和强大的 HTTP 请求处理能力。这个新的 HTTP 客户端支持 HTTP/2 协议，并且提供了 WebSocket 支持，使得 Java 在处理现代网络应用时更加高效。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;HttpClient client = HttpClient.newBuilder()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; .version(HttpClient.Version.HTTP_2)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; .build();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;HttpRequest request = HttpRequest.newBuilder()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; .uri(URI.create(&amp;#34;https://example.com&amp;#34;))
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; .GET()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; .build();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; .thenApply(HttpResponse::body)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; .thenAccept(System.out::println)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; .join();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;时间和日期 API 的更新&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 8 引入了&lt;code&gt;java.time&lt;/code&gt;包，这个包提供了一套全新的日期和时间 API，它解决了旧版&lt;code&gt;java.util.Date&lt;/code&gt;类中存在的问题，并提供了更好的时间日期处理能力。在后续的版本中，这个 API 继续得到了增强。例如，JDK 12 增加了&lt;code&gt;LocalDate&lt;/code&gt;的&lt;code&gt;ofEpochDay&lt;/code&gt;方法，它允许开发者直接从天数创建日期对象，而不需要通过&lt;code&gt;ChronoLocalDate&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;LocalDate date = LocalDate.ofEpochDay(10000); // 创建一个特定的日期
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;结论&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这些 API 的更新和增强不仅提高了 Java 语言的功能，还使得 Java 在处理文件操作、网络通信和时间日期处理等任务时更加高效和便捷。随着 Java 平台的不断发展，我们可以期待未来会有更多的创新和改进，进一步丰富 Java 生态系统，帮助开发者构建更加健壮和高效的应用程序。&lt;/p&gt;
&lt;h2 id="性能改进的深入分析"&gt;&lt;a href="#%e6%80%a7%e8%83%bd%e6%94%b9%e8%bf%9b%e7%9a%84%e6%b7%b1%e5%85%a5%e5%88%86%e6%9e%90" class="header-anchor"&gt;&lt;/a&gt;性能改进的深入分析
&lt;/h2&gt;&lt;p&gt;Java 平台的性能改进一直是开发者社区关注的焦点。从 JDK 8 至 21，Oracle 和 OpenJDK 社区持续致力于优化 Java 虚拟机（JVM）和 Java 语言的性能，以满足日益增长的应用程序性能需求。这些改进包括内存管理、垃圾回收、即时编译器（JIT）优化、启动时间缩短等方面。&lt;/p&gt;
&lt;h3 id="内存管理和垃圾回收"&gt;&lt;a href="#%e5%86%85%e5%ad%98%e7%ae%a1%e7%90%86%e5%92%8c%e5%9e%83%e5%9c%be%e5%9b%9e%e6%94%b6" class="header-anchor"&gt;&lt;/a&gt;内存管理和垃圾回收
&lt;/h3&gt;&lt;p&gt;内存管理和垃圾回收是 Java 性能改进中的关键领域。有效的内存管理确保了应用程序能够高效地使用内存资源，而垃圾回收机制则负责回收不再使用的对象，防止内存泄漏。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;弹性元空间&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 JDK 8 中，元空间（Metaspace）被引入作为&lt;code&gt;PermGen&lt;/code&gt;（永久代）的替代品，用于存储类的元数据。与&lt;code&gt;PermGen&lt;/code&gt;不同，元空间在本地内存中分配，理论上不受堆大小的限制。然而，随着应用程序规模的增长，元空间的内存使用也成为一个关注点。为了解决这一问题，JDK 16 引入了弹性元空间，它允许元空间的内存使用更加动态和可控，减少了对系统内存的占用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;G1 垃圾回收器&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;G1（Garbage-First）垃圾回收器自 JDK 9 起成为默认的垃圾回收器。G1 是一种并行、增量、并发的垃圾回收器，旨在提供可预测的停顿时间模型，同时保持高吞吐量。G1 通过将堆划分为多个区域（Region）并跟踪每个区域的垃圾回收优先级来工作。它定期执行小型的回收任务，以减少应用程序的停顿时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ZGC 和 Shenandoah 垃圾回收器&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ZGC（Z Garbage Collector）和 Shenandoah 垃圾回收器是两个实验性的垃圾回收器，它们在 JDK 11 及后续版本中作为实验特性提供。这两种回收器都旨在为大型堆（多达 4TB）提供低延迟的垃圾回收。ZGC 通过染色指针技术和并发标记周期来实现低延迟，而 Shenandoah 则通过并发标记和压缩来减少停顿时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NUMA-Aware 内存分配&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;随着多核处理器的普及，非一致性内存访问（NUMA）架构变得越来越常见。JDK 14 引入了 NUMA-Aware 内存分配，它允许 JVM 根据处理器的 NUMA 拓扑结构来优化内存分配，从而提高内存访问效率和整体性能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;并行 Full GC 的改进&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JDK 10 中引入了并行 Full GC，它通过在 Full GC 过程中使用多个 GC 线程来提高垃圾回收的效率。这种改进显著减少了 Full GC 的停顿时间，特别是在处理大型堆或长时间运行的应用程序时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;内存管理和垃圾回收的性能改进对于 Java 应用程序的稳定性和响应性至关重要。通过引入新的垃圾回收器、优化内存分配策略和提供更灵活的垃圾回收选项，Java 平台能够更好地适应不同类型和规模的应用程序。随着 Java 技术的不断发展，我们可以期待未来会有更多的内存管理和垃圾回收方面的创新，以满足日益增长的性能需求。&lt;/p&gt;
&lt;h3 id="jit-编译器和运行时性能"&gt;&lt;a href="#jit-%e7%bc%96%e8%af%91%e5%99%a8%e5%92%8c%e8%bf%90%e8%a1%8c%e6%97%b6%e6%80%a7%e8%83%bd" class="header-anchor"&gt;&lt;/a&gt;JIT 编译器和运行时性能
&lt;/h3&gt;&lt;p&gt;Java 虚拟机（JVM）中的即时编译器（JIT）是 Java 性能优化的关键组件。JIT 编译器负责将字节码动态编译为本地机器码，以提高执行效率。随着 JDK 版本的迭代，JIT 编译器也在不断进化，引入了新的优化技术和特性，以提升运行时性能和编译效率。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JIT 编译器的优化&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JIT 编译器的优化主要集中在减少编译时间和提高编译代码的质量上。这些优化包括更智能的编译触发策略、改进的热点检测算法、以及针对性能瓶颈的特定优化。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;分层编译&lt;/strong&gt;：JDK 11 引入了分层编译的概念，它允许 JVM 在不同的编译层次之间进行选择，以平衡编译时间和运行时性能。例如，JVM 可以使用快速的低层次编译器来编译不经常执行的代码，而将更高层次的优化编译器用于热点代码。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;编译器探测&lt;/strong&gt;：JDK 12 及后续版本中，编译器探测（也称为编译器引导）被引入，它允许 JVM 在运行时收集关于代码执行的更多信息，并使用这些信息来指导编译优化决策。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;代码缓存和重用&lt;/strong&gt;：JVM 通过缓存编译后的代码来避免重复编译相同的代码，这在处理具有多个版本的应用程序时尤其有用。此外，JVM 还可以在类加载器之间重用编译后的代码，减少了编译开销。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;运行时性能监控和诊断工具&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了帮助开发者更好地理解和优化 Java 应用程序的性能，JVM 提供了一系列的运行时监控和诊断工具。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Java Mission Control（JMC）&lt;/strong&gt;：JMC 是一个强大的性能分析工具，它可以收集和分析 JVM 的运行时信息，包括线程状态、内存使用情况和垃圾回收日志。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flight Recorder&lt;/strong&gt;：Flight Recorder 是 JDK 11 中引入的一个轻量级事件记录框架，它可以在后台记录 JVM 的详细运行时事件，而对性能的影响非常小。开发者可以在需要时启动详细的事件记录，以进行性能分析。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JVM 统计信息 API&lt;/strong&gt;：JVM 统计信息 API（JVMS）提供了一组接口，允许应用程序查询 JVM 的运行时统计信息，如类加载次数、垃圾回收次数等。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;启动时间缩短&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;启动时间是 Java 应用程序性能的一个重要方面，特别是在需要快速响应的场景中。从 JDK 9 开始，Oracle 致力于减少 JVM 的启动时间，通过优化类加载和初始化过程，以及减少启动时的 JVM 内部处理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;应用类数据共享（Application Class-Data Sharing, ACDS）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ACDS 是 JDK 11 中引入的一个特性，它允许 JVM 在多个 Java 进程之间共享已编译的类数据。通过这种方式，JVM 可以减少启动时的编译工作量，从而缩短启动时间。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;快速应用启动（Fast Application Startup, FAS）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FAS 是 JDK 11 中的一个项目，旨在通过减少类元数据的加载和优化 JVM 的内存布局来加快应用启动速度。虽然 FAS 项目的一些目标在 JDK 11 中并未完全实现，但它为后续版本的启动时间优化奠定了基础。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JIT 编译器和运行时性能的优化是 Java 平台持续进步的重要组成部分。通过引入新的编译策略、监控工具和启动时间缩短技术，Java 能够为开发者提供更加高效和稳定的运行时环境。随着 Java 技术的不断发展，我们可以期待未来会有更多的性能优化特性，以满足日益增长的性能需求和挑战。&lt;/p&gt;
&lt;h2 id="安全改进的全面审视"&gt;&lt;a href="#%e5%ae%89%e5%85%a8%e6%94%b9%e8%bf%9b%e7%9a%84%e5%85%a8%e9%9d%a2%e5%ae%a1%e8%a7%86" class="header-anchor"&gt;&lt;/a&gt;安全改进的全面审视
&lt;/h2&gt;&lt;p&gt;Java 平台一直致力于提供安全可靠的编程环境，以保护用户和企业的数据安全。从 JDK 8 至 21，Java 的安全模型经历了一系列的改进，旨在提高安全性，防范新出现的威胁，并保持与现代安全标准和实践的一致性。&lt;/p&gt;
&lt;h3 id="加密和认证-api"&gt;&lt;a href="#%e5%8a%a0%e5%af%86%e5%92%8c%e8%ae%a4%e8%af%81-api" class="header-anchor"&gt;&lt;/a&gt;加密和认证 API
&lt;/h3&gt;&lt;p&gt;加密和认证 API 是 Java 安全体系中的重要组成部分，它们为 Java 应用程序提供了数据加密、解密、签名和验证等安全操作的能力。随着技术的发展，Java 对这些 API 进行了更新和增强，以支持新的加密算法和满足更高的安全标准。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新的加密算法和 API&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AES-GCM&lt;/strong&gt;：JDK 11 中引入了对 AES-GCM（Galois/Counter Mode）的支持，这是一种用于块加密算法的高效和安全的模式，特别适合处理网络通信中的数据加密和认证。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TLS 1.3&lt;/strong&gt;：Java 11 开始支持 TLS 1.3，这是传输层安全协议的最新版本，提供了更强大的加密算法、更少的握手轮次和更好的隐私保护。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SHA-3&lt;/strong&gt;：随着安全需求的不断演进，JDK 9 引入了对 SHA-3（Secure Hash Algorithm 3）的支持，这是新一代的哈希函数，旨在替代原有的 SHA-2。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;密钥管理和证书处理&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;密钥封装机制（KEM）API&lt;/strong&gt;：JDK 21 中引入了密钥封装机制（Key Encapsulation Mechanisms）API，它提供了一种封装和解封装密钥的方法，这对于密钥交换和密钥管理非常重要。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;证书透明度（Certificate Transparency, CT）&lt;/strong&gt;：Java 11 开始支持证书透明度 API，这是一种公开的、可审计的证书日志系统，用于监控和验证 SSL/TLS 证书的颁发。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;安全随机数生成器&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DRBG（Deterministic Random Bit Generator）&lt;/strong&gt;：Java 11 中引入了基于 NIST SP 800-90A 标准的确定性随机比特生成器（DRBG），它提供了可预测的、高质量的随机数，适用于安全敏感的应用场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;弃用和移除不安全的 API&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不安全的加密算法&lt;/strong&gt;：随着安全意识的提高，Java 社区逐渐弃用了一些被认为是不安全的加密算法，如 DES 和 SHA-1，并推荐使用更安全的替代品。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不安全的 SSL/TLS 协议&lt;/strong&gt;：Java 11 开始弃用 SSL 和早期版本的 TLS 协议，鼓励开发者使用 TLS 1.3 或更高版本，以确保通信的安全性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;加密和认证 API 的更新和增强是 Java 安全改进中的重要一环。通过引入新的加密算法、改进密钥管理和证书处理，以及提供更强大的随机数生成器，Java 平台能够更好地保护用户的数据安全和隐私。同时，通过弃用和移除不安全的 API，Java 鼓励开发者采用更加安全和现代的编程实践。随着 Java 技术的不断发展，我们可以期待未来会有更多的安全特性被引入，以应对不断变化的安全威胁和挑战。&lt;/p&gt;
&lt;h3 id="安全管理器和权限控制"&gt;&lt;a href="#%e5%ae%89%e5%85%a8%e7%ae%a1%e7%90%86%e5%99%a8%e5%92%8c%e6%9d%83%e9%99%90%e6%8e%a7%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;安全管理器和权限控制
&lt;/h3&gt;&lt;p&gt;在 Java 安全模型中，安全管理器（Security Manager）和权限控制（Access Control）扮演着至关重要的角色。它们确保了 Java 应用程序在一个受控的环境中运行，防止恶意代码访问敏感资源或执行危险操作。随着 Java 版本的更新，这些安全机制也在不断地得到加强和优化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;安全管理器&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;安全管理器是 Java 安全架构的核心组件，它负责执行安全策略，控制对系统资源的访问。通过安全管理器，Java 平台能够实施一系列的安全限制，如文件系统访问、网络连接和加密算法的使用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;策略文件和权限&lt;/strong&gt;：安全管理器通常与策略文件（如&lt;code&gt;java.security&lt;/code&gt;）一起工作，这些文件定义了代码可以请求的权限。开发者可以根据需要自定义策略文件，以放宽或限制特定的权限。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安全管理器的配置&lt;/strong&gt;：在 JDK 9 中，安全管理器的配置方式发生了变化。&lt;code&gt;java.security&lt;/code&gt;文件不再位于 JRE 的&lt;code&gt;lib/security&lt;/code&gt;目录下，而是被&lt;code&gt;jdk.security&lt;/code&gt;文件所取代，这使得安全管理器的配置更加模块化和灵活。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;权限控制&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 的权限控制机制允许开发者精确地控制代码的访问权限。这些权限可以是文件系统访问、网络操作、安全属性修改等。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;java.security.Permissions&lt;/strong&gt;：这是一个用于定义和管理权限的类。开发者可以通过创建&lt;code&gt;Permissions&lt;/code&gt;对象并将其传递给&lt;code&gt;SecurityManager&lt;/code&gt;来设置应用程序的权限集。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;java.security.CodeSource&lt;/strong&gt;：这个类用于表示代码的来源，包括 URL 和证书信息。它与权限控制紧密相关，因为安全管理器可以根据代码来源来授予或拒绝特定的权限。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;沙箱和 Applet API 的弃用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Applet API 在 JDK 11 中被标记为弃用，并在 JDK 14 中被完全移除。Applet 提供了在浏览器中运行 Java 程序的能力，但由于安全问题和现代 Web 技术的发展，它已经不再被推荐使用。随着 Applet API 的弃用，Java 的沙箱模型也得到了重新评估，以确保 Java 应用程序的安全性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;加强的 JVM 安全特性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JVM 安全沙箱&lt;/strong&gt;：JVM 本身提供了一个安全沙箱，限制了类加载器和类定义的权限。在 JDK 11 及后续版本中，这个沙箱得到了加强，以防止潜在的安全漏洞。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JVM TI（Tool Interface）&lt;/strong&gt;：JVM TI 提供了一组 API，允许监控和控制运行中的 Java 虚拟机。在 JDK 11 中，对 JVM TI 的访问受到了限制，以减少潜在的安全风险。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;安全管理器和权限控制是 Java 平台安全性的关键组成部分。通过不断更新和改进这些机制，Java 确保了应用程序在一个受控的环境中运行，保护了用户的数据和系统资源。随着 Java 技术的不断发展，我们可以期待未来会有更多的安全特性被引入，以应对不断变化的安全威胁和挑战。开发者应当关注这些更新，确保他们的应用程序遵循最新的安全最佳实践。&lt;/p&gt;
&lt;h3 id="启动和打包的新工具和方法"&gt;&lt;a href="#%e5%90%af%e5%8a%a8%e5%92%8c%e6%89%93%e5%8c%85%e7%9a%84%e6%96%b0%e5%b7%a5%e5%85%b7%e5%92%8c%e6%96%b9%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;启动和打包的新工具和方法
&lt;/h3&gt;&lt;p&gt;随着 Java 生态系统的发展，启动和打包应用程序的方式也在不断演进。为了满足现代应用程序对快速启动和部署的需求，Java 平台引入了一系列新的工具和方法，旨在简化开发流程，提高效率，并支持新的部署模式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;jlink 和 jpackage 工具&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;jlink（Java Linker）和 jpackage 是 Java 平台提供的新工具，它们使得创建定制的运行时映像和打包应用程序变得更加简单和高效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;jlink 工具&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;jlink 工具允许开发者创建一个定制的运行时映像（也称为“链接包”），这个映像包含了运行应用程序所需的最小化的 JVM 组件和应用程序代码。通过这种方式，开发者可以减少运行时的体积，提高启动速度，并减少对系统资源的占用。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;减少运行时体积&lt;/strong&gt;：传统的 JRE（Java 运行时环境）包含了许多不常用的功能和组件，这使得其体积相对较大。使用 jlink，开发者可以选择性地包含必要的模块，从而生成一个更小的运行时映像。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;提高启动速度&lt;/strong&gt;：较小的运行时映像意味着更少的加载时间，从而提高了应用程序的启动速度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定制化部署&lt;/strong&gt;：jlink 提供了高度的定制化能力，开发者可以根据应用程序的特定需求来构建运行时映像，例如，包含特定的语言包或不包含某些不常用的功能。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;jpackage 工具&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;jpackage 工具是 Java 14 中引入的，用于将 Java 应用程序打包为平台特定的安装包。这个工具支持多种操作系统，包括 Windows、macOS 和 Linux，能够生成如 MSI、EXE、PKG、Deb 和 RPM 等格式的安装包。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;一站式打包&lt;/strong&gt;：jpackage 简化了打包流程，开发者只需一个命令就可以生成适用于目标平台的安装包。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;集成式安装&lt;/strong&gt;：生成的安装包可以集成到操作系统的包管理器中，使得应用程序的安装、更新和卸载与操作系统的其他软件一致。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;支持多种平台&lt;/strong&gt;：jpackage 支持跨平台打包，开发者可以使用相同的源代码生成适用于不同操作系统的安装包。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;使用示例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以下是使用 jlink 和 jpackage 工具的基本示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;使用&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jlink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;创建定制的运行时映像&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;jlink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;jdk&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;jmods&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;desktop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;使用&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jpackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;打包应用程序&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;jpackage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;jar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;jar&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在上述示例中，我们首先使用 jlink 创建了一个只包含必要模块的运行时映像，然后使用 jpackage 将应用程序打包为 Windows 平台的 EXE 安装文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;jlink 和 jpackage 工具的引入，为 Java 应用程序的部署提供了更多的灵活性和便捷性。通过这些工具，开发者可以创建定制化的运行时映像和平台特定的安装包，从而满足不同场景下的部署需求。随着 Java 平台的不断发展，我们可以期待未来会有更多的工具和方法来支持现代化的应用程序部署。&lt;/p&gt;
&lt;h3 id="jigsaw-项目和模块系统"&gt;&lt;a href="#jigsaw-%e9%a1%b9%e7%9b%ae%e5%92%8c%e6%a8%a1%e5%9d%97%e7%b3%bb%e7%bb%9f" class="header-anchor"&gt;&lt;/a&gt;Jigsaw 项目和模块系统
&lt;/h3&gt;&lt;p&gt;Jigsaw 项目是 Java 发展史上的一个重要里程碑，它的目标是将 Java 平台模块化，从而提高 Java 应用程序的性能、安全性和可维护性。Jigsaw 项目的核心是引入了 Java 模块系统（也称为 Project Jigsaw），这是 Java 11 中的一个重要特性。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模块化的好处&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;模块化带来了多个好处，包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;更清晰的依赖关系&lt;/strong&gt;：模块系统强制定义了包和模块之间的依赖关系，使得依赖更加明确，减少了类路径冲突的可能性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;更好的封装&lt;/strong&gt;：模块可以隐藏其内部的实现细节，只暴露必要的 API，从而保护了应用程序的核心代码不被外部直接访问。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;更高效的类加载&lt;/strong&gt;：模块系统允许 JVM 有选择地加载和卸载模块，这不仅减少了内存占用，还提高了应用程序的启动速度。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;更灵活的版本管理&lt;/strong&gt;：模块化使得单独的模块可以独立更新和维护，而不会影响到整个应用程序。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;模块系统的基本概念&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java 模块系统基于以下几个基本概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模块&lt;/strong&gt;（Module）：一个模块是一个包含相关类和资源的容器。模块通过&lt;code&gt;module&lt;/code&gt;声明来定义，并在&lt;code&gt;module-info.java&lt;/code&gt;文件中指定。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;依赖&lt;/strong&gt;（Requires）：模块之间通过&lt;code&gt;requires&lt;/code&gt;声明来表达依赖关系。一个模块可以依赖其他模块，并使用这些模块提供的 API。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;导出&lt;/strong&gt;（Exports）：模块可以导出包，使得其他模块可以使用这些包中的类。导出关系通过&lt;code&gt;exports&lt;/code&gt;声明来定义。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;打开&lt;/strong&gt;（Opens）：模块可以打开包，允许其他模块访问其内部的类。这通常用于模块间的服务提供者和使用者关系。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;模块系统的使用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 Java 11 及以上版本中，开发者需要使用模块系统来构建应用程序。以下是一个简单的模块化应用程序的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commons&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;requires&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;exports&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commons&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;opens&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;myapp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;internal&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commons&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这个例子中，我们定义了一个名为&lt;code&gt;com.example.myapp&lt;/code&gt;的模块，它依赖于&lt;code&gt;com.example.commons&lt;/code&gt;模块和 Java 标准库中的&lt;code&gt;java.sql&lt;/code&gt;模块。我们还导出了&lt;code&gt;com.example.myapp&lt;/code&gt;包给&lt;code&gt;com.example.commons&lt;/code&gt;，并打开了&lt;code&gt;com.example.myapp.internal&lt;/code&gt;包给&lt;code&gt;com.example.commons&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Jigsaw 项目和模块系统的引入标志着 Java 平台在模块化方面的重大进步。模块化不仅提高了代码的组织性和可维护性，还为 Java 应用程序的性能和安全性带来了显著的提升。随着 Java 平台的不断发展，模块系统将继续演进，为开发者提供更多的灵活性和控制力。开发者应当熟悉模块化的概念和最佳实践，以便充分利用这一特性。&lt;/p&gt;
&lt;h2 id="javadoc-和字节码的更新"&gt;&lt;a href="#javadoc-%e5%92%8c%e5%ad%97%e8%8a%82%e7%a0%81%e7%9a%84%e6%9b%b4%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;Javadoc 和字节码的更新
&lt;/h2&gt;&lt;p&gt;随着 Java 语言和平台的发展，Javadoc 工具和字节码规范也在不断进化，以适应新的编程实践和性能需求。这些更新对于开发者来说至关重要，因为它们影响着代码的文档化、兼容性和执行效率。&lt;/p&gt;
&lt;h3 id="javadoc-的现代化"&gt;&lt;a href="#javadoc-%e7%9a%84%e7%8e%b0%e4%bb%a3%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;Javadoc 的现代化
&lt;/h3&gt;&lt;p&gt;Javadoc 是 Java 开发者用来生成 API 文档的重要工具。在 Java 9 中，Javadoc 工具经历了一次重大更新，引入了多项新特性和改进。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新的文档标签和工具特性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@thumbnail&lt;/code&gt;标签&lt;/strong&gt;：这个新标签允许开发者在 Javadoc 中嵌入小型图像，增强了文档的可读性和直观性。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;@implSpec&lt;/code&gt;和&lt;code&gt;@implNote&lt;/code&gt;标签&lt;/strong&gt;：这两个标签分别用于描述实现的意图和注意事项，提供了一种标准化的方式来记录实现细节。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Javadoc 预览特性&lt;/strong&gt;：Java 9 引入了预览特性的概念，允许开发者尝试即将推出的 Javadoc 新特性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;HTML5 和搜索功能的引入&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Javadoc 工具现在支持 HTML5，这意味着生成的文档可以利用现代 Web 技术，提供更好的跨设备兼容性和用户体验。此外，Javadoc 输出现在包括一个搜索框，允许用户快速查找 API 文档中的类、方法和属性。&lt;/p&gt;
&lt;h3 id="字节码的改进和新增"&gt;&lt;a href="#%e5%ad%97%e8%8a%82%e7%a0%81%e7%9a%84%e6%94%b9%e8%bf%9b%e5%92%8c%e6%96%b0%e5%a2%9e" class="header-anchor"&gt;&lt;/a&gt;字节码的改进和新增
&lt;/h3&gt;&lt;p&gt;Java 虚拟机（JVM）的字节码指令集和属性也在不断演进，以支持新的语言特性和性能优化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;动态类生成的改进&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;随着 Java 语言和 API 的发展，动态生成类的能力变得更加重要。例如，Java 9 引入了&lt;code&gt;java.lang.invoke.MethodHandle&lt;/code&gt;的新方法，使得动态类生成更加高效和灵活。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新增的字节码指令和属性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;invokedynamic&lt;/code&gt;指令&lt;/strong&gt;：这个指令已经在 Java 7 中引入，用于动态解析方法调用，支持动态语言和框架，如 Project Panama。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;新增的类文件属性&lt;/strong&gt;：Java 11 中引入了新的类文件属性，如&lt;code&gt;Module&lt;/code&gt;和&lt;code&gt;ModulePackages&lt;/code&gt;，它们与 Java 模块系统相关联，用于存储模块化的元数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;新增的 Code Attribute&lt;/strong&gt;：Java 11 还引入了新的 Code Attribute，如&lt;code&gt;StackMapTable&lt;/code&gt;，用于增强 JVM 的类型检查和安全性。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Javadoc 和字节码的更新反映了 Java 平台对现代软件开发需求的响应。Javadoc 的现代化改进使得 API 文档更加丰富和易用，而字节码的增强则为 Java 语言的新特性和性能优化提供了支持。开发者应当关注这些更新，以便更好地利用 Java 平台提供的工具和特性。随着 Java 技术的不断发展，我们可以期待未来会有更多的创新和改进，进一步丰富 Java 生态系统。&lt;/p&gt;
&lt;h2 id="新支持的平台和版本方案"&gt;&lt;a href="#%e6%96%b0%e6%94%af%e6%8c%81%e7%9a%84%e5%b9%b3%e5%8f%b0%e5%92%8c%e7%89%88%e6%9c%ac%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;新支持的平台和版本方案
&lt;/h2&gt;&lt;p&gt;随着技术的发展和市场需求的变化，Java 平台不断扩展其对新硬件和操作系统的支持。这些更新确保了 Java 应用程序能够在更广泛的设备和环境中运行，同时保持了跨平台兼容性。&lt;/p&gt;
&lt;h3 id="跨平台支持和兼容性"&gt;&lt;a href="#%e8%b7%a8%e5%b9%b3%e5%8f%b0%e6%94%af%e6%8c%81%e5%92%8c%e5%85%bc%e5%ae%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;跨平台支持和兼容性
&lt;/h3&gt;&lt;p&gt;Java 的核心优势之一是其跨平台性，这意味着在不同操作系统和硬件上运行的 Java 虚拟机（JVM）能够提供一致的运行时环境。为了维持这一优势，Java 开发团队持续对 JVM 进行优化，以支持新的处理器架构和操作系统版本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新增平台的支持情况&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在 JDK 8 至 21 的版本中，Java 增加了对多个新平台的支持，包括但不限于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Linux/RISC-V&lt;/strong&gt;：随着 RISC-V 开源处理器架构的兴起，Java 在 JDK 19 中增加了对 Linux/RISC-V 的支持，为开发者在这一新兴平台上构建 Java 应用程序提供了可能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;macOS/AArch64&lt;/strong&gt;：随着 Apple 转向自家设计的 ARM 架构处理器，Java 在 JDK 17 中增加了对 macOS/AArch64 的支持，确保了 Java 应用程序能够在新的 Mac 设备上运行。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Windows/AArch64&lt;/strong&gt;：同样，为了支持 Windows 操作系统上的 ARM 架构，Java 也在 JDK 16 中增加了对 Windows/AArch64 的支持。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;版本兼容性和升级指南&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了帮助开发者平滑过渡到新版本，Java 提供了详细的版本兼容性指南。这些指南涵盖了从旧版本迁移到新版本的各个方面，包括 API 变更、废弃特性和新的模块系统。开发者可以参照这些指南来更新他们的应用程序，以利用新版本的特性和性能改进。&lt;/p&gt;
&lt;h3 id="版本命名方案的变更"&gt;&lt;a href="#%e7%89%88%e6%9c%ac%e5%91%bd%e5%90%8d%e6%96%b9%e6%a1%88%e7%9a%84%e5%8f%98%e6%9b%b4" class="header-anchor"&gt;&lt;/a&gt;版本命名方案的变更
&lt;/h3&gt;&lt;p&gt;Java 的版本命名方案在 JDK 9 中经历了重大变化。在此之前，Java 的版本号遵循主版本号。次版本号的模式（例如，1.8 表示 JDK 8）。从 JDK 9 开始，Java 采用了新的命名方案，其中版本号包括了年份和更新次数（例如，9 表示 2017 年的更新版本）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;新版本命名的逻辑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;新版本命名的逻辑旨在简化版本号的管理，并与 Java 的发布节奏保持一致。每个版本号都反映了它发布的时间，这使得开发者和用户能够更容易地了解他们使用的 Java 版本相对于其他版本的年龄。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;版本迭代的速度和周期&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;自 JDK 9 以来，Java 的版本迭代速度加快，每六个月发布一个新的版本。这种快速迭代的模式使得 Java 能够更快地引入新特性和改进，同时也意味着开发者和用户需要更频繁地更新他们的 Java 环境。为了适应这种快速迭代，Java 也引入了长期支持（LTS）版本，这些版本会得到更长时间的支持和维护，适合需要稳定性和长期支持的企业级应用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;新支持的平台和版本方案的变更体现了 Java 对不断变化的技术环境的适应性。通过增加对新硬件和操作系统的支持，Java 确保了其跨平台优势的持续存在。同时，新的版本命名方案和快速迭代模式使得 Java 能够更快地响应开发者的需求，推动 Java 生态系统的持续发展。开发者应当关注这些变化，以确保他们的应用程序能够充分利用 Java 平台的最新特性和改进。&lt;/p&gt;
&lt;h2 id="弃用和移除的特性"&gt;&lt;a href="#%e5%bc%83%e7%94%a8%e5%92%8c%e7%a7%bb%e9%99%a4%e7%9a%84%e7%89%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;弃用和移除的特性
&lt;/h2&gt;&lt;p&gt;随着 Java 平台的发展，某些旧特性可能会因为安全问题、更好的替代方案或者技术进步而变得过时。为了保持语言的现代化和高效性，Java 开发团队会定期审查和弃用这些特性，并在适当的时候将其移除。这一过程需要仔细的规划和透明的沟通，以确保开发者有足够的时间来适应变化。&lt;/p&gt;
&lt;h3 id="弃用列表和未来展望"&gt;&lt;a href="#%e5%bc%83%e7%94%a8%e5%88%97%e8%a1%a8%e5%92%8c%e6%9c%aa%e6%9d%a5%e5%b1%95%e6%9c%9b" class="header-anchor"&gt;&lt;/a&gt;弃用列表和未来展望
&lt;/h3&gt;&lt;p&gt;Java 社区维护了一个详细的弃用特性列表，这个列表随着每个新版本的发布而更新。开发者可以通过查阅这个列表来了解哪些特性已被弃用，以及它们的未来状态。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Java EE 的移除&lt;/strong&gt;：Java 企业版（Java EE）在 JDK 11 中被移除，转而作为独立的 Eclipse 项目继续发展。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CORBA 模块的移除&lt;/strong&gt;：Java 的 CORBA 支持在 JDK 11 中被移除，因为这项技术的使用已经变得较少。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Nashorn JavaScript 引擎的移除&lt;/strong&gt;：在 JDK 15 中，Nashorn JavaScript 引擎被标记为弃用，并在 JDK 11 中被完全移除。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;移除特性的影响和替代方案&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;移除特性会对依赖这些特性的应用程序产生影响。因此，Java 提供了替代方案，以帮助开发者平滑过渡到新的技术。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Java EE 的替代方案&lt;/strong&gt;：对于 Java EE 的替代，开发者可以转向 Spring Boot、Quarkus 等现代框架，它们提供了类似的功能，并且更加轻量级和灵活。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;CORBA 的替代方案&lt;/strong&gt;：对于需要 RPC（远程过程调用）的开发者，可以考虑使用 gRPC 或 RESTful 服务。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Nashorn 的替代方案&lt;/strong&gt;：对于需要在 Java 中执行 JavaScript 的开发者，可以考虑使用 GraalVM 的 JavaScript 引擎。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;向后兼容性的挑战&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;向后兼容性是 Java 平台的一个重要原则，它确保了旧代码在新版本中仍然能够正常运行。然而，移除特性的决定可能会对向后兼容性造成挑战。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;向后兼容性的策略&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为了最小化对现有应用程序的影响，Java 开发团队采取了一系列策略：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;清晰的弃用周期&lt;/strong&gt;：Java 提供了清晰的弃用周期，通常在特性被完全移除之前会有一个警告期。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;提供替代方案&lt;/strong&gt;：Java 努力为每个被弃用的特性提供替代方案，以帮助开发者进行迁移。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;逐步移除&lt;/strong&gt;：对于大型特性，Java 可能会逐步移除，先将其标记为弃用，然后在后续版本中完全移除。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="开发者如何应对弃用和移除"&gt;&lt;a href="#%e5%bc%80%e5%8f%91%e8%80%85%e5%a6%82%e4%bd%95%e5%ba%94%e5%af%b9%e5%bc%83%e7%94%a8%e5%92%8c%e7%a7%bb%e9%99%a4" class="header-anchor"&gt;&lt;/a&gt;开发者如何应对弃用和移除
&lt;/h3&gt;&lt;p&gt;开发者应当密切关注 Java 社区的更新和公告，了解哪些特性正在被弃用或计划被移除。对于正在使用的弃用特性，开发者应该：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;评估影响&lt;/strong&gt;：分析应用程序中使用弃用特性的代码，评估迁移的成本和影响。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;制定迁移计划&lt;/strong&gt;：根据评估结果，制定详细的迁移计划，包括时间表和技术路线。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;测试和验证&lt;/strong&gt;：在迁移到新特性或替代方案后，进行充分的测试以确保应用程序的功能和性能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;更新文档和培训&lt;/strong&gt;：更新内部文档，并为团队成员提供必要的培训，以确保所有人都了解新的变化。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过这些措施，开发者可以确保他们的应用程序能够适应 Java 平台的变化，同时保持高效和稳定。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;在对 Java 平台从 JDK 8 至 21 的演进进行深入探讨后，我们可以总结出几个关键点，这些点不仅展示了 Java 语言和生态系统的发展，也为开发者提供了未来工作的方向和策略。&lt;/p&gt;
&lt;h3 id="语言特性的进步"&gt;&lt;a href="#%e8%af%ad%e8%a8%80%e7%89%b9%e6%80%a7%e7%9a%84%e8%bf%9b%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;语言特性的进步
&lt;/h3&gt;&lt;p&gt;Java 语言在这段时间里经历了显著的增强，包括引入了函数式编程概念、模式匹配、未命名变量和模式、封闭类和记录类等。这些新特性不仅丰富了 Java 的表达能力，也使得代码更加简洁、可读性更强，同时提高了开发效率。&lt;/p&gt;
&lt;h3 id="api-的扩展和更新"&gt;&lt;a href="#api-%e7%9a%84%e6%89%a9%e5%b1%95%e5%92%8c%e6%9b%b4%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;API 的扩展和更新
&lt;/h3&gt;&lt;p&gt;Java 标准库的扩展和更新为开发者提供了新的工具和能力。集合和数学函数 API 的增强、文件和 IO 操作的改进、网络编程的新特性以及安全管理器和权限控制的更新，都使得 Java 应用程序能够更好地处理复杂的任务和安全挑战。&lt;/p&gt;
&lt;h3 id="性能和资源管理的优化"&gt;&lt;a href="#%e6%80%a7%e8%83%bd%e5%92%8c%e8%b5%84%e6%ba%90%e7%ae%a1%e7%90%86%e7%9a%84%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;性能和资源管理的优化
&lt;/h3&gt;&lt;p&gt;Java 虚拟机的性能和资源管理一直是 Java 开发的重点。JIT 编译器的优化、垃圾回收器的进步、内存管理和启动时间的改进，都显著提高了 Java 应用程序的运行效率和用户体验。&lt;/p&gt;
&lt;h3 id="模块化和跨平台支持"&gt;&lt;a href="#%e6%a8%a1%e5%9d%97%e5%8c%96%e5%92%8c%e8%b7%a8%e5%b9%b3%e5%8f%b0%e6%94%af%e6%8c%81" class="header-anchor"&gt;&lt;/a&gt;模块化和跨平台支持
&lt;/h3&gt;&lt;p&gt;Jigsaw 项目的完成和模块系统的引入，标志着 Java 平台在模块化方面取得了重大进展。这不仅提高了 Java 的内在质量和可维护性，也简化了应用程序的部署和分发。同时，Java 对新平台的支持，如 RISC-V 和 AArch64，确保了 Java 在多样化的计算环境中的持续相关性。&lt;/p&gt;
&lt;h3 id="向后兼容性的维护"&gt;&lt;a href="#%e5%90%91%e5%90%8e%e5%85%bc%e5%ae%b9%e6%80%a7%e7%9a%84%e7%bb%b4%e6%8a%a4" class="header-anchor"&gt;&lt;/a&gt;向后兼容性的维护
&lt;/h3&gt;&lt;p&gt;Java 社区对向后兼容性的承诺保证了新版本可以无缝地与现有代码库协同工作。尽管一些特性被弃用或移除，但 Java 提供了清晰的路线图和替代方案，以帮助开发者平滑过渡。&lt;/p&gt;
&lt;h3 id="未来展望"&gt;&lt;a href="#%e6%9c%aa%e6%9d%a5%e5%b1%95%e6%9c%9b" class="header-anchor"&gt;&lt;/a&gt;未来展望
&lt;/h3&gt;&lt;p&gt;随着 Java 的不断发展，我们可以预见到更多的创新和改进。新的语言特性、API 和性能优化将继续出现，以适应新的编程范式和技术趋势。同时，Java 社区将继续致力于提高平台的安全性、可维护性和用户友好性。&lt;/p&gt;
&lt;p&gt;对于开发者来说，保持对 Java 新特性和最佳实践的了解至关重要。通过不断学习和适应变化，开发者可以确保他们的技能和应用程序保持最新，从而在不断变化的技术环境中保持竞争力。随着 Java 生态系统的不断壮大，Java 将继续作为企业级应用程序和现代云服务的坚实基础，引领软件开发的未来。&lt;/p&gt;</description></item><item><title>跟着 Guava 学 Java 之缓存</title><link>https://xiaobox.github.io/p/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/</link><pubDate>Wed, 21 Sep 2022 14:42:09 +0000</pubDate><guid>https://xiaobox.github.io/p/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/cover.jpg" alt="Featured image of post 跟着 Guava 学 Java 之缓存" /&gt;&lt;p&gt;本文我们先介绍一些缓存的背景知识，以及内存缓存的流行开源库类实现，最后利用一些例子重点介绍下 Guava Cache 的缓存功能。&lt;/p&gt;
&lt;h2 id="背景"&gt;&lt;a href="#%e8%83%8c%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;背景
&lt;/h2&gt;&lt;h3 id="什么是缓存"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;什么是缓存
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;在计算中，缓存是一个高速数据存储层，其中存储了数据子集，且通常是短暂性存储，这样日后再次请求该数据时，速度要比访问数据的主存储位置快。通过缓存，可以高效地重用之前检索或计算的数据。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;本文中所提及的缓存主要是指内存缓存，跟硬件没什么关系（比如三级缓存什么的），主要是应用代码层面和内存交互的这部分。&lt;/p&gt;
&lt;h3 id="缓存的特点"&gt;&lt;a href="#%e7%bc%93%e5%ad%98%e7%9a%84%e7%89%b9%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;缓存的特点
&lt;/h3&gt;&lt;p&gt;第一个特点：贼快（操作内存读写当然快了）&lt;/p&gt;
&lt;p&gt;你可能会问了，贼快是多快？嗯，没有对比就没有伤害，我们来看一下不同介质访问数据的时间情况&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/001-ee7b0c2a.jpg"&gt;&lt;/p&gt;
&lt;p&gt;看到了吧，RAM 的速度大概是 10-100 纳秒，什么概念？ 1 秒钟等于 10 亿纳秒，这速度快到你根本感觉不到。&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;/ul&gt;
&lt;h3 id="解决什么问题"&gt;&lt;a href="#%e8%a7%a3%e5%86%b3%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;解决什么问题
&lt;/h3&gt;&lt;p&gt;一般来说，我们利用本地的内存缓存主要可以达到减轻数据库压力、提高系统响应速度和吞吐量的目的。&lt;/p&gt;
&lt;p&gt;总之，如果对某些值的计算或检索成本很高，并且多次需要使用该值时，应该考虑使用缓存。&lt;/p&gt;
&lt;h3 id="内存缓存库类"&gt;&lt;a href="#%e5%86%85%e5%ad%98%e7%bc%93%e5%ad%98%e5%ba%93%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;内存缓存库类
&lt;/h3&gt;&lt;p&gt;在 Java 中一提到缓存，我们首先想到的可以用 &lt;code&gt;ConcurrentHashMap&lt;/code&gt;做缓存。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;static ConcurrentHashMap&amp;lt;String,Object&amp;gt; localCache = new ConcurrentHashMap&amp;lt;&amp;gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么要用 &lt;code&gt;ConcurrentHashMap&lt;/code&gt; 呢？&lt;/p&gt;
&lt;p&gt;因为首先它是个 Map，这种 K,V 的数据结构很适合用来读写缓存对象，其次它还是线程安全的，多线程并发不会有线程安全问题。&lt;/p&gt;
&lt;p&gt;Java 虽然为我们提供了&lt;code&gt;ConcurrentHashMap&lt;/code&gt; 这样合适做缓存的数据结构，但他在功能上却有很多的不足，比如没有 回收、驱逐、监听、刷新等功能。一般来说，我们设计一套完整的缓存方案虽然这些功能，用 &lt;code&gt;ConcurrentHashMap&lt;/code&gt;意味着这些功能你要自己开发了。&lt;/p&gt;
&lt;p&gt;在 Java 的生态中有许多库可以帮助我们省去自己开发的麻烦，人家都封装好了，开箱即用，这里我们列举几个知名和常用的，后面我们重点介绍 Guava 的 cache 模块：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Guava Cache&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Spring Cache&lt;/code&gt; Spring 提供的一整套的缓存解决方案。虽然它本身并没有提供缓存的实现，但是它提供了一整套的接口和代码规范、配置、注解等，这样它就可以整合各种缓存方案了，比如 Redis、Ehcache，我们也就不用关心操作缓存的细节。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Caffeine&lt;/code&gt;（以 GuavaCache 为原型而开发的一个本地缓存框架，相对 GuavaCache, 它有更高的性能与命中率，更强大的功能，更灵活的配置方式）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;J2Cache&lt;/code&gt;（OSChina 开源的一个两级缓存框架，采用固定的 一级 + 二级缓存 的模式，从一开始就是为了解决两级缓存一致性的问题）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JetCache&lt;/code&gt;（是阿里开源的通用缓存访问框架，它统一了多级缓存的访问方式，封装了类似于 SpringCache 的注解，以及 GuavaCache 类似的 Builder, 来简化项目中使用缓存的难度）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里多说两句：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Caffeine&lt;/code&gt;是当前最优秀的内存缓存框架，不论读还是写的效率都远高于其他缓存，而且在&lt;code&gt;Spring5&lt;/code&gt;开始的默认缓存实现就将 Caffeine 代替原来的 Guava。&lt;/p&gt;
&lt;p&gt;在项目中，比如你用 SpringBoot 想加本地缓存，我们通常会引入 &lt;code&gt;SpringCache+Caffeine&lt;/code&gt;的依赖。使用 &lt;code&gt;SpringCache&lt;/code&gt; 注解方法实现缓存。SpringCache 帮我们封装了 Caffeine，通过这种方式集成 Caffeine。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-cache&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;com.github.ben-manes.caffeine&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;caffeine&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有朋友说了，你这是一级缓存，我们一般会使用二级缓存，即一级缓存用 &lt;code&gt;caffeine&lt;/code&gt; 二级缓存用 &lt;code&gt;Redis&lt;/code&gt;(强强联合，很常用的方案)，一级缓存找不到去二级缓存找。&lt;/p&gt;
&lt;p&gt;没错，如果你想用 SpringBoot 集成 &lt;code&gt;Caffeine&lt;/code&gt;和&lt;code&gt;Redis&lt;/code&gt;实现二级缓存，有两种方式：&lt;/p&gt;
&lt;p&gt;第一种，直接集成，引入的依赖有变化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-cache&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;com.github.ben-manes.caffeine&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;caffeine&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-data-redis&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里顺便说一下 &lt;code&gt;spring-boot-starter-data-redis&lt;/code&gt; ，spring-data-redis 和 Redis 的关系如下图，延续了 Spring 的一贯思想，对上层仍然是一层封装，对底层支持各种 Redis 客户端的实现。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/002-de281151.jpg"&gt;&lt;/p&gt;
&lt;p&gt;第一种方式的集成比较简单，但请注意 spring cache (caffeine) 和 spring-data-redis(redis)，是各管各的（如前面括号里写的），不好意思，一二级缓存之间的逻辑关系需要你自己处理 具体来说比如你可以实现 cache 拦截器 &lt;code&gt;CacheInterceptor&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里有一个比较容易混乱的点， spring cache 是支持多个 Provider 的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generic&lt;/li&gt;
&lt;li&gt;JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)&lt;/li&gt;
&lt;li&gt;EhCache 2.x&lt;/li&gt;
&lt;li&gt;Hazelcast&lt;/li&gt;
&lt;li&gt;Infinispan&lt;/li&gt;
&lt;li&gt;Couchbase&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;Caffeine&lt;/li&gt;
&lt;li&gt;Simple&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;意思是我们用 springcache 即可以集成 Caffeine 这种本地缓存，也可以集成 Redis 这种分布式缓存，当然配置肯定不一样。但你要清楚，同时只能集成一个，没有说用 springcache 能同时集成两个的， 上面讲的集成 二级缓存（caffeine+redis），是指各管各的，springcache 集成 Caffeine，spring-data-redis 集成 redis。&lt;/p&gt;
&lt;p&gt;刚才说了第一种集成方式，现在说第二种，即利用 jetCache 做二级缓存的集成，这里依赖有了很大变化：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;com.alicp.jetcache&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;jetcache-starter-redis-lettuce&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;2.6.3&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只需要这一个依赖，不需要 spring-cache 和 spring-data-redis 了 ，因为 jetCache 里面已经引入了 caffeine 和 lettuce 的包了。&lt;/p&gt;
&lt;p&gt;这并不是说 spring-data-redis 和 spring-cache 不能引入，可以用，如果你有需求，但要注意检查依赖的冲突和重复。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jetcache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;statIntervalMinutes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;areaInCacheName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;caffeine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;keyConvertor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fastjson2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;redis.lettuce&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;keyConvertor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fastjson2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;broadcastChannel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;projectA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;keyPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;projectA&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;valueEncoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;java&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;valueDecoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;java&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;redis://127.0.0.1:6379/&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;defaultExpireInMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们从上面的配置中可以看出，很明显这里配置了二级缓存，分别是 &lt;code&gt;local&lt;/code&gt; 和 &lt;code&gt;remote&lt;/code&gt;，由于 jetCache 支持二级缓存的操作，就不用我们自己写代码实现了，不过值得注意的是，要实现分布式两级缓存的同步因为太重，框架没做得自己实现，关于这个问题可以参考（https://github.com/alibaba/jetcache/issues/205）&lt;/p&gt;
&lt;h2 id="guava-缓存"&gt;&lt;a href="#guava-%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;Guava 缓存
&lt;/h2&gt;&lt;p&gt;终于到我们今天的主角 Guava Cache 了，无论你对 Caffeine 和 JetCache 多么感兴趣，请都先克制和忍耐一下，把 Guava 的 cache 看完，毕竟 Caffeine 也是以 Guava 为原型而产生的框架。&lt;/p&gt;
&lt;p&gt;这里我们再次强调** Guava Cache 指的本地缓存，即数据存储在当前应用服务器的内存之中，而像 Redis 这样的分布式缓存，数据是存储在应用服务器内存之外的**。&lt;/p&gt;
&lt;p&gt;下面我们来具体说说 Guava 的 Cache 怎么用&lt;/p&gt;
&lt;h4 id="加载"&gt;&lt;a href="#%e5%8a%a0%e8%bd%bd" class="header-anchor"&gt;&lt;/a&gt;加载
&lt;/h4&gt;&lt;p&gt;cache loading , 即缓存的加载或者创建有两种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cacheLoader&lt;/li&gt;
&lt;li&gt;callable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们首先说一下 cacheLoader ，举个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheLoader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;no &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cahceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;jack
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;123
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;no address!
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;{name=jack, id=123, address=no address!}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面的小例子我们用 cacheBuilder 构造出来的 &lt;code&gt;LoadingCache&lt;/code&gt; 来存取类型均为&lt;code&gt;String&lt;/code&gt; 的 K,V 缓存。build 方法需要传入一个 CacheLoader 对象，CacheLoader 是一个抽象类，需要重写 load 方法。这个 load 的方法的作用是：如果我们调用 LoadingCache 中的 get 方法，缓存不存在相对应的 key 的数据，那么 CacheLoader 会自动调用 load 方法加载数据进来，至于数据从哪里来是你自己设计的，比如从数据库或者 Redis 等等。&lt;/p&gt;
&lt;p&gt;关于最后的 getAll 方法： getAll(Iterable&amp;lt;? extends K&amp;gt;) 方法用来执行批量查询。默认情况下，对每个不在缓存中的键，getAll 方法会单独调用 CacheLoader.load 来加载缓存项。如果批量的加载比多个单独加载更高效，你可以重载 CacheLoader.loadAll 来利用这一点。getAll(Iterable) 的性能也会相应提升。&lt;/p&gt;
&lt;p&gt;关于 LoadingCache ， 我们看一下它的特点：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;A semi-persistent mapping from keys to values. Values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated. The common way to build instances is using CacheBuilder. Implementations of this interface are expected to be thread-safe, and can be safely accessed by multiple concurrent threads.&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;一种半持久化的 KV 结构&lt;/li&gt;
&lt;li&gt;这些 KV 会一直有效，直到被驱逐或者手动设置为无效&lt;/li&gt;
&lt;li&gt;线程安全的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接着我们来看一下 &lt;code&gt;callable&lt;/code&gt; ，举个简单例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIfPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIfPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;jack
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;123
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;null 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到 Callable 只有在缓存值不存在时，才会调用，值存在则直接返回该值。&lt;/p&gt;
&lt;p&gt;总结&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LoadingCache 继承了 Cache 接口。LoadingCache 读取一个指定 key 的数据时，如果 key 不存在，LoadingCache 会执行加载数据到缓存。（相当于全局的）&lt;/li&gt;
&lt;li&gt;cacheloader 的定义比较宽泛，是针对整个 cache 定义的，可以认为是统一的根据 key 值 load value 的方法。而 callable 的方式较为灵活，允许你在 get 的时候指定。（相当于个体自定义的）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实无论是 LoadingCache 还是 callable 的方式加载缓存，他们都实现了一个共同的语义，即 “get-if-absent-compute” 获取缓存-如果没有-则计算。注意这个语义是原子的，通过下图看到底层源码，是加了锁的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/003-914a54cf.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Java 中与此相似的原子语义有：ConcurrentHashMap 中的 &lt;code&gt;putIfAbsent&lt;/code&gt; 和 &lt;code&gt;computeIfAbsent&lt;/code&gt;，注意只是结构相似而已。&lt;/p&gt;
&lt;h3 id="缓存回收"&gt;&lt;a href="#%e7%bc%93%e5%ad%98%e5%9b%9e%e6%94%b6" class="header-anchor"&gt;&lt;/a&gt;缓存回收
&lt;/h3&gt;&lt;p&gt;从大的方面讲缓存的回收分两种，一种是手动回收，一种是自动回收。手动回收就是你自己调方法干掉它，自动就是根据一定的规则约定，当到达触发条件自动回收。&lt;/p&gt;
&lt;p&gt;我们先来看自动这部分。&lt;/p&gt;
&lt;p&gt;Guava Cache 提供了三种基本的缓存回收方式：基于容量回收、定时回收和基于引用回收。&lt;/p&gt;
&lt;p&gt;基于容量的回收（size-based eviction）&lt;/p&gt;
&lt;p&gt;顾名思义根据缓存的容量回收，超过或即将超过最大容量的缓存将被回收，我们可以通过 CacheBuilder 来设置最大容量：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;CacheBuilder.newBuilder().maximumSize(100).build();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;这个 size 指的是 cache 中的条目数，不是内存大小或是其他&lt;/li&gt;
&lt;li&gt;并不是完全到了指定的 size 系统才开始移除不常用的数据的，而是接近这个 size 的时候系统就会开始做移除的动作&lt;/li&gt;
&lt;li&gt;如果一个键值对已经从缓存中被移除了，你再次请求访问的时候，如果 cachebuild 是使用 cacheLoader 方式的，那依然还是会从 cacheloader 中再取一次值，如果这样还没有，就会抛出异常&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据源码注释可以看到，容量回收的算法是&lt;code&gt;LRU&lt;/code&gt;（最近最少使用）&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/004-7e9fb163.jpg"&gt;&lt;/p&gt;
&lt;p&gt;还可以根据缓存的“权重” 进行回收，什么意思呢？&lt;/p&gt;
&lt;p&gt;每一项缓存所占据的内存空间大小都可能不一样，我们可以把它看作它们有不同的“权重”（weights）, 作为执行清除策略时优化回收的对象。不过感觉基于权重的用的比较少。下面是一个官方的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maximumWeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;weigher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Weigher&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;weigh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheLoader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// no checked exception&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;createExpensiveGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以通过自定义一个 weight 函数来设置每项缓存的权重 。&lt;/p&gt;
&lt;p&gt;定时回收（Timed Eviction）&lt;/p&gt;
&lt;p&gt;定时回收，或者说基于存活时间的回收，主要有两个参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;expireAfterAccess 缓存项在给定时间内没有被读/写访问，则回收。这种缓存的回收顺序和基于大小回收一样。&lt;/li&gt;
&lt;li&gt;expireAfterWrite 缓存项在给定时间内没有被写访问（创建或覆盖），则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用，这种回收方式是可取的。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).expireAfterWrite(8,TimeUnit.SECONDS);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过这两个参数的设置可以发现：定时回收周期性地在写操作中执行，偶尔在读操作中执行&lt;/p&gt;
&lt;p&gt;我们在测试定时回收的时候不用设置了时间以后在那儿“傻等”，可以利用 Guava 的 Ticker 来模拟时间流逝，举个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FakeTicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Ticker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AtomicLong&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nanos&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AtomicLong&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cm"&gt;/** Advances the ticker value by {@code time} in {@code timeUnit}. */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FakeTicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;advance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TimeUnit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;timeUnit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nanos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAndGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toNanos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nanos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAndAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;is called &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;expireAfterWriteTestWithTicker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InterruptedException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FakeTicker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FakeTicker&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Use ticker to force expire in 20 minute&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;expireAfterWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MINUTES&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ldr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUnchecked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertNotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIfPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// add 21 minutes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;advance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MINUTES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;assertNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIfPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;基于引用的回收（Reference-based Eviction）&lt;/p&gt;
&lt;p&gt;Guava 允许你通过设置实现在 JVM GC 时回收缓存对象，这种移除方式主要是基于 java 的垃圾回收机制，根据键或者值的引用关系决定移除。&lt;/p&gt;
&lt;p&gt;稍微复习一下 Java 的引用类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;强引用：new 出来的一般对象，只要引用在就不会被回收&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;p&gt;其中，软引用 soft reference 可用来实现内存敏感的高速缓存。而弱引用 weak reference 引用的对象是有价值被 cache, 而且很容易被重新构建，且很消耗内存的对象。所以 软引用和弱引用被 Guava 利用来处理回收问题。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CacheBuilder.weakKeys()&lt;/code&gt; 使用弱引用存储键。当键没有其它（强或软）引用时，缓存项可以被垃圾回收。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CacheBuilder.weakValues()&lt;/code&gt;：使用弱引用存储值。当值没有其它（强或软）引用时，缓存项可以被垃圾回收。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CacheBuilder.softValues()&lt;/code&gt;：使用软引用存储值。软引用的对象会根据内存需求，以 LRU 的方式进行垃圾收集。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Cache&amp;lt;String, String&amp;gt; cache = CacheBuilder.newBuilder().weakKeys().weakValues().softValues().build();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实际工作中用引用回收的很少。&lt;/p&gt;
&lt;p&gt;上面我们介绍的回收方式，无论是基于容量回收、定时回收还是基于引用回收都是类似于自动回收的方式，下面我们介绍下手动显示回收，即手动回收缓存。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;个别清除：&lt;code&gt;Cache.invalidate(key)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;批量清除：&lt;code&gt;Cache.invalidateAll(keys)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;清除所有缓存项：&lt;code&gt;Cache.invalidateAll()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="移除监听器"&gt;&lt;a href="#%e7%a7%bb%e9%99%a4%e7%9b%91%e5%90%ac%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;移除监听器
&lt;/h3&gt;&lt;p&gt;通过&lt;code&gt;CacheBuilder.removalListener(RemovalListener)&lt;/code&gt;，你可以声明一个监听器，以便缓存项被移除时做一些额外操作。缓存项被移除时，&lt;code&gt;RemovalListener&lt;/code&gt;会获取移除通知&lt;code&gt;RemovalNotification&lt;/code&gt;，其中包含移除原因&lt;code&gt;RemovalCause&lt;/code&gt;、键和值。&lt;/p&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RemovalListener&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;myRemovalListener&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCause&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; | [&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;:&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;] is removed!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;removalListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myRemovalListener&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：EXPLICIT | [id:123] is removed!&lt;/p&gt;
&lt;p&gt;注意：默认情况下，监听器方法是在移除缓存时同步调用的。因为缓存的维护和请求响应通常是同时进行的，代价高昂的监听器方法在同步模式下会拖慢正常的缓存请求。在这种情况下，你可以使用&lt;code&gt;RemovalListeners.asynchronous(RemovalListener, Executor)&lt;/code&gt;把监听器装饰为异步操作，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ExecutorService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Executors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newSingleThreadExecutor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;expireAfterWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removalListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemovalListeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asynchronous&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCause&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; --&amp;gt; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="缓存回收的时机"&gt;&lt;a href="#%e7%bc%93%e5%ad%98%e5%9b%9e%e6%94%b6%e7%9a%84%e6%97%b6%e6%9c%ba" class="header-anchor"&gt;&lt;/a&gt;缓存回收的时机
&lt;/h3&gt;&lt;p&gt;关于这点，只需要知道 Guava cache 缓存不会”自动”执行清理和回收工作，也不会在某个缓存项过期后马上清理，也没有诸如此类的清理机制。相反，它会在写操作时顺带做少量的维护工作，或者偶尔在读操作时做——如果写操作实在太少的话。&lt;/p&gt;
&lt;p&gt;这样做的原因在于：如果要自动地持续清理缓存，就必须有一个线程，这个线程会和用户操作竞争共享锁。此外，某些环境下线程创建可能受限制，这样 CacheBuilder 就不可用了。&lt;/p&gt;
&lt;p&gt;相反，Guava 把选择权交到我们手里。如果你的缓存是高吞吐的，那就无需担心缓存的维护和清理等工作。如果你的 缓存只会偶尔有写操作，而你又不想清理工作阻碍了读操作，那么可以创建自己的维护线程，以固定的时间间隔调用&lt;code&gt;Cache.cleanUp()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ScheduledExecutorService&lt;/code&gt;可以帮助你很好地实现这样的定时调度。&lt;/p&gt;
&lt;h3 id="刷新"&gt;&lt;a href="#%e5%88%b7%e6%96%b0" class="header-anchor"&gt;&lt;/a&gt;刷新
&lt;/h3&gt;&lt;p&gt;如果对缓存设置过期时间，在高并发下同时执行 get 操作，而此时缓存值已过期了，如果没有保护措施，则会导致大量线程同时调用生成缓存值的方法，比如从数据库读取，对数据库造成压力，这也就是我们常说的“缓存击穿”。&lt;/p&gt;
&lt;p&gt;而 Guava cache 则对此种情况有一定控制。当大量线程用相同的 key 获取缓存值时，只会有一个线程进入 load 方法，而其他线程则等待（同步），直到缓存值被生成。这样也就避免了缓存击穿的危险。上述机制其实就是 &lt;code&gt;expireAfterWrite/expireAfterAccess&lt;/code&gt; 来控制的，如果你配置了过期策略对应的缓存项在过期后被访问就会走上述流程来加载缓存项。&lt;/p&gt;
&lt;p&gt;但这样做会导致大量的请求线程被阻塞。怎么办呢？&lt;/p&gt;
&lt;p&gt;Guava cache 的办法是提供一种缓存策略，缓存值定时刷新 &lt;code&gt;refreshAfterWrite&lt;/code&gt; ：更新线程调用 load 方法更新该缓存，其他请求线程返回该缓存的旧值。这样对于某个 key 的缓存来说，只会有一个线程被阻塞，用来生成缓存值，而其他的线程都返回旧的缓存值，不会被阻塞。&lt;/p&gt;
&lt;p&gt;我们对比一下 &lt;code&gt;refreshAfterWrite&lt;/code&gt; 和&lt;code&gt;expireAfterWrite&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;expireAfterWrite 是允许一个线程进去 load 方法，其他线程阻塞等待。&lt;/li&gt;
&lt;li&gt;refreshAfterWrite 是允许一个线程进去 load 方法，其他线程返回旧的值。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;那么问题解决了吗？ 单个 key 并发下，使用 refreshAfterWrite，虽然不会阻塞了，但是如果恰巧多个 key 同时过期，还是会给数据库造成压力，这就是我们所说的“缓存雪崩”。这时就要用到异步刷新，将刷新缓存值的任务交给后台线程，所有的用户请求线程均返回旧的缓存值。方法是覆盖 CacheLoader 的&lt;code&gt;reload&lt;/code&gt;方法，使用线程池去异步加载数据&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;只有重写了 reload 方法才有“异步加载”的效果。默认的 &lt;code&gt;reload&lt;/code&gt; 方法就是同步去执行 load 方法&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;关于 &lt;code&gt;reload&lt;/code&gt; 可以参考官方的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//有些键不需要刷新，并且我们希望刷新是异步完成的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;LoadingCache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;graphs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maximumSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refreshAfterWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MINUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheLoader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// no checked exception&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getGraphFromDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ListenableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;prevGraph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neverNeedsRefresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Futures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;immediateFuture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prevGraph&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// asynchronous!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ListenableFutureTask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ListenableFutureTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Graph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getGraphFromDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最佳实践：refreshTime &amp;lt; expireTime&lt;/p&gt;
&lt;p&gt;因为，根据 get 的流程，在 get 的时候，是先判断过期，再判断 refresh（如果 refreshTime &amp;gt; expireTime 意味着永远走不到缓存刷新逻辑。)，即如果过期了会优先调用 load 方法（阻塞其他线程）。&lt;/p&gt;
&lt;p&gt;在不过期情况下且过了 refresh 时间才去做 reload （异步加载，同时返回旧值），所以推荐的设置是 refresh &amp;lt; expire，这个设置还可以解决一个场景就是，如果长时间没有访问缓存，可以保证 expire 后可以取到最新的值，而不是因为 refresh 取到旧值。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/005-d36b6ce7.jpg"&gt;&lt;/p&gt;
&lt;h3 id="可选配置"&gt;&lt;a href="#%e5%8f%af%e9%80%89%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;可选配置
&lt;/h3&gt;&lt;p&gt;除了上文中已经介绍附带主题提到过的一些配置外，还有一些值得关注的配置：&lt;/p&gt;
&lt;p&gt;缓存的并发级别&lt;/p&gt;
&lt;p&gt;Guava 提供了设置并发级别的 api，使得缓存支持并发的写入和读取&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;CacheBuilder.newBuilder()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; // 设置并发级别为 cpu 核心数
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; .concurrencyLevel(Runtime.getRuntime().availableProcessors()) 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; .build();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同 ConcurrentHashMap 类似 Guava cache 的并发也是通过分离锁实现。在一般情况下，将并发级别设置为服务器 cpu 核心数是一个比较不错的选择。&lt;/p&gt;
&lt;p&gt;缓存的初始容量设置&lt;/p&gt;
&lt;p&gt;我们在构建缓存时可以为缓存设置一个合理大小初始容量，由于 Guava 的缓存使用了分离锁的机制，扩容的代价非常昂贵。所以合理的初始容量能够减少缓存容器的扩容次数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;CacheBuilder.newBuilder()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; // 设置初始容量为 100
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; .initialCapacity(100)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; .build();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="统计信息"&gt;&lt;a href="#%e7%bb%9f%e8%ae%a1%e4%bf%a1%e6%81%af" class="header-anchor"&gt;&lt;/a&gt;统计信息
&lt;/h3&gt;&lt;p&gt;在构建 Cache 对象时，可以通过 CacheBuilder 的 recordStats 方法开启统计信息的开关。开关开启后 Cache 会自动对缓存的各种操作进行统计，调用 Cache 的 stats 方法可以查看统计后的信息。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CacheStats&lt;/code&gt;对象以提供如下统计信息：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hitRate()&lt;/code&gt;：缓存命中率；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;averageLoadPenalty()&lt;/code&gt;：加载新值的平均时间，单位为纳秒；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evictionCount()&lt;/code&gt;：缓存项被回收的总数，不包括显式清除。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CacheBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;recordStats&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getIfPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;jack&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;CacheStats&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hitCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;missCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loadSuccessCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loadExceptionCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totalLoadTime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;evictionCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="asmap-视图"&gt;&lt;a href="#asmap-%e8%a7%86%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;asMap 视图
&lt;/h3&gt;&lt;p&gt;asMap 视图提供了缓存的 ConcurrentMap 形式，但 asMap 视图与缓存的交互需要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cache.asMap() 包含当前所有加载到缓存的项。因此相应地，cache.asMap().keySet() 包含当前所有已加载键；&lt;/li&gt;
&lt;li&gt;asMap().get(key) 实质上等同于 cache.getIfPresent(key)，而且不会引起缓存项的加载。这和 Map 的语义约定一致。&lt;/li&gt;
&lt;li&gt;所有读写操作都会重置相关缓存项的访问时间，包括 Cache.asMap().get(Object) 方法和 Cache.asMap().put(K, V) 方法，但不包括 Cache.asMap().containsKey(Object) 方法，也不包括在 Cache.asMap() 的集合视图上的操作。比如，遍历 Cache.asMap().entrySet() 不会重置缓存项的读取时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="caffeine"&gt;&lt;a href="#caffeine" class="header-anchor"&gt;&lt;/a&gt;Caffeine
&lt;/h2&gt;&lt;p&gt;从功能上看，Guava 已经比较完善了，满足了绝大部分本地缓存的需求。Caffine 除了提供 Guava 已有的功能外，同时还加入了一些扩展功能。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-09-21-gen-zhe-guava-xue-java-zhi-huan-cun/006-e87bb1db.jpg"&gt;&lt;/p&gt;
&lt;p&gt;关于 Caffeine 的话题，限于篇幅，我们在以后的文章中再讨论。&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h2&gt;&lt;p&gt;本文我们首先介绍了缓存的一些背景知识，了解了缓存的分类，以及内存缓存的一些开源库类，利用一些短小易懂的例子重点介绍了 Guava Cache，包括它的加载、更新、回收、配置、统计等功能。由于篇幅有限，有关常用的 JetCache 、Caffeine，还有一二级缓存的话题，有机会我会在后面的文章中跟大家再细聊。&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://albenw.github.io/posts/df42dc84/" target="_blank" rel="noopener"
 &gt;https://albenw.github.io/posts/df42dc84/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cnblogs.com/peida/p/guava" target="_blank" rel="noopener"
 &gt;https://www.cnblogs.com/peida/p/guava&lt;/a&gt;_cache.html&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cnblogs.com/rickiyang/p/11074159.html" target="_blank" rel="noopener"
 &gt;https://www.cnblogs.com/rickiyang/p/11074159.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.csdn.net/aitangyong/article/details/53114797" target="_blank" rel="noopener"
 &gt;https://blog.csdn.net/aitangyong/article/details/53114797&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.csdn.net/bitcarmanlee/article/details/106502697" target="_blank" rel="noopener"
 &gt;https://blog.csdn.net/bitcarmanlee/article/details/106502697&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://stackoverflow.com/questions/29990788/guava-ticker-cache-expire" target="_blank" rel="noopener"
 &gt;https://stackoverflow.com/questions/29990788/guava-ticker-cache-expire&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>跟着 Guava 学 Java 之 新集合类型</title><link>https://xiaobox.github.io/p/2022-08-08-gen-zhe-guava-xue-java-zhi-xin-ji-he-lei-xing/</link><pubDate>Mon, 08 Aug 2022 17:26:09 +0000</pubDate><guid>https://xiaobox.github.io/p/2022-08-08-gen-zhe-guava-xue-java-zhi-xin-ji-he-lei-xing/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-08-08-gen-zhe-guava-xue-java-zhi-xin-ji-he-lei-xing/cover.jpg" alt="Featured image of post 跟着 Guava 学 Java 之 新集合类型" /&gt;&lt;p&gt;Guava 引入了很多 JDK 没有的、但明显有用的新集合类型。&lt;/p&gt;
&lt;p&gt;这些新类型是为了和 JDK 集合框架共存，而没有往 JDK 集合抽象中硬塞其他概念。&lt;/p&gt;
&lt;p&gt;作为一般规则，Guava 集合非常精准地遵循了 JDK 接口契约。&lt;/p&gt;
&lt;h2 id="multiset"&gt;&lt;a href="#multiset" class="header-anchor"&gt;&lt;/a&gt;Multiset
&lt;/h2&gt;&lt;p&gt;我们都知道 Set 是无序不重复的，与之相反的是 List 是有序可重复的。Multiset 是几个意思？&lt;/p&gt;
&lt;p&gt;没错 ，&lt;strong&gt;Multiset 占据了 List 和 Set 之间的一个灰色地带：允许重复，但是不保证顺序&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;举个例子，使用 JDK 如果我们想：“统计每个单词出现的次数” 一般这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我用 multiset 就轻松多了，比如&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMultiset&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMultiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//结果是：2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//结果是：1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 如果你想要不重复元素集合，还可以直接转成 Set &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Set&amp;lt;String&amp;gt; words = multiset.elementSet();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你用传统的 HashMap 做统计，那么后续如果再增加元素，你想变更统计结果是不还得再写个 for 循环往 Map 添加元素计数？用 Multiset 轻松多了，直接 add 就行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;javascript&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMultiset&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMultiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;languages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Lists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newArrayList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;go&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;elementSet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; 的出现次数： &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multiset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;python 的出现次数： 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;java 的出现次数： 3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;c 的出现次数： 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;go 的出现次数： 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;javascript 的出现次数： 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当然如果你的需求比较简单，比如只是简单统计去重后的个数什么的，用 JDK8 以上的流式编程一行代码就能搞定，不用搞这么复杂&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;words.stream().distinct().count();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是 multiset 的一些常用方法：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;strong&gt;方法&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;描述&lt;/strong&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;count(E)&lt;/td&gt;
 &lt;td&gt;给定元素在 Multiset 中的计数&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;elementSet()&lt;/td&gt;
 &lt;td&gt;Multiset 中不重复元素的集合，类型为 Set&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;entrySet()&lt;/td&gt;
 &lt;td&gt;和 Map 的 entrySet 类似，返回 Set&amp;lt;Multiset.Entry&amp;gt;，其中包含的 Entry 支持 getElement() 和 getCount() 方法&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;add(E, int)&lt;/td&gt;
 &lt;td&gt;增加给定元素在 Multiset 中的计数&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;remove(E, int)&lt;/td&gt;
 &lt;td&gt;减少给定元素在 Multiset 中的计数&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;setCount(E, int)&lt;/td&gt;
 &lt;td&gt;设置给定元素在 Multiset 中的计数，不可以为负数&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;size()&lt;/td&gt;
 &lt;td&gt;返回集合元素的总个数（包括重复的元素）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Guava 提供了多种 Multiset 的实现，大致对应 JDK 中 Map 的各种实现：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;strong&gt;Map&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;对应的 Multiset&lt;/th&gt;
 &lt;th&gt;是否支持 null 元素&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;HashMap&lt;/td&gt;
 &lt;td&gt;HashMultiset&lt;/td&gt;
 &lt;td&gt;是&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TreeMap&lt;/td&gt;
 &lt;td&gt;TreeMultiset&lt;/td&gt;
 &lt;td&gt;是（如果 comparator 支持的话）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;LinkedHashMap&lt;/td&gt;
 &lt;td&gt;LinkedHashMultiset&lt;/td&gt;
 &lt;td&gt;是&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ConcurrentHashMap&lt;/td&gt;
 &lt;td&gt;ConcurrentHashMultiset&lt;/td&gt;
 &lt;td&gt;否&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ImmutableMap&lt;/td&gt;
 &lt;td&gt;ImmutableMultiset&lt;/td&gt;
 &lt;td&gt;否&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h3&gt;&lt;p&gt;使用 Multiset 可以减少 Map 的复杂操作，从而减少代码量，代码量少了，bug 自然少。早点儿下班。&lt;/p&gt;
&lt;h2 id="multimap"&gt;&lt;a href="#multimap" class="header-anchor"&gt;&lt;/a&gt;Multimap
&lt;/h2&gt;&lt;p&gt;有的时候我们需要一个 key 对应多个 value 的这种结构，通常我们会构造类似这样的数据结构：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Map&amp;lt;K, List&amp;lt;V&amp;gt;&amp;gt; 或 Map&amp;lt;K, Set&amp;lt;V&amp;gt;&amp;gt;&lt;/code&gt; ，甚至可能更复杂，基于这个还有嵌套数据结构。&lt;/p&gt;
&lt;p&gt;如果你需要找到 List 中的某个值是否存在，或者删除 List 中的一个元素 ，又或者要遍历整个数据结构，那么要写一坨代码，老费劲了。&lt;/p&gt;
&lt;p&gt;我们来看用 Multimap 怎么做，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; ArrayListMultimap&amp;lt;String, String&amp;gt; multimap = ArrayListMultimap.create();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;Fruits&amp;#34;, &amp;#34;Bannana&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;Fruits&amp;#34;, &amp;#34;Apple&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;Fruits&amp;#34;, &amp;#34;Pear&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;Vegetables&amp;#34;, &amp;#34;Carrot&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;language&amp;#34;, &amp;#34;java&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;language&amp;#34;, &amp;#34;python&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;language&amp;#34;, &amp;#34;go&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; multimap.put(&amp;#34;language&amp;#34;, &amp;#34;python&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是输出的结果：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{Vegetables=[Carrot], language=[java, python, go, python], Fruits=[Bannana, Apple, Pear, Pear]}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果想得到某一个 key 的 value 直接 get 就可以了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;List&amp;lt;String&amp;gt; language = multimap.get(&amp;#34;language&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你想转回原生的那种数据结构也是可以的，使用 asMap() ：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Map&amp;lt;String, Collection&amp;lt;String&amp;gt;&amp;gt; stringCollectionMap = multimap.asMap();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但要注意，stringCollectionMap 是一个&lt;strong&gt;关联的视图&lt;/strong&gt;，在这个 Map 上的操作会作用于原始的&lt;code&gt;Multimap&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;下面是一些可以很方便地修改 multimap 的方法：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;strong&gt;方法签名&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;描述&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;等价于&lt;/strong&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;put(K, V)&lt;/td&gt;
 &lt;td&gt;添加键到单个值的映射&lt;/td&gt;
 &lt;td&gt;multimap.get(key).add(value)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;putAll(K, Iterable)&lt;/td&gt;
 &lt;td&gt;依次添加键到多个值的映射&lt;/td&gt;
 &lt;td&gt;Iterables.addAll(multimap.get(key), values)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;remove(K, V)&lt;/td&gt;
 &lt;td&gt;移除键到值的映射；如果有这样的键值并成功移除，返回 true。&lt;/td&gt;
 &lt;td&gt;multimap.get(key).remove(value)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;removeAll(K)&lt;/td&gt;
 &lt;td&gt;清除键对应的所有值，返回的集合包含所有之前映射到 K 的值，但修改这个集合就不会影响 Multimap 了。&lt;/td&gt;
 &lt;td&gt;multimap.get(key).clear()&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;replaceValues(K, Iterable)&lt;/td&gt;
 &lt;td&gt;清除键对应的所有值，并重新把 key 关联到 Iterable 中的每个元素。返回的集合包含所有之前映射到 K 的值。&lt;/td&gt;
 &lt;td&gt;multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="迭代-multimap"&gt;&lt;a href="#%e8%bf%ad%e4%bb%a3-multimap" class="header-anchor"&gt;&lt;/a&gt;迭代 Multimap
&lt;/h3&gt;&lt;p&gt;Guava &lt;code&gt;MultiMap&lt;/code&gt; 提供 &lt;code&gt;keySet(), entries(), values(), keys()&lt;/code&gt; 方法类似于 Map 的相应视图集合。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 使用 `keySet()` 方法遍历 Guava 的 `MultiMap`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;keySet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 使用 `entries()` 方法遍历 Guava 的 `MultiMap`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="在-multimap-中查找键值"&gt;&lt;a href="#%e5%9c%a8-multimap-%e4%b8%ad%e6%9f%a5%e6%89%be%e9%94%ae%e5%80%bc" class="header-anchor"&gt;&lt;/a&gt;在 Multimap 中查找键/值
&lt;/h3&gt;&lt;p&gt;Guava 提供了三种方法，即 &lt;code&gt;containsKey()&lt;/code&gt;, &lt;code&gt;containsValue()&lt;/code&gt; 和 &lt;code&gt;containsEntry()&lt;/code&gt; 检查 multimap 是否包含至少一个键值对，分别具有指定的键、指定的值和指定的键值对。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ListMultimap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayListMultimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tyler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Kennedy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;George&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Washington&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;George&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Bush&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 检查 multimap 是否包含至少一个键值对&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 以“John”为键&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Multimap contains the \&amp;#34;John\&amp;#34; key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 检查 multimap 是否包含至少一个键值对&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 以“Kennedy”为值&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Kennedy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Multimap contains the \&amp;#34;Kennedy\&amp;#34; value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 检查 multimap 是否包含至少一个键值对&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 以“George”为键，以“Washington”为值&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;containsEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;George&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Washington&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Multimap contains the specified mapping&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="不可变-multimap"&gt;&lt;a href="#%e4%b8%8d%e5%8f%af%e5%8f%98-multimap" class="header-anchor"&gt;&lt;/a&gt;不可变 Multimap
&lt;/h3&gt;&lt;p&gt;Guava’s &lt;code&gt;Multimap&lt;/code&gt; 接口有三个不可变的实现—— &lt;code&gt;ImmutableMultimap&lt;/code&gt;, &lt;code&gt;ImmutableListMultimap&lt;/code&gt;， 和&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ImmutableSetMultimap&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ListMultimap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;immutableMultimap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ImmutableListMultimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Zachary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Taylor&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Adams&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Tyler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Kennedy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;George&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Washington&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;George&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Bush&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Grover&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Cleveland&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;immutableMultimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这将失败，因为 map 是不可变的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;immutableMultimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Obama&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Barack&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UnsupportedOperationException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java.lang.UnsupportedOperationException thrown&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="multimap-的各种实现"&gt;&lt;a href="#multimap-%e7%9a%84%e5%90%84%e7%a7%8d%e5%ae%9e%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;Multimap 的各种实现
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;strong&gt;实现&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;键行为类似&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;值行为类似&lt;/strong&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;ArrayListMultimap&lt;/td&gt;
 &lt;td&gt;HashMap&lt;/td&gt;
 &lt;td&gt;ArrayList&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;HashMultimap&lt;/td&gt;
 &lt;td&gt;HashMap&lt;/td&gt;
 &lt;td&gt;HashSet&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;LinkedListMultimap&lt;/td&gt;
 &lt;td&gt;LinkedHashMap&lt;/td&gt;
 &lt;td&gt;LinkedList&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;LinkedHashMultimap&lt;/td&gt;
 &lt;td&gt;LinkedHashMap&lt;/td&gt;
 &lt;td&gt;LinkedHashMap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TreeMultimap&lt;/td&gt;
 &lt;td&gt;TreeMap&lt;/td&gt;
 &lt;td&gt;TreeSet&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ImmutableListMultimap&lt;/td&gt;
 &lt;td&gt;ImmutableMap&lt;/td&gt;
 &lt;td&gt;ImmutableList&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ImmutableSetMultimap&lt;/td&gt;
 &lt;td&gt;ImmutableMap&lt;/td&gt;
 &lt;td&gt;ImmutableSet&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;除了两个不可变形式的实现，其他所有实现都支持 null 键和 null 值&lt;/p&gt;
&lt;h3 id="注意"&gt;&lt;a href="#%e6%b3%a8%e6%84%8f" class="header-anchor"&gt;&lt;/a&gt;注意
&lt;/h3&gt;&lt;p&gt;我们可以使用 &lt;code&gt;size()&lt;/code&gt; 方法来确定 multimap 中键值对的总数。请注意，此方法不会返回多重映射中不同键的总数。要获得不同键的总数，请考虑使用 &lt;code&gt;keySet().size()&lt;/code&gt; 或者 &lt;code&gt;asMap().size()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="总结-1"&gt;&lt;a href="#%e6%80%bb%e7%bb%93-1" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h3&gt;&lt;p&gt;如果你有类似上文的复杂数据结构，请使用 &lt;code&gt;Multimap&lt;/code&gt; 它的优点超过 &lt;code&gt;java.util.Map&lt;/code&gt;。可能有些同学用过 apache 的&lt;code&gt;org.apache.commons.collections4.MultiValuedMap&lt;/code&gt; ，我个人感觉 Guava 的更好用。&lt;/p&gt;
&lt;h2 id="bimap"&gt;&lt;a href="#bimap" class="header-anchor"&gt;&lt;/a&gt;BiMap
&lt;/h2&gt;&lt;p&gt;BiMap 提供了一种 key 和 value 的双向关联的数据结构。&lt;/p&gt;
&lt;p&gt;我们知道对于 Map 可能通过 key 获得 value ，但反过来呢，通过 value 怎么取得 key 呢，比较费劲，类似下面代码：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;language&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fruit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//通过 key 获取 value&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;language&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//通过 value 获取 key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entrySet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)){&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你可能会说，不用那么麻烦，我用 stream 一行能搞定，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;testMap.entrySet().stream().filter(entry -&amp;gt; entry.getValue().equals(&amp;#34;java&amp;#34;)).findFirst().get().getKey()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个怎么说呢，行是行，代码可读性也挺好，对于熟悉 stream API 的没问题，不熟悉的理解起来有些成本。&lt;/p&gt;
&lt;p&gt;我们来看看用 Guava 的 BiMap 怎么解决：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashBiMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bimap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashBiMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;language&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fruit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 通过 key 获取 value&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fruit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 通过 value 获取 key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bimap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;是不是感觉很简洁。&lt;/p&gt;
&lt;p&gt;这里要注意：&lt;strong&gt;反转的 map 不是新的 map 对象，它实现了一种视图关联，这样你对于反转后的 map 的所有操作都会影响原先的 map 对象&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="bimap-数据的强制唯一性"&gt;&lt;a href="#bimap-%e6%95%b0%e6%8d%ae%e7%9a%84%e5%bc%ba%e5%88%b6%e5%94%af%e4%b8%80%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;BiMap 数据的强制唯一性&lt;/strong&gt;
&lt;/h3&gt;&lt;p&gt;在使用 BiMap 时，会要求 Value 的唯一性。如果 value 重复了则会抛出错误：java.lang.IllegalArgumentException，例如&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; HashBiMap&amp;lt;String,String&amp;gt; bimap = HashBiMap.create();
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; bimap.put(&amp;#34;language&amp;#34;,&amp;#34;java&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; bimap.put(&amp;#34;fruit&amp;#34;,&amp;#34;apple&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; bimap.put(&amp;#34;hello&amp;#34;,&amp;#34;java&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果我们确实需要插入重复的 value 值，那可以选择 forcePut 方法。但是我们需要注意的是前面的 key 也会被覆盖了。&lt;/p&gt;
&lt;p&gt;value 不能重复，本身 map 的 key 就不是重复的，所以 Bimap 等于即不允许 key 重复，也不允许 value 重复。&lt;/p&gt;
&lt;h3 id="bimap-的各种实现"&gt;&lt;a href="#bimap-%e7%9a%84%e5%90%84%e7%a7%8d%e5%ae%9e%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;BiMap 的各种实现
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Key-Value Map 实现&lt;/th&gt;
 &lt;th&gt;Value-Key Map 实现&lt;/th&gt;
 &lt;th&gt;对应 BiMap 的实现&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;HashMap&lt;/td&gt;
 &lt;td&gt;HashMap&lt;/td&gt;
 &lt;td&gt;HashBiMap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ImmutableMap&lt;/td&gt;
 &lt;td&gt;ImmutableMap&lt;/td&gt;
 &lt;td&gt;ImmutableBiMap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;EnumMap&lt;/td&gt;
 &lt;td&gt;EnumMap&lt;/td&gt;
 &lt;td&gt;EnumBiMap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;EnumMap&lt;/td&gt;
 &lt;td&gt;HashMap&lt;/td&gt;
 &lt;td&gt;EnumHashBiMap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="table"&gt;&lt;a href="#table" class="header-anchor"&gt;&lt;/a&gt;Table
&lt;/h2&gt;&lt;p&gt;当你想使用多个键做索引的时候，你可能会用类似 Map&amp;lt;FirstName, Map&amp;lt;LastName, Person&amp;raquo;的实现，这种方式很丑陋，使用上也不友好。Guava 为此提供了新集合类型&lt;code&gt;Table&lt;/code&gt;，它有两个支持所有类型的键：“行”和“列” ，和 sql 中的联合主键有点像？&lt;/p&gt;
&lt;p&gt;我们看个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="err"&gt;大学课程座位表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ne"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HashBasedTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Chemical&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Electrical&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="ne"&gt;int&lt;/span&gt; &lt;span class="n"&gt;seatCount&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;seatCountForNoEntry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Oxford&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通过 “行”和“列” 定位到 value，不需要再像以前一样构造复杂的数据结构以及复杂的遍历代码。&lt;/p&gt;
&lt;p&gt;Table 有如下几种实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HashBasedTable：本质上用 HashMap&amp;lt;R, HashMap&amp;lt;C, V&amp;raquo;实现；&lt;/li&gt;
&lt;li&gt;TreeBasedTable：本质上用 TreeMap&amp;lt;R, TreeMap&amp;lt;C,V&amp;raquo;实现；&lt;/li&gt;
&lt;li&gt;ImmutableTable：本质上用 ImmutableMap&amp;lt;R, ImmutableMap&amp;lt;C, V&amp;raquo;实现；注：ImmutableTable 对稀疏或密集的数据集都有优化。&lt;/li&gt;
&lt;li&gt;ArrayTable：要求在构造时就指定行和列的大小，本质上由一个二维数组实现，以提升访问速度和密集 Table 的内存利用率&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="检查元素"&gt;&lt;a href="#%e6%a3%80%e6%9f%a5%e5%85%83%e7%b4%a0" class="header-anchor"&gt;&lt;/a&gt;检查元素
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ne"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HashBasedTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Chemical&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Electrical&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="err"&gt;行列的组合是否存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;entryIsPresent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="err"&gt;列是否存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;courseIsPresent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;containsColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="err"&gt;行是否存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;universityIsPresent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;containsRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="err"&gt;值是否存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;seatCountIsPresent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;containsValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="删除元素"&gt;&lt;a href="#%e5%88%a0%e9%99%a4%e5%85%83%e7%b4%a0" class="header-anchor"&gt;&lt;/a&gt;删除元素
&lt;/h3&gt;&lt;p&gt;想要删除元素，也需要通过“行”和“列”的组合&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;universityCourseSeatTable.remove(&amp;#34;Mumbai&amp;#34;, &amp;#34;IT&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="获取子-map"&gt;&lt;a href="#%e8%8e%b7%e5%8f%96%e5%ad%90-map" class="header-anchor"&gt;&lt;/a&gt;获取子 map
&lt;/h3&gt;&lt;p&gt;以“行”为 key ，获取 列和值 的 map&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="ne"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;harvard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以“列”为 key , 获取 行和值的 map&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; Map&amp;lt;String, Integer&amp;gt; universitySeatMap = universityCourseSeatTable.column(&amp;#34;IT&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="转换到传统-map"&gt;&lt;a href="#%e8%bd%ac%e6%8d%a2%e5%88%b0%e4%bc%a0%e7%bb%9f-map" class="header-anchor"&gt;&lt;/a&gt;转换到传统 map
&lt;/h3&gt;&lt;p&gt;如果你看着 Table 不顺眼了，还可以转换回传统的双层 map 嵌套的数据结构：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;courseKeyUniversitySeatMap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rowMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityKeyCourseSeatMap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;columnMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;courseKeyUniversitySeatMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;universityKeyCourseSeatMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Mumbai&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Chemical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;Harvard&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Electrical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Chemical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Mumbai&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;IT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Mumbai&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Harvard&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;Electrical&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Harvard&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="获得行列或-value-的集合"&gt;&lt;a href="#%e8%8e%b7%e5%be%97%e8%a1%8c%e5%88%97%e6%88%96-value-%e7%9a%84%e9%9b%86%e5%90%88" class="header-anchor"&gt;&lt;/a&gt;获得行、列或 value 的集合
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rowKeySet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;columnKeySet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;输出&lt;/span&gt;&lt;span class="err"&gt;：&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Mumbai&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Harvard&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Chemical&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Electrical&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;120&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="行列转换"&gt;&lt;a href="#%e8%a1%8c%e5%88%97%e8%bd%ac%e6%8d%a2" class="header-anchor"&gt;&lt;/a&gt;行列转换
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashBasedTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Chemical&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Mumbai&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Electrical&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Harvard&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;IT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cellSet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRowKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColumnKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tables2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Tables&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transpose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;universityCourseSeatTable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;=====行列转换后=====&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Cell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tables2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cellSet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRowKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColumnKey&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;利用&lt;code&gt;cellSet&lt;/code&gt;方法可以得到所有的数据行，打印结果，可以看到&lt;code&gt;row&lt;/code&gt;和&lt;code&gt;column&lt;/code&gt;发生了互换，输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Mumbai&lt;/span&gt; &lt;span class="n"&gt;Chemical&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Mumbai&lt;/span&gt; &lt;span class="n"&gt;IT&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Harvard&lt;/span&gt; &lt;span class="n"&gt;Electrical&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Harvard&lt;/span&gt; &lt;span class="n"&gt;IT&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;=====&lt;/span&gt;&lt;span class="err"&gt;行列转换后&lt;/span&gt;&lt;span class="o"&gt;=====&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Chemical&lt;/span&gt; &lt;span class="n"&gt;Mumbai&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IT&lt;/span&gt; &lt;span class="n"&gt;Mumbai&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Electrical&lt;/span&gt; &lt;span class="n"&gt;Harvard&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;IT&lt;/span&gt; &lt;span class="n"&gt;Harvard&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="总结-2"&gt;&lt;a href="#%e6%80%bb%e7%bb%93-2" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h3&gt;&lt;p&gt;虽然 Table 底层代码仍然使用的是嵌套 map 结构，但是经过封装使用起来简单多了，如有类似需求可以直接使用 Table，至于应用场景就看大家的需求了，感觉用好了就像用 SQL 操作内存数据库一样，比调用数据库快多了。&lt;/p&gt;
&lt;h2 id="rangeset"&gt;&lt;a href="#rangeset" class="header-anchor"&gt;&lt;/a&gt;RangeSet
&lt;/h2&gt;&lt;p&gt;介绍 RangeSet 之前，我们得先了解一下 Guava 的&lt;code&gt;Range&lt;/code&gt; 类，其实顾名思义就是表达区间范围，我们看一下它的 type 就明白了：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-08-08-gen-zhe-guava-xue-java-zhi-xin-ji-he-lei-xing/001-0aca7405.jpg"&gt;&lt;/p&gt;
&lt;p&gt;RangeSet 类是用来存储一些不为空的也不相交的范围的数据结构。假如需要向 RangeSet 的对象中加入一个新的范围，那么任何相交的部分都会被合并起来，所有的空范围都会被忽略。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RangeSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TreeRangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;8&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closedOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;15&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;20&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openClosed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;{[1‥10]}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;{[1‥10][11‥15)}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;{[1‥10][11‥15)(15‥20)}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;{[1‥5][10‥10][11‥15)(15‥20)}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="得到-rangeset-互补的范围"&gt;&lt;a href="#%e5%be%97%e5%88%b0-rangeset-%e4%ba%92%e8%a1%a5%e7%9a%84%e8%8c%83%e5%9b%b4" class="header-anchor"&gt;&lt;/a&gt;得到 rangeSet 互补的范围
&lt;/h3&gt;&lt;p&gt;们可以用 RangeSet 提供的 complement() 方法，rangeSet.complement() 同样是一个 RangeSet，其中的元素也是互不相交、且不为空的 RangeSet，那么 rangeSet 的互补集可以像下面这样来写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;RangeSet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;complement&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;complement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;输出&lt;/span&gt;&lt;span class="err"&gt;：&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;∞‥&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;5&lt;/span&gt;&lt;span class="err"&gt;‥&lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="err"&gt;‥&lt;/span&gt;&lt;span class="n"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;15&lt;/span&gt;&lt;span class="err"&gt;‥&lt;/span&gt;&lt;span class="n"&gt;15&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="n"&gt;20&lt;/span&gt;&lt;span class="err"&gt;‥&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="err"&gt;∞&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="包含查找"&gt;&lt;a href="#%e5%8c%85%e5%90%ab%e6%9f%a5%e6%89%be" class="header-anchor"&gt;&lt;/a&gt;包含查找
&lt;/h3&gt;&lt;p&gt;如果想知道某个元素是在 rangeSet 中哪个范围里面，可以这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;integerRange&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rangeContaining&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;17&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;integerRange&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//输出 (15‥20)，因为 17 被包含在 (15‥20) 中，所以输出这个范围。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果想知道某个范围是否包含在 rangeSet 的范围中，可以这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;encloses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encloses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closedOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;20&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encloses&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//true. 因为范围 (18,20) 包含在范围 (15,20) 中&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;encloses&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encloses&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closedOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;20&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encloses&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//false. 因为范围 (5,20) 不被 rangeSet 中任何范围包含。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="rangemap"&gt;&lt;a href="#rangemap" class="header-anchor"&gt;&lt;/a&gt;RangeMap
&lt;/h2&gt;&lt;p&gt;看到这个名字，聪明的你一定猜到了，它又是跟 Range 相关的，对，没错。&lt;/p&gt;
&lt;p&gt;RangeMap 是一种集合类型 ( collection type)，它将不相交、且不为空的 Range（key）映射给一个值（Value）。&lt;strong&gt;和 RangeSet 不一样，RangeMap 不可以将相邻的区间合并，即使这个区间映射的值是一样的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;举个例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RangeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TreeRangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;偏瘦&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;130&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;正常&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;111&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;正常 1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;150&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;偏胖&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;肥胖&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//正常 1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;103&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//[[90..100)=偏瘦，[100..101)=正常，[101..111]=正常 1, (111..130)=正常，[130..150)=偏胖，[150..180]=肥胖]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从输出中我们可以看到，rangeMap 中的每一段 range 都对应着一个 value&lt;/p&gt;
&lt;h3 id="treemap"&gt;&lt;a href="#treemap" class="header-anchor"&gt;&lt;/a&gt;TreeMap
&lt;/h3&gt;&lt;p&gt;通过上面的代码，我们能看到 TreeMap 的一些特性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TreeRangeMap 是 RangeMap 的一个实现，保证内部区间不重叠且有序（通过上面的代码能看出来）&lt;/li&gt;
&lt;li&gt;如果 TreeRangeMap 要插入的区间与 TreeRangeMap 已保存的区间发生重叠，那么 TreeRangeMap 会对之前的区间切割，保留当前插入区间的完整性&lt;/li&gt;
&lt;li&gt;TreeRangeMap 虽然以区间作为键，但 get 方法却以单个值 K 作为参数。此时，TreeRangeMap 会先查找这个 K 对应的区间，然后返回这个区间对应的值&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="remove"&gt;&lt;a href="#remove" class="header-anchor"&gt;&lt;/a&gt;remove
&lt;/h3&gt;&lt;p&gt;remove 方法用来切割 TreeRangeMap 中的键区间&lt;/p&gt;
&lt;p&gt;1）如果 TreeRangeMap 中的某个区间没有被完全删除，那么这个区间只是被切割小，但还是存在于 TreeRangeMap 中 2）如果 TreeRangeMap 中的某个区间被完全删除，那么这个区间和对应的值都被删除掉&lt;/p&gt;
&lt;h3 id="subrange"&gt;&lt;a href="#subrange" class="header-anchor"&gt;&lt;/a&gt;subRange
&lt;/h3&gt;&lt;p&gt;和 RangeSet 不一样，RangeMap 没有提供 complement()、contains()、rangeContaining() 以及 encloses() 方法。&lt;/p&gt;
&lt;p&gt;但提供了 subRange ，可以获取一个子区间。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RangeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TreeRangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;偏瘦&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;130&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;正常&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;111&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;正常 1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;150&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;偏胖&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;肥胖&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RangeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subRangeMap1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subRangeMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;80&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RangeMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;subRangeMap2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subRangeMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;closedOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;93&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;150&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subRangeMap1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subRangeMap2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出结果：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;{[93..100)=偏瘦，[100..101)=正常，[101..111]=正常 1, (111..130)=正常，[130..150)=偏胖}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从输出结果可以看出，如果要划出的子 Range 和 RangeMap 没有交集，那么返回空，如果有，则返回所有的 Range。&lt;/p&gt;
&lt;p&gt;根据 subRange 的特性我想到了一个实用的场景：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;假设我们的业务是租赁业务（房子、车、录像带等等），比如租车，一辆车在一天的 24 小时内都可能被租走，如何根据用户预约的租赁时间快速判断一辆车在这个时间段是否被占用？&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;我们可以把已某辆车哪天的租赁时间段存入 RangeMap ，再调用 subRangeMap 传入预约时间段参数去看有没有交集，如果返回空表明这段时间没被占用，可以租，如果非空则证明有时间冲突不能租&lt;/strong&gt;。（当然你也可以自己写个贪心算法来解决）&lt;/p&gt;
&lt;h3 id="整体跨度"&gt;&lt;a href="#%e6%95%b4%e4%bd%93%e8%b7%a8%e5%ba%a6" class="header-anchor"&gt;&lt;/a&gt;整体跨度
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rangeMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;span&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lowerEndpoint&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//90&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;upperEndpoint&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;intValue&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;//180&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.baeldung.com/guava-rangemap" target="_blank" rel="noopener"
 &gt;https://www.baeldung.com/guava-rangemap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.baeldung.com/java-map-key-from-value" target="_blank" rel="noopener"
 &gt;https://www.baeldung.com/java-map-key-from-value&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.csdn.net/wypblog/article/details/9363861" target="_blank" rel="noopener"
 &gt;https://blog.csdn.net/wypblog/article/details/9363861&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/google/guava/wiki/NewCollectionTypesExplained" target="_blank" rel="noopener"
 &gt;https://github.com/google/guava/wiki/NewCollectionTypesExplained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://guava.dev/releases/23.0/api/docs/com/google/common/collect/Multimap.html" target="_blank" rel="noopener"
 &gt;https://guava.dev/releases/23.0/api/docs/com/google/common/collect/Multimap.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>