直接println输出HashSet中的元素,是按照什么顺序输出的?

第一次输出是1002 BB输出在前,如果是按照添加元素的顺序输出不应该是1001 AA先输出吗?

后三次输出的排序也看不明白,求大佬指点一下


@Test
public void test3(){
    HashSet set = new HashSet();
    Person p1 = new Person(1001,"AA");
    Person p2 = new Person(1002,"BB");

    set.add(p1);
    set.add(p2);
    System.out.println(set);

    p1.name = "CC";
    set.remove(p1);
    System.out.println(set);
    set.add(new Person(1001,"CC"));
    System.out.println(set);
    set.add(new Person(1001,"AA"));
    System.out.println(set);

}

以上代码输出的结果是


[Person{id=1002, name='BB'}, Person{id=1001, name='AA'}]
[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]

Person类中重写的toString 、equals、hashCode 方法如下


@Override
public String toString() {
    return "Person{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Person person = (Person) o;

    if (id != person.id) return false;
    return name != null ? name.equals(person.name) : person.name == null;
}

@Override
public int hashCode() {
    int result = id;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    return result;

  1. HashSet底层用的是HashMap中的KeySet。
  2. 而HashMap的key是数组+链表的形式存储的,每个数组单元放的都是一个链表,从0号位的链表迭代打印,再打印1号位,......以此类推。
  3. 之所以说HashMap和HashSet是无序的,是因为你add进去的key通过hash算法计算后,存放到的数组单元是随机的,第一次add放到的是5号位的数组链表中,第二次也许放的是1号位的数组链表,然后print中却永远是从0号数组单元检索到最后的数组单元,也就导致了最后的打印结果和你放入的顺序对不上。
  4. 具体的细节你可以去看下HashMap的源码。

HashSet是无序集合,每次执行的效果都是不一样的,随机的。
重写hash方法,是因为重写了equals方法,为了比较值的相等。
你只要知道Set集合是无序,List集合是有序的就好了。
至于为什么是无序,这个就跟底层有关系了,底层是链表+数组。

HashSet是一个无序的列表,每次取得顺序都不一样

HashSet没有顺序,Hash开头的集合类都没有顺序。