简体   繁体   English

Rust 中的 hash 映射在同一 function 中就地修改、插入或删除

[英]In-place modification, insertion or removal in the same function for hash maps in Rust

Say I have a hash map m: HashMap<K, V> , a key k: K and a value v: V , and would like to do the following:假设我有一个 hash map m: HashMap<K, V> ,一个键k: K和一个值v: V ,并且想做以下事情:

  • If m does not contain a value at index k , insert v at index k .如果m在索引k处不包含值,则在索引k处插入v
  • If m contains a value w at index k , apply a function fn combine(x: V, y: V) -> Option<V> to v and w , and:如果m在索引k处包含值w ,则将 function fn combine(x: V, y: V) -> Option<V>应用于vw ,并且:
    • If the result is None , remove the entry at index k from m .如果结果为None ,则从m中删除索引k处的条目。
    • If the result is Some(u) , replace the value at index k by u .如果结果是Some(u) ,将索引k处的值替换为u

Is there a way to do this "in-place", without calling functions that access, modify or remove the value at k multiple times?有没有办法“就地”执行此操作,而无需调用多次访问、修改或删除k处的值的函数?

I would also like to avoid copying data, so ideally one shouldn't need to clone v to feed the clones into insert and combine separately.我还想避免复制数据,所以理想情况下不需要克隆v来将克隆提供给insert并单独combine

I could rewrite combine to use (mutable) references (or inline it), but the wish of not copying data still remains.我可以重写combine以使用(可变)引用(或内联它),但不复制数据的愿望仍然存在。

Digging deeper into the Entry documentation , I noticed that the variants of the Entry enum offer functions to modify, remove or insert entries in-place.深入研究Entry文档,我注意到Entry枚举的变体提供了就地修改、删除或插入条目的功能。

After taking std::collections::hash_map::Entry into scope, one could do the following:在采取std::collections::hash_map::Entry进入 scope 之后,可以执行以下操作:

match m.entry(k) {
    Entry::Occupied(mut oe) => {
        let w = oe.get_mut();
        match combine(v, w) {
            Some(u) => { *w = u; },
            None    => { oe.remove_entry(); },
        }
    },
    Entry::Vacant(ve) => { ve.insert(v); },
}

( Here is a PoC in the Rust playground.) 是 Rust 操场上的 PoC。)

This, however, requires combine to take a (mutable) reference as its second argument (which is fine in my case).但是,这需要combine将(可变)引用作为其第二个参数(在我的情况下这很好)。

I managed to do it in one access, one write and one key-deletion in total in the worst case.在最坏的情况下,我设法通过一次访问、一次写入和一次删除密钥来完成。 The last key-deletion should not be necessary, but I'm not certain it can be done.最后一个键删除应该不是必需的,但我不确定它是否可以完成。 I gave it my best so far.到目前为止,我已经尽力了。 I hope this helps!我希望这有帮助!

Okay, so I think we want to use the Entry API .好的,所以我想我们想使用条目 API

The full method list for Entry is here . Entry的完整方法列表在这里

I think we'd do it in the following order:我想我们会按照以下顺序进行:

  • If m contains a value w at index k : (two more steps)如果m在索引k处包含值w :(再执行两步)
  • Or insert v at index k .或者在索引k处插入v

This can be done by using .and_modify and then .or_insert .这可以通过使用.and_modify然后.or_insert来完成。 Something like this:像这样的东西:

let map = // ... Initialize the map

// Do stuff to it
// ...

// Our important bit:

let mut delete_entry = false;

map.entry(k)
   .and_modify(|w| { // If the entry exists, we modify it

     let u = combine(v, w);

     match u {

       Some(y) => *w = y;

       None => delete_entry = true;
       }
     }
   )
   .or_insert(v); // If it doesn't, we insert v

if delete_entry {
  map.remove(k);
}

I don't think there's a way to do all three things without that last map.remove access, so this is my best attempt for now.如果没有最后一个map.remove访问权限,我认为没有办法完成所有三件事,所以这是我目前最好的尝试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM