为什么HashSet保持自然/字母顺序?

时间:2018-02-01 10:39:45

标签: java hashset

当我在代码下面运行时,它始终以自然/字母顺序给出o / p。据我所知,HashSet没有对条目进行排序。我知道HashSetHashMap支持,而不是LinkedHashMap。我尝试浏览HashSetHashMap的源代码,但无法找到此行为的代码。

从源代码中可以看到HashSet类中的构造函数:

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

使用LinkedHashMap。如果我使用过这个构造函数,我会认为这就是这种行为的原因,但我没有使用这个构造函数。

有人可以解释一下这种行为的原因/代码吗?

这是我的简单代码:

Set<String> mySet = new HashSet<>();

        mySet.add("D");
        mySet.add("B");
        mySet.add("1");
        mySet.add("E");
        mySet.add("A");
        mySet.add("F");

        mySet.stream().forEach(x -> System.out.println(x));

OP:

1
A
B
D
E
F

5 个答案:

答案 0 :(得分:2)

这是巧合,因为默认的HashSet大于散列的范围且没有碰撞,并且字符串的散列最终按字母顺序排列。

这是String.hashCode:

的代码
   public int hashCode() {
        int h = hash;
        if (h == 0) {
            int off = offset;
            char val[] = value;
            int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

正如您所看到的,单字符字符串的哈希最终只是字符值。

HashSet的default capacity为16,这意味着您的所有值最终都会显示在存储区char value % 16中,结果为您的示例的字母顺序。试试&#34; 2&#34;而不是&#34; 1&#34;,例如,这应该在&#34; A&#34;之后结束。即使你交换了&#34; A&#34;和&#34; 1&#34;这也应该在输出中交换它们。请参阅Ascii table

答案 1 :(得分:1)

来自Java 8 Docs

  

此类实现Set接口,由哈希表(实际上是HashMap实例)支持。它不能保证集合的迭代顺序;特别是,它不保证订单会随着时间的推移保持不变。

换句话说,你不能依赖HashSet中元素的顺序。

答案 2 :(得分:0)

使用以下代码,您会看到添加元素的哈希码按升序排列:

Set<String> mySet = new HashSet<>();

mySet.add("D");
mySet.add("B");
mySet.add("1");
mySet.add("E");
mySet.add("A");
mySet.add("F");

mySet.stream()
     .forEach(x -> System.out.println(x + " : " + x.hashCode()));

System.out.println(mySet);
  

1:49

     

A:65

     

B:66

     

D:68

     

E:69

     

F:70

     

[1,A,B,D,E,F]

这里您使用了一个非常特别的示例:您只添加了包含单个字符(字母或数字)的String
由于这些代码的哈希码对应于它们的ASCII代码,因此您可以获得符合ASCCI顺序的可预测顺序。

不同的哈希码值在HashMap实现中由数组的不同元素物理表示:

transient Node<K,V>[] table;

Iterator的{​​{1}}迭代按索引索引的数组元素 而结果。

现在,HashMap用于迭代的ASCII顺序看起来像数字和字母字符的自然顺序,仅适用于非常简单的情况,其中添加的Map仅由...组成1个字母或1个数字

添加包含多个字符的String,您将有一个不可预测的顺序:

String
  

90000:54118329

     

妈妈:77733

     

15454:46883119

     爸爸:68455

     

[90000,妈妈,15454,爸爸]

答案 3 :(得分:0)

长度为1的字符串的hashCode只是唯一的char,其哈希码是它自己的数值。 Voilà,全部都是订购的。

对于具有相同前缀,长度相同且与安全漏洞相关的字符串,也可以找到这种现象。 (我相信MD5需要人工种子。)

答案 4 :(得分:0)

这对于生成有序哈希的测试/工作数据集来说只是巧合。我在你的套装中添加了一些元素。尝试运行以下代码,我想你会得到你的答案。

Set<String> mySet = new HashSet<>();

mySet.add("D");
mySet.add("B");
mySet.add("1");
mySet.add("E");
mySet.add("A");
mySet.add("F");
mySet.add("C");
mySet.add("Z");
mySet.add("M");
mySet.add("Q");


mySet.stream().forEach(x -> System.out.println(x));

这是我的输出(不是自然顺序): 1 一个 Q 乙 C d Ë F ž 中号