为什么我的HashSet中的顺序永远不会改变?

时间:2018-01-31 11:09:49

标签: java random collections hashset shuffle

我正在使用带有HashSet的字符串(长句子),并且我试图在每次程序运行时将它们随机播放以获得随机句子,但这不会发生

public class testshuffle {

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            run();
        }
    }

    public static void run() {
        ArrayList<String> list = new ArrayList<>();
        Set<String> set = new HashSet<>();
        list.add("Alexandria And Mimy are good people");
        list.add("Bob And Alexandria are better than Mimy");
        list.add("Camelia And Johanness are better than Bob And Alexandria");

        shuffle(list, ThreadLocalRandom.current());
        set.addAll(list);
        System.out.println(set);
    }
}

我知道HashSet订单无法保证。使用Integer或Double时,返回的hashCode可能会导致元素被排序。

但是我在这里使用Strings,输出是:

[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]
.
.
.
[Alexandria And Mimy are good people, Bob And Alexandria are better than Mimy, Camelia And Johanness are better than Bob And Alexandria]

请不要将此标记为重复,因为这与我在此处找到的案例不同

3 个答案:

答案 0 :(得分:0)

  

无法保证HashSet顺序

这不完全正确,有什么顺序?如果原生订单(1&lt; 2,a&lt; b),则为真。但是当放入HashSet时,它有自己的基于元素哈希码的顺序,这意味着如果所有元素都有唯一的哈希码,你运行1000次,顺序总是一样!

如果您将代码更改为:

    list.add("Alexandria");
    list.add("Bob");
    list.add("Camelia");

结果是:

[Bob, Camelia, Alexandria]
[Bob, Camelia, Alexandria]
[Bob, Camelia, Alexandria]
你知道吗?没有字母顺序!

答案 1 :(得分:0)

HashSet使用计算出的hashCodes以分块方式放置这些字符串。

根据String hashCode()契约,两个相等的字符串在同一个JVM中将具有相同的哈希代码。这意味着只要字符串不改变,哈希码就不会改变。

话虽如此,实际的hashCode()实现已从一个JVM版本更改为另一个JVM版本和/或从一个JVM供应商更改为另一个JVM版本。因此,即使它在您的情况下看似以可预测的方式表现,也不要完全依赖它。

String hashCode()JavaDoc:

/** * Returns a hash code for this string. The hash code for a * {@code String} object is computed as * <blockquote><pre> * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] * </pre></blockquote> * using {@code int} arithmetic, where {@code s[i]} is the * <i>i</i>th character of the string, {@code n} is the length of * the string, and {@code ^} indicates exponentiation. * (The hash value of the empty string is zero.) * * @return a hash code value for this object. */

答案 2 :(得分:0)

这是对其他答案和评论的补充,但似乎OP仍然不明白,所以我会尝试举例。

HashSet的结构是一个桶阵列。存储桶包含该集合的0,1或几个元素。如果存储桶中有多个元素,则它们将存储在该存储桶内的链接列表中。

(注意,这是一个简化:HashSet比这更复杂,并且可以在某些条件下开始使用树)。

向HashSet添加元素时,将根据元素的hashCode以确定的方式选择存储该元素的存储区。

所以,想象一下HashSet有7个桶b1到b7。

想象一下,你将3个元素A,B和C添加到HashSet中。

想象一下,用于选择桶的确定性函数返回

  • b1 for A
  • b2 for B
  • b3 for C

您将拥有类似

的结构
 [
   b1 -> A,
   b2 -> B,
   b3 -> C,
   b4 -> <empty>
   b5 -> <empty>
   b6 -> <empty>
   b7 -> <empty>
 ]

迭代时,HashSet不会随机迭代。它将简单地从一个桶到另一个桶,并且总是打印A,然后是B,然后是C.因为选择桶的功能是确定性的,A,B和C将始终分别存储在b1,b2和b3中,无论插入是什么订单是。

这就是为什么你总是得到同样的订单。

现在,假设A,B和C具有相同的hashCode。或者至少,用于根据hashCode为A,B和C查找存储桶的函数的结果返回A,B和C的相同存储桶:b3。

如果你插入A,然后是B,然后是C,你最终会得到

 [
   b1 -> <empty>,
   b2 -> <empty>,
   b3 -> A -> B -> C
   b4 -> <empty>
   b5 -> <empty>
   b6 -> <empty>
   b7 -> <empty>
 ]

但是如果你插入C,然后是B,那么A,你最终会输入

 [
   b1 -> <empty>,
   b2 -> <empty>,
   b3 -> C -> B -> A
   b4 -> <empty>
   b5 -> <empty>
   b6 -> <empty>
   b7 -> <empty>
 ]

当迭代HashSet时,顺序因此会有所不同,具体取决于插入顺序。

TL; DR:HashSet可以按照自己的方式自由排序,因此您不应该依赖HashSet中元素的顺序。只需直接使用您的列表,因为它已经改组,并提供订购保证。