如果添加到java.util.HashSet的每个对象都以确定的方式实现Object.equals()和Object.hashCode(),那么对于每个添加的相同元素集,HashSet上的迭代顺序保证是相同的, 无论的添加顺序是什么?
奖金问题:如果插入顺序相同怎么办?
(假设Sun JDK6具有相同的HashSet初始化。)
编辑:我原来的问题不明确。它不是关于HashSet的一般契约,而是Sun在JDK6中对HashSet的实现提供了关于确定性的保证。它本质上是非确定性的吗?是什么影响了迭代器使用的顺序?
答案 0 :(得分:18)
绝对不是。
只要您遇到存储桶冲突,插入顺序就会直接影响迭代顺序:
当两个元素在同一个桶中结束时,插入的第一个元素也将是迭代期间返回的第一个元素,至少如果碰撞处理和迭代的实现是直截了当的(并且是Sun的{{1是}
答案 1 :(得分:13)
对于这样的事情没有“官方”保证。我会说同样的HashSet实现的实例很可能是真的,以相同的方式初始化。但是我已经看到了例如Java 5和6之间迭代顺序不同的情况。
此外,由于重新散列,对于使用不同大小初始化的相同HashSet实现的实例可能会有所不同。即如果你有100个元素和两个集合,一个初始化大小大于100,另一个具有小得多的大小,第二个将被重新分配并且其元素在填充时重新进行多次。这可能导致映射到同一存储桶的元素以不同的顺序添加(并因此迭代)。
在Java4及更高版本中,您有LinkedHashSet
,它保证迭代顺序将是其元素插入的顺序。
答案 2 :(得分:8)
根据javadoc:
此类实现Set 接口,由哈希表支持 (实际上是一个HashMap实例)。它 不保证 集合的迭代顺序;在 特别是,它并不保证 订单将保持不变 时间。 [...] 此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间修改了该集合
方法iterator
:
返回元素上的迭代器 在这一套。返回元素 没有特别的顺序。
所以我认为你不能做出这样的假设。
答案 3 :(得分:7)
想要确认/提前评论。简而言之,不依赖于HashSet迭代的一致顺序。这会导致系统中出现错误。
我们刚刚发现并修复了HashSet中迭代顺序不一致的错误,即使是:
使用LinkedHashSet修复它。
感谢早期的海报:)
答案 4 :(得分:2)
永远不要对你放入HashSet的任何东西的迭代顺序做出假设,因为它的契约明确表示你不能以任何方式依赖它。如果要维护广告订单,请使用LinkedHashSet;如果要维护自然排序顺序,请使用TreeSet。
答案 5 :(得分:1)
不,这不能保证。
首先,不同的JVM可能以不同的方式实现HashSet算法(只要它符合HashSet规范),因此您将在不同的JVM上获得不同的结果。
其次,算法在构建不同的桶(哈希表算法的一部分)时可能依赖于非确定性因素。
答案 6 :(得分:1)
显示的订单对象将取决于HashSet的最终桶数。通过更改负载系数和/或初始容量,您可以更改元素最终的顺序。
在以下示例中,您可以看到这些确认每个结果的顺序不同。
public static void main(String...args) throws IOException {
printOrdersFor(8, 2);
printOrdersFor(8, 1);
printOrdersFor(8, 0.5f);
printOrdersFor(32, 1f);
printOrdersFor(64, 1f);
printOrdersFor(128, 1f);
}
public static void printOrdersFor(int size, float loadFactor) {
Set<Integer> set = new HashSet<Integer>(size, loadFactor);
for(int i=0;i<=100;i+=10) set.add(i);
System.out.println("new HashSet<Integer>("+size+", "+loadFactor+") adding 0,10, ... 100 => "+set);
}
打印
new HashSet<Integer>(8, 2.0) adding 0,10, ... 100 => [0, 50, 100, 70, 40, 10, 80, 20, 90, 60, 30]
new HashSet<Integer>(8, 1.0) adding 0,10, ... 100 => [0, 50, 100, 70, 20, 80, 10, 40, 90, 30, 60]
new HashSet<Integer>(8, 0.5) adding 0,10, ... 100 => [0, 100, 70, 40, 10, 50, 20, 80, 90, 30, 60]
new HashSet<Integer>(32, 1.0) adding 0,10, ... 100 => [0, 100, 70, 40, 10, 50, 80, 20, 90, 60, 30]
new HashSet<Integer>(64, 1.0) adding 0,10, ... 100 => [0, 70, 10, 80, 20, 90, 30, 100, 40, 50, 60]
new HashSet<Integer>(128, 1.0) adding 0,10, ... 100 => [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
答案 7 :(得分:0)
我确信Java开发人员希望您认为答案是“不”。特别是,对于散列表,为什么它们会让那些不需要这个属性的其他人保证它的速度变慢,以保证以相同顺序观察到散列冲突(相同hashCode%size)的对象,而不管它们的顺序如何投入?
答案 8 :(得分:0)
无法做出这样的假设。 javadoc说:
此类实现Set 接口,由哈希表支持 (实际上是一个HashMap实例)。它 不保证 集合的迭代顺序;在 特别是,它并不保证 订单将保持不变 时间。
您最接近的是使用LinkedHashSet来维护广告订单。