为什么我的程序的 HashSet 顺序总是相同的?

Why HashSet order always same for my program?

对于一些教程,他们说:

HashSet doesn’t maintain any order, the elements would be returned in any random order.

但是我写了一个测试程序,结果总是一样。

import java.util.*;

public class HashSetDemo {

    public static void main(String[] args) {
        HashSet<String> hs1 = new HashSet<String>();
        hs1.add("a");
        hs1.add("b");
        hs1.add("c");
        hs1.add("d");
        hs1.add(null);
        hs1.add(null);
        System.out.println(hs1);
        System.out.println(hs1);
    }
}

输出:

[null, a, b, c, d]
[null, a, b, c, d]

我试了很多次,但顺序总是一样的。为什么?希望有人能帮助我,在此先感谢!

不能保证维持秩序并不意味着它们有时不会秩序井然。

如果您需要排序,请使用不同的集合 - 例如树集。

如我们所见文档

It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.

它确实保持但不保证。随着时间的推移,你添加,删除几次你可以看到差异。

当然不是随机顺序,对于某个输入,迭代器的顺序是固定的,我想他们想说的顺序可能与输入顺序不同。 事实上,这里的顺序取决于 String.hashCode()String.equals()set.add() invoke.

的顺序

当您调用 System.out.print(set) 时,您的意思是 System.out.print(set.toString()) ,并且 set.toString() 调用 set 的迭代器来访问所有元素。

此行为的原因是 HashSetHashMap 支持,而 HashMap 又由 arrayEntry 对象支持。其中 hash 用于查找 arrayindex。所以在 HashSet 中总是有一个元素的顺序(array 的顺序),你只是不能保证这个顺序是什么。

据我从代码中可以看出,HashSet 的顺序由其元素的计算 hashes 的顺序决定(或至少受到影响)。然后,对于相对简单的输入(比如您的单个字符串),人们可能会假设 hashes 有严格的排序,这会给您带来看似自然的排序。对于更复杂的对象,因此更复杂的 hash 计算,hashes 将更加分散,并且排序 "more random".

此外,就像已经指出的那样,"no guarantee of ordering" 并不意味着 "guaranteed random ordering"。

String class 的 hashcode 方法也在这里发挥作用,对于单个字符 Strings hashcode 将只是int String 中第一个 char 的值。由于 charint 值是按字母顺序排列的,因此单个 char String 的计算 hashes 也是如此。

HashSet 不保证它,但这并不一定意味着它必须改变顺序。如果没有添加任何内容,则没有必要更改顺序。例如看看这个例子

hs1.add("c");
hs1.add("b");
hs1.add("d");
hs1.add("g");
hs1.add(null);
hs1.add(null);
System.out.println(hs1);

输出: [null, b, c, d, g]

然后我们添加一个新元素并再次打印:

    hs1.add("a");
    System.out.println(hs1);

输出: [null, a, b, c, d, g]

如您所见,它在一定程度上改变了顺序。

没有什么是可以保证的,但这并不意味着它必须特意改变顺序

HashSet 顺序不是随机的,它取决于实现并且实现可以自由更改。 JDK 8 中进行了显着更改。因此,如果您升级到 Java 8,您可能会看到 HashMap 顺序已更改。如果您使用像 IBM 这样的非 Oracle JDK,它也可能会有所不同。一般来说,你永远不应该依赖它,否则你的程序将来可能会崩溃。

HashSet() 没有任何排序。它也不支持输入顺序。但顺序不是随机的。即使您更改版本并升级,输出也会更改并与该版本保持相同。通过实施您的源代码,我得到了一些不同的答案,如下所示。我已经连续多次执行这段代码,但输出是一样的。

[a, b, c, d, 空]

还有一点,HashSet() 不支持重复,因此,添加重复 "null" 只会增加代码的长度。

HashSet 使用 binary search 查找可能的重复项,并且必须在哈希 (hashCode()) 之后对列表中的对象进行排序才能这样做。