当前位置: 首页 > news >正文

浅显易懂HashMap的数据结构

HashMap 就像一个大仓库,里面有很多小柜子(数组),每个小柜子可以挂一串链条(链表),链条太长的时候会变成更高级的架子(红黑树)。下面用超简单的例子解释:


​壹. 排列方式

  • 数组:一排固定的小柜子(比如柜子0、柜子1、柜子2...)。
  • 链表:如果多个东西要放在同一个柜子里,它们会串成一条链子。
  • 红黑树:当某个柜子的链子太长(比如超过8个),链子会变成一个小架子(树结构),找东西更快。

​贰. 存数据的过程

假设我们要存一个键值对 ("name", "小明")

  1. 找柜子:先算 "name" 的哈希值(类似身份证号),比如得到柜子3。
  2. 放数据
    • 如果柜子3是空的,直接放进去。
    • 如果柜子3已经有东西,检查链条上的每个元素:
      • 如果有相同的键(比如已经有 "name"),替换它的值。
      • 如果没有,把新键值对挂到链子末尾。
  3. 链条转架子:如果链子长度超过8,就把链子变成红黑树架子。

​叁. 取数据的过程

假设要取 "name" 对应的值:

  1. 找柜子:算 "name" 的哈希值,确定是柜子3。
  2. 找数据
    • 如果柜子3是链子,遍历链子找 "name"
    • 如果柜子3是架子(红黑树),用树的快速查找方法。

​肆. 代码简化版(Java)​

// 小柜子(数组)
Node[] table = new Node[16];// 链表节点(如果链子太长,会变成 TreeNode)
class Node {String key;String value;Node next; // 下一个节点
}// 红黑树节点(架子结构)
class TreeNode extends Node {TreeNode parent;TreeNode left;TreeNode right;
}// 存数据示例
void put(String key, String value) {int hash = key.hashCode();int index = hash % table.length; // 计算柜子位置if (table[index] == null) {// 柜子是空的,直接放进去table[index] = new Node(key, value);} else {// 柜子非空,挂到链子末尾或更新值// 如果链子太长,转成红黑树}
}

​伍. 一句话总结

HashMap​ 的数组是主干,链表解决哈希冲突,红黑树解决链表过长时的性能问题!

陆. 源码:HashMap的putVal()方法

/*** Implements Map.put and related methods** @param hash hash for key* @param key the key* @param value the value to put* @param onlyIfAbsent if true, don't change existing value* @param evict if false, the table is in creation mode.* @return previous value, or null if none*/final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

柒、我们来拆解 ​​“哈希冲突时,链表如何挂到数组上”​​ 的详细过程,用大白话 + 例子解释:


1. 核心原理:用链表串联冲突的键值对

  • 当两个不同的键(比如 "name" 和 "age")通过哈希计算后,分配到 ​同一个数组位置(同一个柜子)​​ 时,就会发生 ​哈希冲突
  • 此时,HashMap 会用 ​链表​ 把这些冲突的键值对串起来,挂在数组的这个位置上(类似挂一串钥匙)。

2. 具体挂链表的步骤(逐行分析)​

