在我们之前的Rust文章中,我们探讨了Rust是否是一种OOP语言。在这篇文章中,我们将深入探讨Rust语言的一些标准库,简称std,即HashMap和BTreeMap,以及插入和删除等基本操作。
目录。
- 什么是地图?
- 树、二叉树和BST
- 什么是哈希?
- Rust中的BTreeMap
- Rust中的HashMap
让我们开始了解Rust集合。HashMap和BTreeMap。
什么是地图?
让我们先来定义一下什么是 "地图"。在一个非常广泛和一般的意义上,映射意味着将一组项目以某种方式关联到另一组。如果你熟悉一些基本的微积分,这正是一个函数的含义。
比如说。
F(x) = x + 1
这个函数 "映射 "或将X值与通常被称为Y值的东西联系起来。X的每一个值都对应着Y的一个值。
在编程中,字典是地图的一个很好的例子。你把键和值联系起来。
树、二进制树和BST
树
如上图所示,树是一种数据结构。每个节点都包含数据和一个键。第一个节点被称为 "根 "节点。从这里开始,其余的节点都是分支。作为同一父节点的子女的节点是兄弟姐妹。没有子节点的节点被称为叶节点,位于特定分支的末端。正如你所看到的,它被称为树的原因是显而易见的。
二叉树
二叉树是基于Tree数据结构的另一种数据结构。主要也是唯一的区别是,它只允许每个节点有两个孩子。在上面的例子中,节点E、F和G都属于L节点。这就是3个孩子,意味着它不是一个二叉树。下面是二进制树的表示方法。
二进制搜索树(BST)
二进制搜索树是一种特殊的二进制树,在根节点之后,每一个加入的节点都是有序的。低值在左边,高值在右边。而且每个节点的模式都会重复,直到数值被插入到正确的位置。花点时间看看下面的图片,找到这个模式。
BT树
BT树具有二进制搜索树的所有优点,但没有每个节点只允许有两个孩子的限制。比如说...

