【问题标题】:Do keys and values in an an unordered hash map line up when iterated independently?独立迭代时,无序哈希映射中的键和值是否对齐?
【发布时间】:2023-04-04 13:29:01
【问题描述】:

我已经看到有关此主题的答案,但想查找是否有人有任何示例,其中您从哈希映射派生的键集和值列表将具有不同顺序的键和值。我知道条目本身在哈希映射中可能有未定义的顺序,但是键和值的列表是否可以相互乱序?

这里有一个简短的 sn-p 说明:

public class App {
    public static void main(String[] args) {
        Map<String,String> stateCapitols = new HashMap<>();
        stateCapitols.put("AL", "Montgomery");
        stateCapitols.put("AK", "Juneau");
        stateCapitols.put("CO", "Denver");
        stateCapitols.put("FL", "Tallahassee");
        stateCapitols.put("Indiana", "Indianapolis");

        stateCapitols.keySet().stream().forEach(System.out::println);
        System.out.println();
        stateCapitols.values().stream().forEach(System.out::println);
    }
}

在上述示例中,AL 是否会出现在与 Denver(或任何其他值)相同的位置?

【问题讨论】:

标签: java collections hashmap


【解决方案1】:

让我们再看看Map合约中关于迭代顺序的Java SE API语言:

一些地图实现,如 TreeMap 类,对它们的顺序做出具体保证;其他的,比如 HashMap 类,则不需要。

还有HashMap

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

由于明确指出 HashMap 迭代器没有顺序,因此不能假设即使在调用 same 方法之间迭代也会稳定,更不用说在调用 不同的方法keySet()values()

有用的是,Map 有一个方法 entrySet() 可以完全满足您的需要:它以配对键和值的方式迭代映射内容。任何时候您都需要依赖该对的两个部分时,都可以使用这种方法。

随着对 Java 许可的更改现在生效,那些认为他们可能总是使用 Oracle 的 Java 实现的个人和组织现在正在寻找替代实现。依赖单一实现的不成文细节是极其危险的,现在比 Oracle 的许可和定价变化之前更是如此。

【讨论】:

  • 许可备注是不必要的。甚至 Oracle 的实施也可以改变,事实上,已经改变了很多次。
【解决方案2】:

HashMap 不保证其迭代顺序。原则上(即规范允许)键的顺序是可能的,例如,从一次迭代到下一次迭代,即使映射的内容没有改变,或者迭代顺序为键与对应值的迭代顺序不同。

这在HashMap 规范中有说明:

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

在实践中,如果 HashMap 的初始化和填充方式完全相同,则 HashMap 的迭代顺序从一次迭代到下一次,甚至从一次 JVM 调用到下一次都是稳定的。但是,应用程序依赖于此是不明智的。创建具有不同初始大小或加载因子的 HashMap 可能会影响迭代顺序,即使映射填充了相同的内容。 HashMap 实现确实会不时更改,这也会影响迭代顺序。即使在 JDK 的补丁或错误修复版本中也会发生此类更改。不幸的是,历史表明,当迭代顺序发生变化时,应用程序会中断。因此,健壮的应用程序应该努力避免对 HashMap 迭代顺序产生任何依赖。

这在实践中很难做到。我知道 JDK 的一个(非公开)版本具有随机化 HashMap 的迭代顺序的测试模式。这可能有助于清除此类依赖项。

如果您需要在迭代时关联 HashMap 的键和值,请获取 HashMap 的 entrySet() 并对其进行迭代。它提供映射条目(键值对),因此键和值之间的关系得以保留。

JDK 中的替代 Map 实现提供了良好定义的迭代顺序。 TreeMap 和 ConcurrentSkipListMap 根据提供的比较方法对它们的条目进行排序。 LinkedHashMap 提供基于插入顺序的迭代顺序。 (它还提供了一种按访问顺序进行迭代的模式,这有时很有用,但其行为往往令人惊讶。)

请注意,Java 9 中引入的不可修改集合(Set.of、Map.of 等)提供了随机迭代顺序。 JVM 的一次运行与下一次运行的顺序不同。这应该有助于应用程序避免对迭代顺序产生无意的依赖。

【讨论】:

  • 我相信从 hashMap 的 Set 视图创建键和值列表将保证键/值在各自的列表中以相同的顺序出现:stateCapitols.entrySet().stream().map(Entry::getKey).forEach(System.out::println);stateCapitols.entrySet().stream().map(Entry::getValue).forEach(System.out::println);
  • @SaturdaySherpa entrySet() 视图以及 keySet() 和 values() 视图是底层 HashMap 的“实时”视图。如果 HashMap 发生变化,视图也会发生变化,并且在某些情况下修改视图会写入底层的 HashMap。视图不是快照,因此不能保证多次迭代会产生一致的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-31
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
相关资源
最近更新 更多