假设数组的某个位置 index 已经有数据,现在要存入新的键值对 (key2, value2)

  1. 检查是否重复:遍历链表,对比每个节点的 key

    • 如果发现某个节点的 key 和 key2 ​相同​(用 equals() 判断),直接覆盖它的值。
    • 如果链表中没有相同的 key,则把新节点 ​挂到链表末尾​(Java 8 之后是尾插法)。
  2. 链表挂载示意图

    // 数组的某个位置 index 已经有一个节点 Node1
    table[index] = Node1(key1, value1) -> null;// 存入新节点 Node2(冲突)
    Node1.next = Node2(key2, value2); // 挂到链表尾部
    table[index] = Node1 -> Node2 -> null;
  3. 代码逻辑简化版

    Node existingNode = table[index]; // 获取数组当前位置的链表头// 遍历链表,检查是否已有相同的 key
    while (existingNode != null) {if (existingNode.key.equals(newKey)) {existingNode.value = newValue; // 覆盖旧值return;}existingNode = existingNode.next;
    }// 如果没有重复 key,把新节点挂到链表末尾
    Node newNode = new Node(newKey, newValue);
    newNode.next = table[index]; // Java 8 之前是头插法(新节点变成链表头)
    table[index] = newNode;      // Java 8 之后是尾插法(直接挂在链表尾部)

3. 关键细节:头插法 vs 尾插法

  • Java 8 之前:新节点插入链表头部(头插法)。
    • 问题:多线程下可能导致链表成环(死循环)。
    • 示例:table[index] = 新节点 -> 旧节点 -> null
  • Java 8 之后:新节点插入链表尾部(尾插法)。
    • 改进:避免多线程下的链表成环问题。
    • 示例:旧节点 -> 新节点 -> null

4. 链表转红黑树的条件

  • 当链表长度 ​超过 8,且 ​数组总长度 ≥ 64​ 时,链表会转换成红黑树。
  • 如果数组长度 < 64,即使链表长度 > 8,HashMap 也会优先选择 ​扩容数组​(而不是转树),因为扩容能直接减少哈希冲突的概率,成本更低。
  • 这正说明 HashMap 的设计是 ​先尝试扩容解决冲突,实在不行再转树。😄
    • 为什么这样设计?

      • 小数组时扩容更高效

        • 数组较小时,哈希冲突可能只是因为数组容量不足,直接扩容能快速缓解问题。
        • 红黑树结构复杂,维护成本高,小规模数据下不如扩容划算。
      • 大数组时优化查询

        • 数组足够大(≥64)后,如果仍有长链表,说明哈希冲突难以通过扩容解决(如多个 key 哈希值相同)。
        • 此时转为红黑树,将查询复杂度从 O(n) 降为 O(logn)

5. 完整流程示例

假设存入 ("name", "小明") 和 ("age", 18),它们的哈希值冲突(都映射到数组位置 3):

  1. 存入第一个节点

    table[3] = Node("name", "小明") -> null;
  2. 存入第二个节点(冲突)​

    // 检查链表,发现没有重复 key,挂到链表末尾
    table[3] = Node("name", "小明") -> Node("age", 18) -> null;
  3. 查找时:遍历链表,逐个对比 key,找到对应值。


​6.总结

  • 链表挂在数组上:通过节点的 next 指针串联冲突的键值对。
  • 插入位置:Java 8 之后用尾插法,避免多线程问题。
  • 转红黑树:链表过长时优化查找速度(从 O(n) 优化到 O(log n))。

捌、再帮你用 ​​“仓库管理员” 的比喻​ 总结一下,确保彻底理解:


终极傻瓜版总结

  1. 仓库(数组)​:管理员有一排柜子(比如16个),每个柜子有编号(0到15)。
  2. 存东西(键值对)​
    • 管理员用 ​哈希函数​(比如 key.hashCode() % 16)算出东西应该放哪个柜子。
    • 如果柜子空,直接放进去。
  3. 柜子冲突(哈希冲突)​
    • 如果柜子已经有东西,管理员会拿一根 ​链条(链表)​​ 把新东西和旧东西绑在一起,挂在柜子里。
    • 链条的挂法:新东西挂在链条的尾部(Java 8之后)。
  4. 链条太长怎么办
    • 如果链条长度超过8,管理员会把链条换成 ​高级货架(红黑树)​,这样找东西更快。
    • 但如果仓库的柜子总数太少(比如小于64个),管理员会直接 ​扩建仓库(扩容数组)​,而不是换货架。

关键逻辑图示

// 数组(柜子列表)
Node[] table = [柜子0, 柜子1, ..., 柜子15];// 柜子里的内容(链表或树)
柜子3 -> Node1("name", "小明") -> Node2("age", 18) -> null  // 链表形式
柜子5 -> TreeNode("id", 1001)  // 树形式(如果链表转成了红黑树)

容易混淆的点

  1. 覆盖值:如果两次存同一个 key(比如两次存 "name"),会直接覆盖旧值。
  2. 哈希函数hashCode() 决定柜子位置,但不同对象可能算出相同的哈希值(冲突)。
  3. 扩容:当仓库的柜子被填满超过一定比例(默认75%),管理员会扩建仓库(数组长度翻倍),重新分配所有东西的位置。

玖、结合 : 面试被问 Java中hashmap数据结构 

        URL: 地基:Java中hashmap数据结构(面试被问)-CSDN博客

兄弟们,理解好了。rt.jar包里的hashmap源码的putval方法的方式,有厉害的同学可以学以致用哈!向大家致敬!

(望各位潘安、各位子健/各位彦祖、于晏不吝赐教!多多指正!🙏)

-----分界线----------------------------------------------------------------------------------------------------

兄弟们,上面的足以应付面试了,如何还想更深入,可以看下面的。

拾. 为什么数组长度 ≥64 时不扩容,而是转树?

​1 扩容的局限性**

  • 扩容的本质:通过扩大数组长度,将节点分散到新数组中,减少哈希冲突。
  • 局限性:如果多个键的哈希值本身就冲突(例如哈希算法导致碰撞),即使扩容也无法分散它们。
    // 例如:键A和键B的哈希值不同,但取模后仍落在同一个桶
    hash(A) % 64 = 5;
    hash(B) % 64 = 5;  // 即使数组扩容到128,依然可能 hash(A) % 128 = 5,hash(B) % 128 = 5

​2) 转树的优势**

  • 解决哈希碰撞:当哈希冲突无法通过扩容避免时(如多个键哈希值相同),红黑树将查询复杂度从链表O(n)降到O(logn)
  • 成本权衡
    • 扩容成本高:需新建数组、重新哈希所有节点(时间复杂度O(n))。
    • 转树成本低:只处理单个冲突严重的桶,其他桶不受影响。

