- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
HashMap
实现了 get
和 insert
方法,它们分别采用单个不可变借用和单个值移动。
我想要一个类似这样的特征,但它需要两个键而不是一个。它使用了里面的 map ,但它只是一个实现细节。
pub struct Table<A: Eq + Hash, B: Eq + Hash> {
map: HashMap<(A, B), f64>,
}
impl<A: Eq + Hash, B: Eq + Hash> Memory<A, B> for Table<A, B> {
fn get(&self, a: &A, b: &B) -> f64 {
let key: &(A, B) = ??;
*self.map.get(key).unwrap()
}
fn set(&mut self, a: A, b: B, v: f64) {
self.map.insert((a, b), v);
}
}
最佳答案
这当然是可能的。 signature of get
是
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
这里的问题是实现一个 &Q
类型,这样
(A, B): Borrow<Q>
Q
实现 Hash + Eq
要满足条件(1),我们需要考虑怎么写
fn borrow(self: &(A, B)) -> &Q
诀窍在于 &Q
不需要是一个简单的指针,它可以是一个 trait object !这个想法是创建一个特征 Q
,它将有两个实现:
impl Q for (A, B)
impl Q for (&A, &B)
Borrow
实现将简单地返回 self
,我们可以分别从这两个元素构造一个 &dyn Q
特征对象。
full implementation 是这样的:
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
// See explanation (1).
trait KeyPair<A, B> {
/// Obtains the first element of the pair.
fn a(&self) -> &A;
/// Obtains the second element of the pair.
fn b(&self) -> &B;
}
// See explanation (2).
impl<'a, A, B> Borrow<dyn KeyPair<A, B> + 'a> for (A, B)
where
A: Eq + Hash + 'a,
B: Eq + Hash + 'a,
{
fn borrow(&self) -> &(dyn KeyPair<A, B> + 'a) {
self
}
}
// See explanation (3).
impl<A: Hash, B: Hash> Hash for dyn KeyPair<A, B> + '_ {
fn hash<H: Hasher>(&self, state: &mut H) {
self.a().hash(state);
self.b().hash(state);
}
}
impl<A: Eq, B: Eq> PartialEq for dyn KeyPair<A, B> + '_ {
fn eq(&self, other: &Self) -> bool {
self.a() == other.a() && self.b() == other.b()
}
}
impl<A: Eq, B: Eq> Eq for dyn KeyPair<A, B> + '_ {}
// OP's Table struct
pub struct Table<A: Eq + Hash, B: Eq + Hash> {
map: HashMap<(A, B), f64>,
}
impl<A: Eq + Hash, B: Eq + Hash> Table<A, B> {
fn new() -> Self {
Table {
map: HashMap::new(),
}
}
fn get(&self, a: &A, b: &B) -> f64 {
*self.map.get(&(a, b) as &dyn KeyPair<A, B>).unwrap()
}
fn set(&mut self, a: A, b: B, v: f64) {
self.map.insert((a, b), v);
}
}
// Boring stuff below.
impl<A, B> KeyPair<A, B> for (A, B) {
fn a(&self) -> &A {
&self.0
}
fn b(&self) -> &B {
&self.1
}
}
impl<A, B> KeyPair<A, B> for (&A, &B) {
fn a(&self) -> &A {
self.0
}
fn b(&self) -> &B {
self.1
}
}
//----------------------------------------------------------------
#[derive(Eq, PartialEq, Hash)]
struct A(&'static str);
#[derive(Eq, PartialEq, Hash)]
struct B(&'static str);
fn main() {
let mut table = Table::new();
table.set(A("abc"), B("def"), 4.0);
table.set(A("123"), B("456"), 45.0);
println!("{:?} == 45.0?", table.get(&A("123"), &B("456")));
println!("{:?} == 4.0?", table.get(&A("abc"), &B("def")));
// Should panic below.
println!("{:?} == NaN?", table.get(&A("123"), &B("def")));
}
解释:
KeyPair
trait 起到我们上面提到的 Q
的作用。我们需要 impl Eq + Hash for dyn KeyPair
,但 Eq
和 Hash
都不是 object safe 。我们添加了 a()
和 b()
方法来帮助手动实现它们。
现在我们将 Borrow
特性从 (A, B)
实现到 dyn KeyPair + 'a
。注意 'a
——这是使 Table::get
实际工作所需的一个微妙的部分。任意 'a
允许我们说 (A, B)
可以借用到 trait 对象any 生命周期。如果我们不指定 'a
,未调整大小的特征对象将是 default to 'static
,这意味着 Borrow
特征只能在像 (&A, &B)
这样的实现超过 'static
时应用,当然不是这种情况。
最后,我们实现 Eq
和 Hash
。与第 2 点相同的原因,我们实现的是 dyn KeyPair + '_
而不是 dyn KeyPair
(在此上下文中表示 dyn KeyPair + 'static
)。这里的'_
是一个语法糖,意思是任意生命周期。
在 get()
中计算散列和检查相等性时,使用特征对象会产生间接成本。如果优化器能够将其去虚拟化,则可以消除成本,但 LLVM 是否会这样做是未知的。
另一种方法是将 map 存储为 HashMap<(Cow<A>, Cow<B>), f64>
。使用它需要较少的“聪明代码”,但现在存储拥有/借用标志以及 get()
和 set()
中的运行时成本需要内存成本。
除非您 fork 标准 HashMap
并添加一个方法来单独通过 Hash + Eq
查找条目,否则没有保证零成本的解决方案。
关于hashmap - 如何实现有两个键的HashMap?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45786717/
我有不同的结构,它们都包含一个 HashMap与 String作为键,但具有不同的值类型。例如,一个结构有一个类型为 HashMap 的成员, 另一个将有一个 HashMap 类型的成员, 等等。 我
我想制作一个包含学生姓名和科目的板,每个学生在每个科目中都有一个成绩(或者没有..他可以离开考试而不写,然后他的案子将是空的)。我只想使用 HashMap。我的意思是,它会是这样的: HashMap>
是否有内存和速度高效的方法来在 HashMap 中动态存储唯一键:值对? key 保证是唯一的,但它们的数量经常变化。插入和删除必须很快。 我所做的是包含有符号距离场的八叉树(非线性/完整)。八叉树经
有谁知道为什么选择通过 LinkedList 而不是另一个 Hashmap 来实现 HashMap 的存储桶。如果桶本身变成了 HashMap,那么 contains 或 get 的时间复杂度似乎是
我想创建一个具有嵌套结构的 HashMap,就像这个复杂的示例: { type: boy name: Phineas father: type: man
这个问题在这里已经有了答案: How do I create a global, mutable singleton? (7 个答案) 关闭 7 年前。 我想要一个可扩展的字典,将 Object 与
HashMap> hm = new HashMap>(); hm.put("Title1","Key1"); for(int i=0;i hm1 = new H
我必须修改当前代码以适应 Spring MVC。我有 HashMap hashmap = new HashMap(); request.setAttribute("dslrErrors", hashm
我正在尝试进行一些错误捕获。 错误应该检查数组的长度是否小于 2,并检查 HashMap 是否包含用户输入的键。 捕获的错误必须仅使用 if 语句,并且必须使用 .length() 方法,并且必须使用
在 stackoverflow 上提出另一个问题后,(Java- Why this program not throwing concurrent Modification exception)我开始
我有两个类,想使用 org.dozer.Mapper( http://dozer.sourceforge.net/ ) 将 Female 对象的属性映射到 Male 对象。 第一类是: public
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be
是否有任何方法可以检查 HashMap 是否包含一组特定的键(这些键是在数组中给出的)。当我尝试类似下面的代码时,它返回 false。 map.containsKey(arrayOf("2018-01
跟进我的问题:How To Access hash maps key when the key is an object 我想尝试这样的事情:webSearchHash.put(xfile.getPa
我有一个可扩展的 ListView ,对于每个 child ,我需要有 4 个“额外”或字符串或其他名称来调用它:- 子标题- 描述- 链接1- 链接2 跟着教程,创建 ListView 、不同的 p
我想确保这是正确的,因为如果不正确,它可能会破坏我的应用程序。 我有这个: private static HashMap> balance = new HashMap<>(); 如果我得到这样的值:
我想做以下事情: 为某个键查找Vec,并将其存储以备后用。 如果它不存在,则为键创建一个空的 Vec,但仍将其保存在变量中。 如何有效地做到这一点?自然地,我认为我可以使用 match: use st
我想做以下事情: 为某个键查找Vec,并将其存储以备后用。 如果它不存在,则为键创建一个空的 Vec,但仍将其保存在变量中。 如何有效地做到这一点?自然地,我认为我可以使用 match: use st
我想做以下事情: 为某个键查找Vec,并将其存储以备后用。 如果它不存在,则为键创建一个空的 Vec,但仍将其保存在变量中。 如何有效地做到这一点?自然地,我认为我可以使用 match: use st
我想做以下事情: 为某个键查找Vec,并将其存储以备后用。 如果它不存在,则为键创建一个空的 Vec,但仍将其保存在变量中。 如何有效地做到这一点?自然地,我认为我可以使用 match: use st
我是一名优秀的程序员,十分优秀!