首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何简明地填写2D HashMap?

如何简明地填写2D HashMap?
EN

Stack Overflow用户
提问于 2017-03-23 18:02:32
回答 3查看 1.8K关注 0票数 7

有一个简单的方法来填充一个二维HashMap吗?在C++中,我可以这样做:

代码语言:javascript
复制
std::unordered_map<int,std::unordered_map<int,int>> 2dmap;
//...
2dmap[0][0] = 0;
2dmap[0][1] = 1;
//...

在我的锈蚀项目中,我试图填写一个类似的地图:

代码语言:javascript
复制
let mut map: HashMap<i32, HashMap<i32, i32>> = HashMap::new();
//...fill the hash map here.

我认为唯一能做到这一点的方法是构建每个子地图,然后将它们移动到超级地图中,如下所示:

代码语言:javascript
复制
let mut sub1: HashMap<i32, i32> = HashMap::new();
sub1.insert(0, 0);
sub1.insert(1, 1);
let mut map: HashMap<i32, HashMap<i32, i32>>::new();
map.insert(0, sub1);

有没有一种更简洁的方法来做到这一点?

上面的代码是我的实际用例的简化,它使用枚举作为HashMap的索引。

代码语言:javascript
复制
enum Example {
    Variant1,
    Variant2,
    //...
}

这些变体中没有一个保存值。我使用这个语法从我的HashMap查找

代码语言:javascript
复制
let value = map[Example::Variant1][Example::Variant2];
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-03-23 18:19:53

在铁锈里,元组的效果真的很好。您可能应该只使用HashMap<(i32, i32), i32>。然后,您将得到一些与C++代码非常接近的内容。

代码语言:javascript
复制
let mut map: HashMap<(i32, i32), i32> = HashMap::new();
sub1.insert((0, 0), 0);
sub1.insert((0, 1), 1);
// ...

当然,如果我们有一个像vec!这样的宏和RFC正在研制中。,那就太好了。使用这个答案中的宏,您可以编写:

代码语言:javascript
复制
let map = hashmap![(0,0) => 0, (0,1) => 1, ...];

如果使用枚举,则不会发生任何变化--只需确保为其派生了EqPartialEqHash

代码语言:javascript
复制
let mut sub1: HashMap<(Example, Example), i32> = HashMap::new();
sub1.insert((Example::Variant1, Example::Variant1), 0);
sub1.insert((Example::Variant1, Example::Variant2), 1);
// ...
票数 10
EN

Stack Overflow用户

发布于 2017-03-23 18:28:57

使用entry API:

代码语言:javascript
复制
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.entry(0).or_insert_with(HashMap::new).insert(0, 0);
    map.entry(0).or_insert_with(HashMap::new).insert(1, 1);

    println!("{:?}", map);
    println!("{}", map[&0][&1]);
}

与C++不同,构造嵌套的HashMap并不是隐式的--您必须非常清楚地知道,您希望允许创建一个新的HashMap

另一个答案不同,这保留了原始数据结构和基于初始键获取整个地图子集的能力:

代码语言:javascript
复制
println!("{:?}", map.get(&0));
println!("{:?}", map.get(&0).and_then(|m| m.get(&1)));

如果总是提供两个数值索引,那么元组是一个更好的解决方案,因为它更准确地建模了问题--只有一个真正的键,它恰好有两个组件。它还可能具有更好的数据局部性,因为有一个大内存块,而不是额外级别的指针追逐散列哈希。

票数 7
EN

Stack Overflow用户

发布于 2017-03-23 23:13:01

C++代码片段的简洁性归功于方便的operator [],如果映射中缺少该值,它将自动构造该值。虽然Rust在默认情况下不会这样做,但是很容易告诉它这样做,如Shepmaster的答案所示。

为了避免详细说明entry(key).or_insert_with(ValueType::new),可以将相当于C++的operator []的方法添加到Rust的HashMap中。毕竟,Rust有必要的工具--它支持使用特性向现有类型添加方法,并且它的特征大致相当于C++的默认构造函数。

C++表达式:

代码语言:javascript
复制
map[0][0] = 0;
map[0][1] = 1;

将使用返回引用的方法(而不是operator [] )将其编写为以下Rust

代码语言:javascript
复制
*map.ensure(0).ensure(0) = 0;
*map.ensure(0).ensure(1) = 1;

ensure将被声明为一个必须由希望获得该方法的代码导入的特性:

代码语言:javascript
复制
use std::collections::HashMap;
use std::hash::Hash;

trait MapDefault<K, V: Default> {
    fn ensure(&mut self, key: K) -> &mut V;
}

...and定义如下:

代码语言:javascript
复制
impl<K: Eq + Hash, V: Default> MapDefault<K, V> for HashMap<K, V> {
    fn ensure(&mut self, key: K) -> &mut V {
        self.entry(key).or_insert_with(V::default)
    }
}

如果能够为IndexMut定义HashMap,这将使表达式缩短为*map[0][0] = 0,这与C++的原始版本几乎完全一样,但遗憾的是,Rust不允许为来自其他模块的类型实现运算符。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42983662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档