​3) 阈值为什么是64?**

  • 经验值:基于测试和性能权衡:
    • 数组长度较小时(<64),哈希冲突可能因数组容量不足,优先扩容更高效。
    • 数组长度≥64时,认为哈希冲突更可能是哈希算法导致(而非数组容量问题),转树更合理。

4. 源码逻辑验证

HashMap的treeifyBin()方法中会先检查数组长度是否≥64,再决定转树或扩容:

// HashMap 源码片段
final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) { // MIN_TREEIFY_CAPACITY=64resize(); // 数组长度<64时,选择扩容} else {// 数组长度≥64时,将链表转为红黑树TreeNode<K,V> hd = null, tl = null;// ...(具体转树逻辑)}
}

5. 举例说明

场景1:数组长度=16,链表长度=9
  • 数组长度16 < 64 → ​触发扩容​(数组扩大到32),而不是转树。
场景2:数组长度=64,链表长度=9
  • 数组长度≥64 → ​链表转为红黑树,不再扩容。

6、​总结

  • 转树的条件:链表长度>8 ​​ 数组长度≥64。
  • 转树的目的:针对哈希算法导致的不可分散冲突,用空间换时间(红黑树优化查询)。
  • 扩容的优先级:数组较小时,扩容是更经济的解决方案。

拾壹、数组扩容是否重新计算哈希值?

在 HashMap 中,当数组长度小于 64 时触发扩容,哈希值本身不会重新计算,但元素在数组中的位置(索引)会根据新的数组长度重新确定。以下是具体机制:


1. 哈希值如何生成?

  • 哈希值来源
    HashMap 中每个键(Key)的哈希值由以下两步生成:

    1. 调用键对象的 hashCode() 方法,得到原始哈希值。
    2. 通过 ​扰动函数​(位运算)对原始哈希值进行二次处理,增加随机性,减少哈希冲突。
      // HashMap 的扰动函数实现
      static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      }
  • 哈希值的存储
    这个最终哈希值会在键值对被插入 HashMap 时计算一次,并存储在 Node 或 TreeNode 对象中,后续不再重新计算


2. 扩容时如何确定新位置?

