原文标题:Why doesn't Rust's BTreeMap have a with_capacity() method?


原文链接:https://www.nicolas-hahn.com/2020/11/30/btreemap-with-capacity/

公众号: Rust 碎碎念


翻译 by: Praying

声明:我发现这里已经有一篇解释,但是我认为它有点不太好理解,所以我希望我写的这篇文章能够更容易理解一些。

Rust 的 HashMap(以及 HashSet 和 Vec)集合都提供了一个初始化方法—— fn with_capacity(capacity: usize),该方法预先分配足够的内存空间以存储capacity个元素。为什么 BTreeMap(和 BTreeSet)没有这个方法呢?

答案就在于这两个结构体在内存中布局的不同。简而言之,HashMap,就像 Vec,使用了一个 array(一个连续的内存块),要求在 O(1)的时间内插通过索引插入和查找元素。在 Vec 中,这很明显,但是在 HashMap 中,key 是被 hash 之后转为 value 在数组中的索引。

让我们来看一个已经存入四条记录的 HashMap(简单起见,我打算忽略真实的实现细节,比如 hash 碰撞时的装桶(bucket))。它在本质上来讲是一个拥有四个元素的数组。下面是一个表示存有三条记录的 HashMap 的内存表示(每个格子为一个字节),以及若干个方格(亮绿色是内存中被填充的字节,深绿色是空的,但是被结构体保留)。

我们插入两个元素。现在我们需要分类更多内存以存放第五个元素。常见的实现是将数组的大小翻倍(以便于我们不必在每次插入时都进行分配)。在理想情况下,我们可以直接使用内存中接下来的四个字节。

(事实上,元素是不可能像这样被连续存放的,因为 hasher 会以近似随机分布的方式输入一个数组的索引)。

尽管如此,如果接下来的四个字节已经被分配给其他的结构体了会怎么样呢?


在这种情况下,我们需要把整个 HashMap 移动到内存中的某个可以容下八条记录的位置。不同于额外分配四个字节 ,这次我们需要先分配八个字节(将数据拷贝过去),然后析构原来的四个字节,这个开销就比较高了。


这里就是with_capacity()出现的原因。如果我们预先知道我们至少会有五个元素,那么预先分配八个字节就能让我们不必反复析构和重分配,这也是with_capacity()所做的事情。

那么 BTreeMap 为什么没有这个方法呢?来看一下BTree 是如何工作的。在下面这个例子中,我打算把它简化为一个普通的二分查找树。它们俩之间的本质区别在于,BST(二分查找树)的每个节点有一个值和两个指针,但是一个 BTree 的每个节点拥有一组值和一组指针:

这里为了便于上面的解释,它们暂时可以被视作等同。

BST 的每个节点由一个值和两个分别指向左右子节点的指针组成。下面是一个只有一个节点和值的BTreeMap(亮蓝色)。第二个和第三个暗蓝色的字节被保留用于指向子节点的指针,目前是空的。

当一个元素被插入时,一个新节点会被创建并且会分配属于它的内存。因为指针可以指向内存中的任意地址,所以不必要求节点像 HashMap 那样在内存中存储为连续的字节。如果我们打算插入一条新记录,会如下图所示:


我们可以把这条新记录放在内存中任意拥有三个字节的自由空间的位置。一个 BTreeMap 可以遍布在程序的内存各处,因为我们不必把记录连续存放。这意味着,我们将从不需要析构和重分配空间以拷贝记录(元素),所以我们不会在 BTreeMap 初始化时通过预先分配额外的内存空间来节省某些环节(在整个程序运行时)。

如果你明确想要预先分配以节省插入过程的时间,或者如果这时的延迟代价很大, BTreeMap::with_capacity()或许会有意义。但我想这种用例对于标准库函数而言过于特殊。在有用(usefulness)和臃肿之间存在一个微妙的平衡。

欢迎关注公众号:Rust碎碎念,获取更多好文章

