【问题标题】:For loop not printing hash map in orderFor循环不按顺序打印哈希图
【发布时间】:2021-12-03 06:55:28
【问题描述】:

我无法弄清楚为什么这没有按顺序打印哈希图。分配键时是否以这种方式保存?用肉眼看来,它似乎是随机的。这对 Rust 来说正常吗?

use std::collections::HashMap;
use rand::Rng;

fn main() {
    let mut board = HashMap::new();
    
    for n in 0..99 {
        board.insert(n,0);
    }
    // board.insert(1, 0);

    for (key,value) in &board {
        println!("{}: {}", key, value);
    }
}

输出:

21: 0
15: 0
90: 0
10: 0
52: 0
92: 0
32: 0
61: 0
91: 0
50: 0
28: 0
93: 0
64: 0
72: 0
75: 0
95: 0
98: 0
89: 0
57: 0
88: 0
9: 0
85: 0
87: 0
24: 0
29: 0
37: 0
19: 0
16: 0
44: 0
51: 0
79: 0
53: 0
73: 0
11: 0
7: 0
59: 0
62: 0
3: 0
74: 0
14: 0
96: 0
34: 0
40: 0
23: 0
86: 0
49: 0
82: 0
54: 0
80: 0
22: 0
31: 0
60: 0
76: 0
12: 0
0: 0
2: 0
97: 0
83: 0
27: 0
33: 0
69: 0
26: 0
46: 0
68: 0
43: 0
71: 0
58: 0
77: 0
17: 0
5: 0
35: 0
65: 0
56: 0
20: 0
48: 0
1: 0
13: 0
30: 0
4: 0
41: 0
55: 0
45: 0
25: 0
47: 0
63: 0
66: 0
6: 0
67: 0
38: 0
70: 0
81: 0
84: 0
39: 0
18: 0
42: 0
8: 0
78: 0
36: 0
94: 0

【问题讨论】:

    标签: rust hashmap


    【解决方案1】:

    通常,hashmap 的工作原理是将键映射到存储桶中,方法是将哈希函数应用于这些键,然后将值存储在该存储桶中。对于可以接受不可信数据的通用 hashmap,有必要使用加密安全的随机散列函数(技术上是 PRF),这样攻击者就不会通过将许多密钥散列到一个桶中来导致病态性能。

    Rust 实现了一个使用这种方法的 hashmap,就像许多其他语言一样(尽管值得注意的是,上次我检查过,PHP 没有)。由于这种方法,在内部,hashmap 没有排序,其项目将以任意顺序存储,这通常会因实例而异。

    一些语言,比如 Ruby,明确选择实现对象的插入顺序遍历,这在很多情况下非常方便。然而,这不是免费的,因为 Rust 旨在尽可能避免昂贵的开销。因此,您有一些选择:

    • 如果您需要按键排序的对象,或者只是按某些确定的顺序(例如,用于测试),您可以使用 BTreeMap,它提供按键排序的顺序。请注意,这与 HashMap 具有不同的性能特征,并且需要不同的键(具体来说,它们是 Ord 而不是 Hash)。
    • 如果您特别想要广告订单,您可以找到提供此功能的板条箱,例如 indexmaplinked_hash_map

    【讨论】:

    • “有必要使用加密安全的随机散列函数” SipHash(这是 Rust 的默认散列函数)特别不是加密散列函数,而且它真的不是一个选择有多短的散列哈希图。然而,它旨在减轻碰撞预测攻击(“散列泛洪”DOS)。
    • “Rust 实现了一个使用这种方法的 hashmap,就像许多其他语言一样(尽管值得注意的是,上次我检查过,PHP 没有)。”这主要是开放寻址表的问题,PHP 使用单独的链接。
    • @Masklinn 错误哈希函数(或被恶意制作的输入破坏)的病态性能是开放寻址哈希表和链表链式哈希表的问题。在这两种情况下,哈希表操作都会降级到 O(n)。
    • @user4815162342 冲突插入降级到 O(n) 与开放寻址,但使用单独的链接,除非你的实现真的很愚蠢(你使用单链表并附加)它仍然是 O(1 )。其他操作会降级,但攻击者对它们的控制要少得多。
    • @Masklinn 不,它必须是 O(n),因为您必须检查每个碰撞元素是否与您尝试插入的元素相等,以便区分插入和更新。即使没有插入,查找也会降级为 O(n)。对哈希表的冲突攻击绝不是开放寻址所特有的。
    【解决方案2】:

    由于 Hashmap 是一个可增长的结构,它由调用者动态分配,但由于操作系统在您使用的同一内核上执行的其他任务,它在运行期间可能会停止。

    由于某些操作系统级别的限制,哈希图在其他结构之上存在限制和好处。

    运行时内存排序

  • 然后哈希图会增长,但内存排序不是严格线性的,因为它不是从固定数量的引用开始的。


  • 对象的分配

  • 最后,完整的块由打印调用函数以分配方式表示,与分配由 OS 静态链接线程重新排序后写入的数据无关。


  • 这种结构使得哈希映射非常快,因为不需要根据初始计数对引用进行原子计数和排序;并且它们的排序方式是基于键本身而不是来自预分配的引用计数。


  • 调用函数和链接到它们所持有的对象的引用以非线性方式存储哈希映射的此类键。因此代表了所描述的方式。


  • 基于虚拟机的语言

  • 最后一件需要注意的重要事情是,某些语言(例如 rust 本身)在运行您编写的某些代码时会捕获操作系统信号,这是某种形式的基本 VM(虚拟机)自行捕获(嵌入在您的exe) 应用程序中可能的分配或运行时错误。

  • 以前的语言在二进制文件本身运行时不会进行运行时检查。其他语言确实有自己的堆栈,就像 Java 一样,它本身就是一个完整的虚拟机。虚拟机可以及时解决很多与引用分配相关的问题,进而为languaje模型中更实体的对象引用提供基础。
  • 【讨论】:

    • 无论是操作系统,还是虚拟机的存在与否,对 hashmap 排序都没有任何影响(对其性能几乎没有影响)。
    【解决方案3】:

    hashmap 的目的不是保持插入键的顺序。它用于快速数据查找。 This question 可能会帮助您找到解决方案

    【讨论】:

      猜你喜欢
      • 2016-06-22
      • 2021-05-26
      • 2022-01-19
      • 1970-01-01
      • 2017-08-16
      • 2012-08-14
      • 2018-03-15
      • 2022-12-10
      • 1970-01-01
      相关资源
      最近更新 更多