当数组长度从 oldCap(例如 16)扩容到 newCap(例如 32)时,元素的索引位置会按以下规则重新分配:

  1. 索引计算公式
    新索引 = hash & (newCap - 1)
    newCap - 1 是新的掩码,例如 32 → 0b11111)。

  2. 优化计算技巧
    由于扩容是翻倍(newCap = oldCap << 1),新掩码比旧掩码多出一个高位 1(例如 16 → 0b1111,32 → 0b11111)。
    因此,新索引只需判断哈希值中新增的高位是 0 还是 1

    • 如果高位是 0:新索引 = ​原位置
    • 如果高位是 1:新索引 = ​原位置 + oldCap

    源码逻辑

    // 遍历旧数组中的每个桶
    for (int j = 0; j < oldCap; j++) {Node<K,V> e;if ((e = oldTab[j]) != null) {// 检查哈希值高位是 0 还是 1if ((e.hash & oldCap) == 0) {// 高位为 0 → 留在原位置(j)} else {// 高位为 1 → 移动到新位置(j + oldCap)}}
    }

3. 示例说明

假设原数组长度 oldCap = 16(二进制 0b10000),扩容后 newCap = 32(二进制 0b100000)。

  • 键的哈希值:假设为 0b10101
  • 旧索引计算
    hash & (oldCap - 1) = 0b10101 & 0b1111 = 0b00101 → 索引 5。
  • 新索引计算
    hash & (newCap - 1) = 0b10101 & 0b11111 = 0b10101 → 索引 21。
    或者直接通过高位判断:
    hash & oldCap = 0b10101 & 0b10000 = 0b10000 ≠ 0 → 高位为 1,新索引 = 5 + 16 = 21。

4. 为什么哈希值不重新计算?

  • 性能优化:哈希值的计算涉及 hashCode() 方法和扰动函数,重新计算会带来额外开销。
  • 一致性保障:哈希值在键的生命周期内应保持不变(除非键对象被修改,但这违反 HashMap 的设计前提)。

5、​总结

  • 哈希值固定:在键插入时计算一次,后续不再变更。
  • 索引重新分配:扩容时利用原哈希值的高位判断新位置,无需重新计算哈希值。
  • 设计目标:以最小成本重新分布元素,同时减少哈希冲突。

兄弟辛苦,🙇‍♂️。 点烟!

(望各位潘安、各位子健/各位彦祖、于晏不吝赐教!多多指正!🙏)

相关文章:

浅显易懂HashMap的数据结构

HashMap 就像一个大仓库&#xff0c;里面有很多小柜子&#xff08;数组&#xff09;&#xff0c;每个小柜子可以挂一串链条&#xff08;链表&#xff09;&#xff0c;链条太长的时候会变成更高级的架子&#xff08;红黑树&#xff09;。下面用超简单的例子解释&#xff1a; ​壹…...

Fisher信息矩阵与Hessian矩阵:区别与联系全解析

Fisher信息矩阵与Hessian矩阵&#xff1a;区别与联系全解析 在统计学和机器学习中&#xff0c;Fisher信息矩阵&#xff08;FIM&#xff09;和Hessian矩阵是两个经常出现的概念&#xff0c;它们都与“二阶信息”有关&#xff0c;常用来描述函数的曲率或参数的敏感性。你可能听说…...

【HTML— 快速入门】HTML 基础

准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器&#xff0c;我们在写前端代码时&#xff0c;使用 Sublime Text 打开比使用记事本打开&#xff0c;得到的代码体验更好&#xff0c;比 vscode…...

Docker 与 Serverless(无服务器架构)

Serverless&#xff08;无服务器架构&#xff09; 是一种新的云计算架构&#xff0c;它通过让开发者专注于业务逻辑而无需管理服务器基础设施&#xff0c;来简化应用的开发和部署。Serverless 模型通常由云服务提供商管理基础设施的所有方面&#xff0c;而开发者只需提供代码和…...

DMA 定制固件教程:小白跟做即得单人固件,超详细纯喂饭教程,100% 成功秘籍!FPGA仿真1:1、中断逻辑和TLP核心都在。

DMA 定制固件教程 小白跟着操作做可以做出的单人固件 图文教程 链接&#xff1a;https://docs.qq.com/doc/DQ01lVGtHelROVHNv 本图文教程包含内容&#xff1a; 一、DMA仿真技术采集真实单人固件 二、网卡TLP仿真固件生成 三、DMA仿真技术io、中断逻辑&#xff0c;从零仿真 四、…...

嵌入式开发:傅里叶变换(4):在 STM32上面实现FFT(基于STM32L071KZT6 HAL库+DSP库)

目录 步骤 1&#xff1a;准备工作 步骤 2&#xff1a;创建 Keil 项目&#xff0c;并配置工程 步骤 3&#xff1a;在MDK工程上添加 CMSIS-DSP 库 步骤 5&#xff1a;编写代码 步骤 6&#xff1a;配置时钟和优化 步骤 7&#xff1a;调试与验证 步骤 8&#xff1a;优化和调…...

飞鱼科技游戏策划岗内推

协助策划完成相关工作&#xff0c;包括但不仅限于策划配置&#xff0c;资料搜集&#xff0c;游戏体验&#xff1b; 游戏策划相关作品&#xff1b;游戏大赛经历&#xff1b;游戏demo制作经历&#xff1b;游戏公司策划岗位实习经历优先 内推码 DSZP7YFU...

前端如何进行性能优化

1. 减少HTTP请求 合并文件&#xff1a;将多个CSS或JavaScript文件合并为一个&#xff0c;减少请求次数。 使用CSS Sprites&#xff1a;将多个小图标合并为一张大图&#xff0c;通过背景定位显示。 内联小资源&#xff1a;将小的CSS或JavaScript直接嵌入HTML中&#xff0c;减少…...

大白话Vuex 核心概念(state、mutations、actions)的使用案例与原理

大白话Vuex 核心概念&#xff08;state、mutations、actions&#xff09;的使用案例与原理 Vuex是Vue.js应用程序中专门用来管理状态的工具&#xff0c;就好像是一个大管家&#xff0c;帮你把项目里一些重要的数据和操作管理得井井有条。下面用大白话结合案例来介绍Vuex核心概…...

阿里云ack的创建与实战应用案例

阿里云ack的创建与应用案例 创建前开通ack相关服务&#xff1a;开始创建简单的魔方游戏&#xff0c;熟悉sv与clb自动注册创建部署一个nginx 服务示例&#xff1a;走不同域名访问不同svc资源&#xff1a;为什么需要 Ingress &#xff1f;创建第一个域名的 Deployment和Service。…...

鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar)

鸿蒙开发深入浅出01&#xff08;基本环境搭建、页面模板与TabBar&#xff09; 1、效果展示2、下载 DevEco Studio3、创建项目4、新建页面模板5、更改应用信息6、新建以下页面7、Index.ets8、真机运行9、图片资源文件 1、效果展示 2、下载 DevEco Studio 访问官网根据自己的版本…...

Vue组件:从使用到原理的深度解析

一、什么是Vue组件&#xff1f; 组件是Vue的核心特性之一&#xff0c;它允许开发者将UI拆分为独立可复用的代码片段。每个组件本质上是一个Vue实例&#xff0c;具有自己的&#xff1a; 模板&#xff08;Template&#xff09; 数据&#xff08;Data&#xff09; 方法&#xf…...

Docker部署 MongoDB及常用命令

docker 部署 docker run -d \--name mongo \-e MONGO_INITDB_ROOT_USERNAMEroot \-e MONGO_INITDB_ROOT_PASSWORD123456 \-p 27017:27017 \-v ./local-mongo-data:/data/db \mongo:latest或者编写 docker-compose.yaml 文件。如下&#xff1a; version: 3.1services:mongo:im…...

27.[前端开发-JavaScript基础]Day04-函数基本使用-递归-变量作用域-函数式编程

一、JavaScript函数 1 认识JavaScript函数 程序中的foo、bar、baz 认识函数 函数使用的步骤 2 函数的声明和调用 声明和调用函数 函数的参数 有参数的函数练习 函数的返回值 函数的练习 arguments参数&#xff08;JS高级再学习&#xff09; 3 函数的递归调用 函数中调用函数…...

mac修改docker的daemon.json 镜像文件

1、找到daemon.json文件的位置 docker info 可以看出位置在&#xff1a; /Users/spuer/.docker 2. 进入daemon.json 所在的目录&#xff1a; cd /Users/spuer/.docker3. 查看daemon.json的内容&#xff1a; more daemon.json可以看出&#xff0c;没有配置registry-mirrors&…...

KIMI K1.5:大规模强化学习在大语言模型中的应用与工程实践

目录 1、核心技术创新:长上下文强化学习 2、策略优化的技术细节 2.1、在线镜像下降变体 2.2、长度惩罚机制 2.3、智能采样策略 3、工程架构创新 3.1、混合部署框架 3.2、代码沙箱与奖励模型 3.3、分布式系统架构 4、实验成果与性能提升 5、结论与未来展望 大语言模…...

seacms v9 实现的MySQL注入

目录 过滤关键词information_schema 怎么办 一、环境搭建 二、环境分析 三、源代码分析 1、过滤程序 2、注入点 四、获取数据库名 五、获取数据库表名 六、获取表的列名 七、获取数据信息 过滤关键词information_schema 怎么办 1.、利用sys数据库&#xff08;MySQL 5.…...

Go中slice和map引用传递误区

背景 关于slice和map是指传递还是引用传递&#xff0c;很多文章都分析得模棱两可&#xff0c;其实在Go中只有值传递&#xff0c;但是很多情况下是因为分不清slice和map的底层实现&#xff0c;所以导致很多人在这一块产生疑惑&#xff0c;下面通过代码案例分析slice和map到底是…...

C# Unity 唐老狮 No.2 模拟面试题

本文章不作任何商业用途 仅作学习与交流 安利唐老狮与其他老师合作的网站,内有大量免费资源和优质付费资源,我入门就是看唐老师的课程 打好坚实的基础非常非常重要: Unity课程 - 游习堂 - 唐老狮创立的游戏开发在线学习平台 - Powered By EduSoho 如果你发现了文章内特殊的字体…...

【Uniapp-Vue3】开发userStore用户所需的相关操作

在项目根路径下创建的stores文件夹中创建user.js文件 并将以下内容复制到user.js中 import {ref} from "vue" import { defineStore } from pinia; const uniIdCo uniCloud.importObject("uni-id-co") const db uniCloud.database(); const usersTable…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...