【译】为什么Rust中的BTreeMap没有with_capacity()方法?的更多相关文章

  1. 【译】Rust中的array、vector和slice

    原文链接:https://hashrust.com/blog/arrays-vectors-and-slices-in-rust/ 原文标题:Arrays, vectors and slices in ...

  2. Rust中的结构体及方法语法

    这个可以和类作比较,或是go当中的方法比较. #[derive(Debug)] struct Rectangle { width: u32, height: u32, } impl Rectangle ...

  3. 【译】理解Rust中的闭包

    原文标题:Understanding Closures in Rust 原文链接:https://medium.com/swlh/understanding-closures-in-rust-21f2 ...

  4. 【译】理解Rust中的局部移动

    原文标题:Understanding Partial Moves in Rust 原文链接:https://whileydave.com/2020/11/30/understanding-partia ...

  5. 【译】理解Rust中的Futures (一)

    原文标题:Understanding Futures In Rust -- Part 1 原文链接:https://www.viget.com/articles/understanding-futur ...

  6. 【译】理解Rust中的Futures(二)

    原文标题:Understanding Futures in Rust -- Part 2 原文链接:https://www.viget.com/articles/understanding-futur ...

  7. 【译】深入理解Rust中的生命周期

    原文标题:Understanding Rust Lifetimes 原文链接:https://medium.com/nearprotocol/understanding-rust-lifetimes- ...

  8. Rust初步(四):在rust中处理时间

    这个看起来是一个很小的问题,我们如果是在.NET里面的话,很简单地可以直接使用System.DateTime.Now获取到当前时间,还可以进行各种不同的计算或者输出.但是这样一个问题,在rust里面, ...

  9. 译:DOM2中的高级事件处理(转)

    17.2. DOM2中的高级事件处理(Advanced Event Handling with DOM Level 2)        译自:JavaScript: The Definitive Gu ...

随机推荐

  1. 理解 Linux 的硬链接与软链接(转)

    Linux 的文件与目录 现代操作系统为解决信息能独立于进程之外被长期存储引入了文件,文件作为进程创建信息的逻辑单元可被多个进程并发使用.在 UNIX 系统中,操作系统为磁盘上的文本与图像.鼠标与键盘 ...

  2. JWT鉴权

    一.HTTP基本认证 Basic Authentication--当浏览器访问使用基本认证的网站的时候, 浏览器会提示你输入用户名和密码. http auth的过程: 客户端发送http请求 服务器发 ...

  3. 自动化测试_移动端测试(二)—— Appium原理

    一.什么是Appium Appium是一个开源.跨平台的测试框架,可以用来测试原生及混合的移动端应用.Appium支持IOS.Android及FirefoxOS平台.Appium使用WebDriver ...

  4. MyBatis学习01

    1.初识MyBatis 环境说明: jdk 8 + MySQL 5.7.19 maven-3.6.1 IDEA 学习前需要掌握: JDBC MySQL Java 基础 Maven Junit 什么是M ...

  5. Xrepo:一个现代化的跨平台 C/C++ 包管理器

    xrepo 是一个基于 Xmake 的跨平台 C/C++ 包管理器. 项目源码 官方文档 它基于 xmake 提供的运行时,但却是一个完整独立的包管理程序,相比 vcpkg/homebrew 此类包管 ...

  6. Folx中与下载相关的参数如何设置

    Folx是一款简单易用,功能强大的MacOS专用下载管理工具.要使Folx下载/上传速度快,同时又不影响其他软件的上网使用,还能够有计划地安排下载,那么就必须对Folx进行参数设置.接下来小编详细讲解 ...

  7. 粉丝少的UP主如何赚大钱

    常逛B站的小伙伴应该知道,B站官方经常会推出各类征稿活动,奖金池也非常高,少则几万,多则上百万,可以说非常受UP主们的欢迎. 图1:B站各类活动 要知道,除了少数头部UP主可能因为没(有)有(钱)看( ...

  8. python批量爬取猫咪图片

    不多说直接上代码 首先需要安装需要的库,安装命令如下 pip install BeautifulSoup pip install requests pip install urllib pip ins ...

  9. Centos7安装vscode

    CentOS7 安装vscode                              最近在Linux环境下写几个程序时发现用vim时总出现一点问题,配置了vim也还是不太习惯,因此就安装了vs ...

  10. 《HelloGitHub》第 56 期

    兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ...