【问题标题】:How to iterate through the keys of a HashMap in order如何按顺序遍历 HashMap 的键
【发布时间】:2020-03-21 22:08:11
【问题描述】:

我想按顺序遍历 HashMap 的键。有没有一种优雅的方式来做到这一点?我能想到的最好的是:

use std::collections::HashMap;

fn main() {
    let mut m = HashMap::<String, String>::new();

    m.insert("a".to_string(), "1".to_string());
    m.insert("b".to_string(), "2".to_string());
    m.insert("c".to_string(), "3".to_string());
    m.insert("d".to_string(), "4".to_string());

    let mut its = m.iter().collect::<Vec<_>>();
    its.sort();

    for (k, v) in &its {
        println!("{}: {}", k, v);
    }
}

我希望能够做这样的事情:

for (k, v) in m.iter_sorted() {
}
for (k, v) in m.iter_sorted_by(...) {
}

显然我可以写一个特征来做到这一点,但我的问题是这样的东西已经存在了吗

编辑:另外,由于人们指出BTreeMap 已经排序,我可能应该注意到虽然这是真的,但它实际上不如HashMapsort()(当然只要你只排序一次)。以下是随机u32-&gt;u32 地图的一些基准测试结果:

另外,BTreeMap 只允许一个排序顺序。

【问题讨论】:

  • 如果你真的想要HashMap,这在概念上是你能做的最好的。如果您可以使用BTreeMap,迭代将自动按顺序进行。
  • 是的,我知道BTreeMap,我知道这是算法最优的。我只是在询问有关编码人体工程学的问题-基本上是否有一种更短更优雅的方式来编写它。

标签: rust hashmap


【解决方案1】:

基于@Inline 所写的,使用HashMap 的更通用的解决方案,允许按值排序和更改值。 (注意调整了HashMap的内容,让key和value排序的区别可见。)

use itertools::Itertools;  // itertools = "0.10"
use std::collections::HashMap;

fn main() {
    let mut m = HashMap::<String, String>::new();

    m.insert("a".to_string(), "4".to_string());
    m.insert("b".to_string(), "3".to_string());
    m.insert("c".to_string(), "2".to_string());
    m.insert("d".to_string(), "1".to_string());

    // iterate (sorted by keys)
    for (k, v) in m.iter().sorted_by_key(|x| x.0) {
        println!("k={}, v={}", k, v);
    }
    println!();

    // iterate (sorted by values)
    for (k, v) in m.iter().sorted_by_key(|x| x.1) {
        println!("k={}, v={}", k, v);
    }
    println!();

    // iterate (sorted by keys), write to values
    for (k, v) in m.iter_mut().sorted_by_key(|x| x.0) {
        *v += "v"; // append 'v' to value
        println!("k={}, v={}", k, v);
    }
}

Playground link

【讨论】:

  • 有谁知道如何按值排序和写入值?
【解决方案2】:

HashMap 不保证特定的迭代顺序。实现顺序一致的最简单方法是使用BTreeMap,它基于B-tree,对数据进行排序。

您应该了解,任何实现都会在O(n) 内存中执行此操作,尤其是存储对所有项目的引用以及至少O(n * log(n)) 时间来整理数据。

如果您了解这样做的成本,您可以使用来自itertools crate 的IterTools::sorted

use itertools::Itertools; // 0.8.2
use std::collections::HashMap;

fn main() {
    let mut m = HashMap::<String, String>::new();

    m.insert("a".to_string(), "1".to_string());
    m.insert("b".to_string(), "2".to_string());
    m.insert("c".to_string(), "3".to_string());
    m.insert("d".to_string(), "4".to_string());

    println!("{:#?}", m.iter().sorted())
}

Playground link

【讨论】:

  • 是的,我知道BTreeMap - Itertools 是我正在寻找的答案,谢谢!
  • 另外使用O(N) 内存不太可能成为问题,因为O(N) 内存已经用于存储实际数据。我还期望BTreeMap 的内存开销比存储N 迭代器的内存开销大。至于时间,不止一次排序肯定会更慢,但如果你只需要排序一次的数据,我不会感到惊讶,如果它非常相似甚至更快(取决于它是如何实现的),因为BTreeMap 是有效的无论如何都是插入排序。
  • @Timmmm “插入排序”通常意味着 O(n ²) 算法对数组进行就地排序。在BTreeMap 中插入 n 元素是根本不同的,并且具有 O(n log n) 的时间复杂度。如果您需要按顺序排列数据,我会选择BTreeMap - 它肯定更简单,而且可能更快。
  • 噢,我当然是指树排序!我不相信BTreeMap 它会更快。从算法上讲,它们具有相同的复杂性,至少在 C++ 领域,std::map 可能非常慢,因为它涉及大量分配。如果我有时间,也许我会对其进行基准测试。
  • @Timmmm 我也不相信它会更快,但在没有基准的情况下,我会选择更简单的解决方案。 :)
猜你喜欢
  • 2010-10-25
  • 1970-01-01
  • 1970-01-01
  • 2020-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-15
相关资源
最近更新 更多