什么是哈希?
这篇文章并不涉及密码学和散列的来龙去脉,但是我们需要谈一谈什么是散列以及它的作用,以便在HashMaps中应用这些基本知识。
从本质上讲,哈希是一种密码学形式。在密码学中,有两种加密的方式。一种是单向加密,另一种是双向加密。这是什么意思?很简单!单向加密意味着数据可以被加密,但不能被解密。双向意味着你可以对数据进行来回加密和解密。
所以......加密和解密很容易理解。你对一个信息进行编码,以将其发送给某人,并避免任何人窥探能够阅读它。然后,接收者解密它并获得信息。所以你可能会问,我为什么要用一种非常难以解密的方式来加密?这个问题很有道理!答案很简单。有时我们不需要知道信息是什么。我们只需要知道它是正确的。最好的例子是密码存储。
在过去,密码是以简单的文本存储的。意思是说,你注册了一个网站,你的密码按原样进入了一个数据库。你怎么能知道这种情况发生过,或者这种情况是否还在发生?(希望不会)。如果你试图从网站上恢复你的密码,并且他们能够将你的密码以纯文本形式发送给你......很可能他们将密码原封不动地保存在数据库中。
哈希值就是为了解决这个问题。散列是一个单向函数,它接受一个输入(根据散列函数,可选择添加一些 "盐 "或随机性)并返回一个加密的文本。这有几个有趣的特点(如果散列函数是精心设计的)。
-
非常非常难以逆转或猜测哈希值。意思是说,你需要猜测成千上万的输入,如果你知道是哪一个,就通过哈希函数得到它们,然后比较产生的哈希。这是很慢的,而且如果哈希值足够好,这根本不可行。(有些哈希值会比到宇宙死亡的时间还要长。那是一个漫长的计算过程!)
-
改变原始密码中的一个字符就会产生一个完全不同的哈希值。也就是说,"Hello "和 "hello "会产生完全不同的哈希值,而不仅仅是其中一部分的变化。
-
相同的输入将总是输出相同的哈希值。所以在注册后,网站会存储你的哈希值,而不是你的密码。当你试图再次登录时,网站需要做的就是通过哈希函数运行你的输入文本,并将哈希值与存储的哈希值进行比较。这样,他们就可以在不知道文本的情况下验证文本是否相同。很好吧?
-
如果哈希函数是精心设计的,就很难找到两个不同的输入产生相同的哈希值。这有可能吗?是的,是的,它是
-
在运行散列函数之前,你一般无法预测散列值会是什么。意味着你不能仅仅通过阅读输入来手动计算出哈希值。
所有这些好处都取决于散列函数。如果处理输入的哈希值的函数太简单,它就很容易被发现,被破坏,然后你就会被攻击者所摆布。
嘿嘿,这花了不少时间。我希望你还在听我说,因为理论部分已经结束了!那么...
说完了这些...
锈蚀时间!
值得庆幸的是,Rust已经将这些数据结构和功能中的一些模块实现了,这些模块在std库中都是现成的!
Rust中的BTreeMap
为了将BTreeMap数据结构带入你的程序范围,你只需要在你的文件顶部添加以下内容。
use std::collections::BTreeMap;
一旦我们将BTreeMap带入范围...全部完成!我们就可以开始自由使用它了。
BTreeMap被定义为。
let mut rpg_party = BTreeMap::new();
mut用于使BTreeMap可变,new()用于定义一个新对象。
元素的插入方式为。
rpg_party.insert("mage", "healthy");
我们可以检查一个元素是否存在。
rpg_party.contains_key("druid")
一个元素的删除方式为:。
rpg_party.remove("bard");
Rust中涉及BTreeMap的完整实现是。
// type inference lets us omit an explicit type signature (which
// would be `BTreeMap<&str, &str>` in this example).
let mut rpg_party = BTreeMap::new();
// First we insert some keys and values...
rpg_party.insert("mage", "healthy");
rpg_party.insert("warrior", "healthy");
rpg_party.insert("rogue", "healthy");
rpg_party.insert("bard", "healthy");
// Do we have a Druid? Let's ask our BTree!
if !rpg_party.contains_key("druid") {
println!("You're missing a druid! You might want to look for one in the next tavern.");
}
// How big is our party?
println!("Your party size right now is: {}.", rpg_party.len());
// Let's remove one of our party members...
println!("Oh no! Your bard got drunk and is now incapable of coming with you any longer.");
rpg_party.remove("bard");
// How big is our Party now?
println!("Now your party size is: {}.", rpg_party.len());
println!("--------------------"); // Just for formatting reasons!
// Let's modify one of our values, we got ambushed!
println!("You've been Ambushed! Your warrior tanks some damage!");
for (key, value) in &mut rpg_party {
if *key == "warrior" {
*value = "wounded";
println!("{} is now {}", key, value);
}
}
println!("--------------------"); // Just for formatting reasons!
// Let's display our current Party status
for (key, value) in &rpg_party {
println!("{}'s status is currently: {}", key, value);
}
Rust中的HashMap
按照同样的思路,为了带来HashMap的结构和功能,你在文件的顶部添加以下内容
use std::collections::HashMap;
一个HashMap对象被定义为:。
let mut rpg_party = HashMap::new();
一个元素的插入方式是
rpg_party.insert("mage", "healthy");
一个元素的删除方式为:
rpg_party.remove("bard");
通过这个完整的Rust实现来了解所有的操作。
// Type inference lets us omit an explicit type signature (which
// would be `HashMap<String, String>` in this example).
let mut rpg_party = HashMap::new();
// First we insert some keys and values...
rpg_party.insert("mage", "healthy");
rpg_party.insert("warrior", "healthy");
rpg_party.insert("rogue", "healthy");
rpg_party.insert("bard", "healthy");
// Do we have a Druid? Let's ask our BTree!
if !rpg_party.contains_key("druid") {
println!("You're missing a druid! You might want to look for one in the next tavern.");
}
// How big is our party?
println!("Your party size right now is: {}.", rpg_party.len());
// Let's remove one of our party members...
println!("Oh no! Your bard got drunk and is now incapable of coming with you any longer.");
rpg_party.remove("bard");
// How big is our Party now?
println!("Now your party size is: {}.", rpg_party.len());
println!("--------------------"); // Just for formatting reasons!
// Let's modify one of our values, we got ambushed!
println!("You've been Ambushed! Your warrior tanks some damage!");
for (key, value) in &mut rpg_party {
if *key == "warrior" {
*value = "wounded";
println!("{} is now {}", key, value);
}
}
println!("--------------------"); // Just for formatting reasons!
// Let's display our current Party status
for (key, value) in &rpg_party {
println!("{}'s status is currently: {}", key, value);
}
下面是两个代码段的输出。
正如你所看到的,两个代码片断基本上是一样的,只是结构不同。所以你可能会问,我为什么要选择一个而不是另一个?
极其简化...如果你的键有一定的顺序,就用BTreeMap。如果没有,而你只是想要一个地图。使用HashMap!
如果你已经走到了这一步...
祝贺你!自从我们开始以来,我们已经走过了一段相当长的路程。这些数据结构有很多用途,但请始终记住。没有适合所有的尺寸。始终注意你的数据结构工具带,为手头的问题选择合适的工具。
参考资料。
关于BTreeMap文档的直接链接,请查看Rust文档的指南。
关于HashMap文档的直接链接,请查看Rust文档指南。
谢谢你的阅读,我希望能在我们的Rusty之旅的下一个目的地见到你!