join - 在 Rust 中内连接两个 `HashMap`
问题描述
假设我们有两个std::collections::HashMap
-s,比如说HashMap<K, V1>
andHashMap<K, V2>
和一个 function Fn(V1, V2) -> R
。
如何在这些哈希映射上执行内部连接,以便获得HashMap<K, R>
它们的共享密钥?这是一个参考实现来说明我的意思:
use std::collections::HashMap;
fn main() {
let mut map_a = HashMap::<&str, i32>::new();
map_a.insert("foo", 1);
map_a.insert("bar", 2);
map_a.insert("qux", 3);
map_a.insert("quux", 4);
let mut map_b = HashMap::<&str, i32>::new();
map_b.insert("foo", 5);
map_b.insert("bar", 6);
map_b.insert("quuux", 7);
map_b.insert("quuuux", 8);
// To keep it simple I'm just combining respective values into a tuple:
let joined_map = map_a
.into_iter()
.filter_map(|(key, value_a)| map_b.remove(key).map(|value_b| (key, (value_a, value_b))))
.collect::<HashMap<&str, (i32, i32)>>();
println!("{:?}", joined_map);
}
预期的输出是:
{"foo": (1, 5), "bar": (2, 6)}
这很好用,我可以使它在 a 中通用,但我在标准库或crates.ioutils.rs
上找不到它的现有实现,这感觉不对。此外,不执行sort-merge join似乎不是最理想的,因为我们已经在下面有排序的哈希码。我在监督什么吗?还是因为无论如何都要查找而过于挑剔?HashMap
O(1)
解决方案
正如您链接的答案所提到的,对于没有固有顺序的哈希表,没有办法更有效地做到这一点。(使用对象的散列码没有帮助,因为除非您使用了备用散列器,否则每个散列器都HashMap
使用不同的散列函数,以防止提交故意冲突散列键的拒绝服务攻击。)
您可以对算法进行一项高级改进:迭代两个哈希表中较小的一个,因为这需要最少的查找。
如果您有一个排序集合,例如 a BTreeMap
,那么itertools::Itertools::merge_join_by
可以帮助您实现排序数据的合并:
use std::collections::BTreeMap;
use itertools::{Itertools, EitherOrBoth};
fn main() {
let mut map_a = BTreeMap::<&str, i32>::new();
map_a.insert("foo", 1);
map_a.insert("bar", 2);
map_a.insert("qux", 3);
map_a.insert("quux", 4);
let mut map_b = BTreeMap::<&str, i32>::new();
map_b.insert("foo", 5);
map_b.insert("bar", 6);
map_b.insert("quuux", 7);
map_b.insert("quuuux", 8);
let joined_map = map_a
.into_iter()
.merge_join_by(map_b, |(key_a, _), (key_b, _)| Ord::cmp(key_a, key_b))
.filter_map(|elem| match elem {
// Keep only paired elements, discard all others (making this an inner join).
EitherOrBoth::Both((k, val_a), (_, val_b)) => Some((k, (val_a, val_b))),
_ => None,
})
.collect::<BTreeMap<&str, (i32, i32)>>();
println!("{:?}", joined_map);
}
这是对单个查找的性能改进,如果映射具有可比的大小,因为BTreeMap
的查找是 O(log(n)),所以整个连接算法将是 O(n log(n)),而是 O (n) 使用合并,其中 n 是两个映射的最大大小。
另一方面,如果其中一个映射要小得多,那么迭代那个较小的映射是一个更好的算法,因为如果 n 比 m 小一个因子,O(n log(n)) 比 O(m) 好对数(n)。
总之,有不止一种可能的算法,哪一种最好取决于集合类型和相对集合大小。您可以使用排序来加速连接,但只有当您已经具有排序时才有用,而哈希映射中没有排序。
推荐阅读
- activemq - 使用特定 IP 地址配置 ActiveMQ 5.16.0 Web 控制台
- javascript - Array.find() 不适用于对象,但是当我在特定对象上单独执行时,它可以工作
- tensorflow - 使用条件变分自动编码器进行回归 (CVAE)
- visual-studio - 构建服务器上缺少多个 MSBuild .target 文件
- excel - Excel 宏挂了很长时间,但在调试期间从不挂起
- css - Navpills 要在媒体最小宽度的右侧对齐:900px
- c++ - 模板类中的完美转发
- python - open().read() 函数在第二次调用时返回空字符串,即使在重新打开文件后也是如此:Python
- php - 无法在 Laravel 的 POST 请求中添加外键
- python - 如何用 pytest.raises 捕获 sqlite3.IntegrityError?