修改HashMap章的一些表述

pull/36/head
murphy 8 years ago
parent a92d0401a8
commit f92f141762

@ -4,7 +4,7 @@
> <br>
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
最后介绍的常用集合类型是**哈希 map***hash map*)。`HashMap<K, V>`类型储存了一个键类型`K`对应一个值类型`V`的映射。它通过一个**哈希函数***hashing function*)来实现映射,决定如何将键和值放入内存中。很多编程语言支持这种数据结构不过通常有不同的名字哈希、map、对象、哈希表或者关联数组仅举几例。
最后介绍的常用集合类型是 **哈希 map***hash map*)。`HashMap<K, V>` 类型储存了一个键类型 `K` 对应一个值类型 `V` 的映射。它通过一个**哈希函数***hashing function*来实现映射决定如何将键和值放入内存中。很多编程语言支持这种数据结构不过通常有不同的名字哈希、map、对象、哈希表或者关联数组仅举几例。
哈希 map 可以用于需要任何类型作为键来寻找数据的情况,而不是像 vector 那样通过索引。例如,在一个游戏中,你可以将每个团队的分数记录到哈希 map 中,其中键是队伍的名字而值是每个队伍的分数。给出一个队名,就能得到他们的得分。
@ -23,11 +23,10 @@ scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
```
注意必须首先`use`标准库中集合部分的`HashMap`。在这三个常用集合中,这个是最不常用的,所以并不包含在被 prelude 自动引用的功能中。标准库中对哈希 map 的支持也相对较少;例如,并没有内建的用于构建的宏。
注意必须首先 `use` 标准库中集合部分的 `HashMap`。在这三个常用集合中,`HashMap` 是最不常用的,所以并没有被 prelude 自动引用。标准库中对 `HashMap` 的支持也相对较少,例如,并没有内建的构建宏。
像 vector 一样,哈希 map 将他们的数据储存在堆上,这个 `HashMap` 的键类型是 `String` 而值类型是 `i32`。同样类似于 vector哈希 map 是同质的:所有的键必须是相同类型,值也必须都是相同类型。
就像 vector 一样,哈希 map 将他们的数据储存在堆上。这个`HashMap`的键类型是`String`而值类型是`i32`。同样类似于 vector哈希 map 是同质的:所有的键必须是相同类型,值也必须都是相同类型。
另一个构建哈希 map 的方法是使用一个元组的 vector 的`collect`方法,其中每个元组包含一个键值对。`collect`方法可以将数据收集进一系列的集合类型,包括`HashMap`。例如,如果队伍的名字和初始分数分别在两个 vector 中,可以使用`zip`方法来创建一个元组的 vector其中“Blue”与 10 是一对,依此类推。接着就可以使用`collect`方法将这个元组 vector 转换成一个`HashMap`
另一个构建哈希 map 的方法是使用一个元组的 vector 的 `collect` 方法,其中每个元组包含一个键值对。`collect` 方法可以将数据收集进一系列的集合类型,包括 `HashMap`。例如,如果队伍的名字和初始分数分别在两个 vector 中,可以使用 `zip` 方法来创建一个元组的 vector其中“Blue”与 10 是一对,依此类推。接着就可以使用 `collect` 方法将这个元组 vector 转换成一个 `HashMap`
```rust
use std::collections::HashMap;
@ -38,7 +37,7 @@ let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
```
这里`HashMap<_, _>`类型注解是必要的,因为可能`collect`进很多不同的数据结构,而除非显式指定 Rust 无从得知你需要的类型。但是对于键和值的参数来说,可以使用下划线而 Rust 可以根据 vector 中数据的类型推断出哈希 map 所包含的类型。
这里`HashMap<_, _>`类型注解是必要的,因为可能`collect`进很多不同的数据结构,而除非显式指定 Rust 无从得知你需要的类型。但是对于键和值的类型参数来说,可以使用下划线占位,而 Rust 能够根据 vector 中数据的类型推断出 `HashMap` 所包含的类型。
### 哈希 map 和所有权
@ -75,7 +74,7 @@ let team_name = String::from("Blue");
let score = scores.get(&team_name);
```
这里,`score`将会是与蓝队分数相关的值,而这个值将是`Some(10)`。因为`get`返回`Option<V>`所以结果被封装进`Some`;如果某个键在哈希 map 中没有对应的值,`get`会返回`None`。程序将需要采用第六章提到的方法中之一来处理`Option`
这里,`score` 将会是与蓝队分数相关的值,应为 `Some(10)`。因为 `get` 返回 `Option<V>`,所以结果被装进 `Some`;如果某个键在哈希 map 中没有对应的值,`get` 会返回 `None`。这时就要用某种第六章提到的方法来处理 `Option`
可以使用与 vector 类似的方式来遍历哈希 map 中的每一个键值对,也就是`for`循环:
@ -101,8 +100,7 @@ Blue: 10
### 更新哈希 map
虽然键值对的数量是可以增长的,不过每个单独的键同时只能关联一个值。当你想要改变哈希 map 中的数据时,必须选择是用新值替代旧值,还是完全无视旧值。我们也可以选择保留旧值而忽略新值,并只在键**没有**对应一个值时增加新值。或者可以结合新值和旧值。让我们看看着每一种方式是如何工作的!
尽管键值对的数量是可以增长的,不过任何时候,每个键只能关联一个值。当你想要改变哈希 map 中的数据时,根据目标键是否有值以及值的更新策略分成多种情况,下面我们了解一下:
#### 覆盖一个值
如果我们插入了一个键值对,接着用相同的键插入一个不同的值,与这个键相关联的旧值将被替换。即便下面的代码调用了两次`insert`,哈希 map 也只会包含一个键值对,因为两次都是对蓝队的键插入的值:
@ -160,11 +158,11 @@ for word in text.split_whitespace() {
println!("{:?}", map);
```
这会打印出`{"world": 2, "hello": 1, "wonderful": 1}``or_insert`方法事实上会返回这个键的值的一个可变引用(`&mut V`)。这里我们将这个可变引用储存在`count`变量中,所以为了赋值必须首先使用星号(`*`)解引用`count`。这个可变引用在`for`循环的结尾离开作用域,这样所有这些改变都是安全的并被借用规则所允许
这会打印出`{"world": 2, "hello": 1, "wonderful": 1}``or_insert`方法事实上会返回这个键的值的一个可变引用(`&mut V`)。这里我们将这个可变引用储存在`count`变量中,所以为了赋值必须首先使用星号(`*`)解引用`count`。这个可变引用在`for`循环的结尾离开作用域,这样所有这些改变都是安全的并符合借用规则
### 哈希函数
`HashMap`默认使用一个密码学上是安全的哈希函数它可以提供抵抗拒绝服务Denial of Service, DoS攻击的能力。这并不是现有最快的哈希函数不过为了更好的安全性带来一些性能下降也是值得的。如果你监控你的代码并发现默认哈希函数对你来说非常慢可以通过指定一个不同的 *hasher* 来切换为另一个函数。hasher 是一个实现了`BuildHasher` trait 的类型。第十章会讨论 trait 和如何实现他们。你并不需要从头开始实现你自己的 hashercrates.io 有其他人分享的实现了许多常用哈希算法的 hasher 的库。
`HashMap`默认使用一种密码学安全的哈希函数它可以抵抗拒绝服务Denial of Service, DoS攻击。然而并不是最快的不过为了更高的安全性值得付出一些性能的代价。如果性能监测显示此哈希函数非常慢以致于你无法接受你可以指定一个不同的 *hasher* 来切换为其它函数。hasher 是一个实现了`BuildHasher` trait 的类型。第十章会讨论 trait 和如何实现他们。你并不需要从头开始实现你自己的 hashercrates.io 有其他人分享的实现了许多常用哈希算法的 hasher 的库。
## 总结

Loading…
Cancel
Save