【问题标题】:Iterating over the same HashMap keys twice. Is the order guaranteed to be same?迭代相同的 HashMap 键两次。订单是否保证相同?
【发布时间】:2026-02-17 14:25:01
【问题描述】:

如果我在 freemarker 中按以下方式遍历地图两次

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

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

一些哈希值保持有意义的顺序

这是什么意思?

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

【问题讨论】:

    标签: html freemarker


    【解决方案1】:

    来自HashMap 文档:

    这个类不保证地图的顺序;特别是,它不保证订单会随着时间的推移保持不变。

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

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

    一些哈希值保持有意义的顺序

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

    【讨论】:

    • FreeMarker 是否尊重我们用来填充模板变量的 java.util.Map(例如 HashMap、TreeMap 等)的特定实现?它是否使用常规 java API 来访问地图中的键?
    • @ishan3243 如果您使用版本为freemarker.template.Configuration.VERSION_2_3_22 或更高版本的配置,那么正如\@ddekany 所解释的,它使用一个适配器,该适配器仅调用您传递给它的Map 实现的方法。跨度>
    【解决方案2】:

    HashMap.keys() 调用两次以相同的顺序返回键(并且它在迄今为止的所有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 总是以相同的顺序返回键,FreeMarker 也是。

    【讨论】:

      最近更新 更多