Set接口

JAVA 不重复有序的集合 java 无序可重复_ide


JAVA 不重复有序的集合 java 无序可重复_ide_02

Set接口:

  • 存储无序的
  • 不可重复的数据

HashSet:

作为Set接口的主要实现类;

  • 线程不安全的
  • 可以存储null值

LinkedHashSet:

作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历 对于频繁的遍历操作,LinkedHashSet效率高于HashSet.

TreeSet:

可以按照添加对象的指定属性,进行排序。

Set:存储无序的、不可重复的数据

1.无序性:

  • 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

2. 不可重复性:

保证添加的元素按照 equals() 判断时,不能返回true.即:相同的元素只能添加一个。

JAVA 不重复有序的集合 java 无序可重复_数据_03

要求:

  1. 向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
  2. 要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码

重写的hashCode()

JAVA 不重复有序的集合 java 无序可重复_User_04

重写的equals()

JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_05

JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_06

案例: User对象 重写hashCode()和equals()

JAVA 不重复有序的集合 java 无序可重复_ide_07


实现 Comparable

public class User implements Comparable{
    private String name;
    private int age;

   

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

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

        User user = (User) o;

        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

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

31最为系数,设置目的是减少哈希冲突.而且31是个素数且只占用5bit 造成数据溢出的概率很小

JAVA 不重复有序的集合 java 无序可重复_User_08

添加元素的过程:以HashSet为例:

JAVA 不重复有序的集合 java 无序可重复_数据_09


JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_10


JAVA 不重复有序的集合 java 无序可重复_数据_11

HashSet底层结构

数组+链表结构

数组初始容量为16 扩展因子0.75

JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_12


HashSet 底层 有个 HashMap

JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_13

LinkedHashSet

LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。

JAVA 不重复有序的集合 java 无序可重复_数据_14

优点:

对于频繁的遍历操作,LinkedHashSet效率高于HashSet

TreeSet

概念:

  1. 向TreeSet中添加的数据,要求是相同类的对象
  2. 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)
  3. 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
  4. 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

JAVA 不重复有序的集合 java 无序可重复_数据_15

结构:

JAVA 不重复有序的集合 java 无序可重复_ide_16

自然排序:java.lang.Comparable

  • Comparable接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo(T t)方法被称为它的自然比较方法。当前对象this与指定对象t比较“大小”,如果当前对象this大于指定对象t,则返回正整数,如果当前对象this小于指定对象t,则返回负整数,如果当前对象this等于指定对象t,则返回零。
  • 实现Comparable接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

Comparable的典型实现:

  • String:按照字符串中字符的Unicode值进行比较
  • Character:按照字符的Unicode值来进行比较
  • 数值类型对应的包装类以及BigInteger、BigDecimal:按照它们对应的数值大小进行比较
  • Date、Time等:后面的日期时间比前面的日期时间大

定制排序:java.util.Compartor

  • 强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
  • 当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序。

JAVA 不重复有序的集合 java 无序可重复_数据_17

经典试题

JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_18


JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_19


其中Person 类 重写了 hashCode 和 equal()方法

public class Person {

     int id;
     String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Person() {

    }

    @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;
    }
}

开始

JAVA 不重复有序的集合 java 无序可重复_JAVA 不重复有序的集合_20


JAVA 不重复有序的集合 java 无序可重复_User_21

set.remove(p1);

JAVA 不重复有序的集合 java 无序可重复_ide_22


JAVA 不重复有序的集合 java 无序可重复_数据_23


解析:

  • person(1001.cc)是存在 开始 penson(1001 ,aa)哈希运算后的位置上的.所以remove操作,是根据person(1001.cc)哈希运算去删除,找不到相对应的元素.

set.add(new Person(1001,“CC”));

JAVA 不重复有序的集合 java 无序可重复_ide_24


JAVA 不重复有序的集合 java 无序可重复_数据_25


解析:

  • 第一个person(1001.cc)是存在 开始 penson(1001 ,aa)哈希运算后的位置上的.
  • 第二个person(1001.cc)是存在 本身 penson(1001 ,aa)哈希运算的位置上的.

JAVA 不重复有序的集合 java 无序可重复_User_26


最后结果 是四个值

  • Person(1001,“cc”)
  • Person(1002,“BB”)
  • Person(1001,“cc”)
  • Person(1001,“AA”)