迭代相同的 HashMap 键两次。订单保证相同吗?

Iterating over the same HashMap keys twice. Is the order guaranteed to be same?

如果我在 freemarker 中迭代地图两次,如下所示

<#list node_map?keys as node>
  <th>${node}</th>
</#list>
<#list node_map?keys as node>
  <th>${node}</th>
</#list>

是否保证两次迭代中键的顺序相同?这个 documentation 表示按键的顺序是任意的。这是否意味着当我们多次迭代同一张地图时它会发生变化?它还说

some hashes maintain a meaningful order

这是什么意思?

我正在使用 java Map/HashMap 来填充 node_map 模板变量。

至于 HashMap.keys() 以相同的顺序调用两次 returns 键(目前在所有版本的 Java 中都是如此),两个 #list -s 将以相同的顺序打印密钥(假设支持 Map 中的密钥集在两者之间没有改变)。该文档仅表示某些 Map-s,特别是 HashMap,具有随机的键顺序,就(普通)用户而言。

一些细节:#list?keys 很简单,它们不打乱顺序,它们只是调用适当的 TemplateModel 方法来列出键。 ObjectWrapper 比较棘手;这就是将 Map-s 包装成 TemplateModel-s 的原因。遗留配置使用 SimpleHash 来包装 Map-s,众所周知,与原始 Map 相比,它在某些情况下会更改键顺序,但仅在创建时才会更改,稍后读取时不会更改。 SimpleHash 将原始 Map 复制到内部 Map,如果原始是 HashMap,它会将其复制到另一个 HashMap,因此行为将相似(尽管实际的键顺序可能不同)。更现代的配置使用 DefaultMapAdapter,它不会更改包装的 Map 的键顺序,因为它只是一个适配器。所以在那种情况下,就包装 Map 总是 returns 相同顺序的键而言,FreeMarker 也是如此。

来自 HashMap 文档:

This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

理论上这意味着在后续迭代之间绝对不能保证键的顺序,即使你立即一个接一个地做(时间已经改变),你也可以看到this question .

在实践中,在检查 Java 8 源代码后 HashMap 内部有一个 Node<K,V>[] table 数组,它在迭代期间所做的一切就是遍历它。因此,如果您将在不对地图进行任何更改的情况下对 node_map?keys 进行 2 次后续调用,我可以自信地说它会以相同的顺序进行。我永远不会真正编写依赖于它的代码,因为它不受合同保证。

some hashes maintain a meaningful order

这意味着您可以使用其他 Map 保证一致排序的实现,例如 TreeMapLinkedHashMap。如果你用LinkedHashMap填充node_map那么它不仅在实践上而且在理论上都将保证一致(如果你想依赖它,你应该这样做)。