Skip to content

Set接口

父类是Collect接口。不允许重复元素。

HashSet类

实现Set接口,底层由HashMap实现,当使用构造方法创建一个HashSet对象时,底层会使用HashMap的构造方法创建一个对象给HashSet使用。 只使用HashMap集合对象中的键的位置,值的位置都设置为null。代码复用

集合特点

无序;(只能通过迭代器遍历)

无下标;

键唯一;

允许null元素;

基本使用

构造方法

java
无参构造:源代码直接使用返回的是一个 HashMap 类型对象
// 构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。 
HashSet() 
    
// 构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子
HashSet(int initialCapacity, float loadFactor)

方法:此类中没有单个查询的方法 也没有修改的方法,只能遍历得到元素

add()

java
作用:向列表中添加元素。
  
参数:元素Object;
      
返回值:布尔值
    
示例:
    
// 创建HashSet列表
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
System.out.println("reine = " + reine); // reine = true

clear()

java
作用:清空列表元素。
  
参数:无;
      
返回值:无
    
示例:
    
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 清空列表
hashSet.clear();

contains()

java
作用:列表内是否包含元素。
  
参数:Object;
      
返回值:布尔值
    
示例:
    
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 列表中包含与否
boolean res = hashSet.contains("Reine");
System.out.println("res = " + res);

isEmpty()

java
作用:列表是否为空。
  
参数:无;
      
返回值:布尔值
    
示例:
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 列表是否为空
boolean empty = hashSet.isEmpty();
System.out.println("empty = " + empty); // false

remove()

java
作用:删除列表中的元素。
  
参数:object元素;
      
返回值:布尔值,成功为true,否则为false
    
示例:

// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 列表中删除元素
boolean res = hashSet.remove("Reine");
System.out.println("res = " + res); // res = true

iterator()

java
作用:获取迭代器对象
  
参数:无;
      
返回值:迭代器Iterator对象;
    
示例:
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 遍历集合
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
    String item = iterator.next();
    System.out.println("item = " + item);

}

size()

java
作用:获取列表中元素数量
  
参数:无;
      
返回值:int类型;
    
示例:
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 获取列表中数量
int size = hashSet.size();
System.out.println("size = " + size);

遍历集合

方式:通过迭代器方式遍历集合

java
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 遍历集合
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()){
    String item = iterator.next();
    System.out.println("item = " + item);

}

方式:通过增强for遍历

java
// 通过增强for遍历
for (String s : hashSet) {
    System.out.println("s = " + s);
}

源码解析

构造函数

java
// 直接new一个HashMap对象
public HashSet() {
    map = new HashMap<>();
}

add方法直接调用put方法

java
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

LinkedHashSet类

  • LinkedHashSet继承自HashSet类。

  • LinkedHashSet底层实现代码复用的是LinkedHashMap类的代码。

  • HashSet类中的方法都可以使用。

基本使用

构造方法

java
// 构造一个具有默认初始容量(16)和负载因子(0.75)的新的,空的链接散列集
LinkedHashSet()
    
// 构造具有指定的初始容量和负载因子的新的,空的链接散列集
LinkedHashSet(int initialCapacity, float loadFactor)

方法

add()

java
作用:向列表中添加元素。
  
参数:元素Object;
      
返回值:布尔值
    
示例:
// 创建对象
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
// 添加元素
linkedHashSet.add("Reine");
// 遍历集合
Iterator<String> iterator = linkedHashSet.iterator();
while (iterator.hasNext()){
    String item = iterator.next();
    System.out.println("item = " + item); // item = Reine
}

...

方法的使用如同LinkedHashMap类中的方法一致。(方法名有所不同,使用的仍是HashSet方法名格式)

集合特点

有序;(顺序为元素插入的顺序)

双向链表;

源码解析

构造方法的源码

java
public LinkedHashSet() {
    super(16, .75f, true);
}

// super 指向 HashSet构造,创建一个LinkedHashMap对象
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

添加元素的源码

java
// 复用HashMapMap中的put方法
public boolean add(E e) {
	return map.put(e, PRESENT)==null;
}

TreeSet类

有序的Set集合,顺序为元素比较的顺序,父接口为Set接口,底层代码复用的是TreeMap类的代码

基本使用

构造方法

java
无参构造方法:通过对象类本身实现Comparable接口,重写比较方法 如同 TreeMap的使用方式。

    
有参构造方法:通过自定义比较器类,实现Comparator接口,重写比较方法 如同 TreeMap的使用方式。

示例:无参构造方法,通过对象类本身实现接口

java
package com.collectPart.SetPart;

import java.util.TreeSet;

public class TestTreeSet {
    public static void main(String[] args) {
        // 无参构造,使用String类中自带的方法
        TreeSet<String> set = new TreeSet<String>();
        // 添加元素
        set.add("a");
        set.add("ad");
        set.add("ae");
        set.add("ab");
        set.add("ac");
        set.add("af");

        // 增强for循环遍历
        for (String s : set) {
            System.out.println("s = " + s);
        }

        System.out.println("---------------------------------------------------------");

        // 创建对象,无参构造,值是Student自定义对象类型
        TreeSet<Student> stuSet = new TreeSet<>();
        // 创建学生对象 作为添加元素
        Student stu1 = new Student("赵四",26);
        Student stu2 = new Student("广坤",24);
        Student stu3 = new Student("大拿",22);
        Student stu4 = new Student("小宝",23);


        // 添加元素
        stuSet.add(stu1);
        stuSet.add(stu2);
        stuSet.add(stu3);
        stuSet.add(stu4);

        // 打印集合长度
        System.out.println(stuSet.size());

        System.out.println("---------------------------------------------------------");
    }
}
java
package com.collectPart.SetPart;

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Student implements  Comparable<Student> {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    /**
     *  重写 compareTo方法  自定义比较规则
     * @param stu the object to be compared.
     * @return
     */
    @Override
    public int compareTo(Student stu) {
        //        if(this.getAge() > stu.getAge()){ // 当前对象的年龄大于传入对象的年龄
        //            return 1; // 返回正数
        //        }else if(this.getAge() < stu.getAge()){ // 当前对象的年龄小于传入对象的年龄
        //            return -1; // 返回负数
        //        }
        //        return 0; // 以上条件都不成立 表示两个对象的年龄相等 则 返回0


        // return  this.getAge() > stu.getAge() ? 1 :  (this.getAge() < stu.getAge() ? -1  : 0) ;
        return stu.getAge() - this.getAge();
    }
}

示例:有参构造方法,通过自定义比较器类,实现接口

java
package com.collectPart.SetPart;

import java.util.TreeSet;

public class TestTreeSet {
    public static void main(String[] args) {
        // 创建集合对象 指定比较器对象
        TreeSet<Person> personSet = new TreeSet<>(new PersonComparator());
        // 创建Person对象 作为添加元素
        Person p1 = new Person("赵四", 188);
        Person p2 = new Person("小宝", 199);
        Person p3 = new Person("刘能", 155);
        Person p4 = new Person("大拿", 175);

        // 添加元素
        personSet.add(p1);
        personSet.add(p2);
        personSet.add(p3);
        personSet.add(p4);

        // 打印集合长度
        System.out.println(personSet.size());
    }
}
java
package com.collectPart.SetPart;

public class Person {
    private String name;
    private double height;

    public Person(String name, double height) {
        this.name = name;
        this.height = height;
    }

    public Person() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
}
java
package com.collectPart.SetPart;

import java.util.Comparator;

public class PersonComparator implements Comparator<Person> {
    /**
     *  重写 compare方法  自定义比较规则  最终返回值为int类型
     * @param p1 the first object to be compared.
     * @param p2 the second object to be compared.
     * @return
     */
    @Override
    public int compare(Person p1, Person p2) {
        // p1大于p2 返回正数  p1小于p2 返回负数  p1==p2 返回0
        return  p1.getHeight() > p2.getHeight() ? 1 :  (p1.getHeight() < p2.getHeight() ? -1  : 0) ;
    }
}

方法

add()

java
作用:向列表中添加元素。
  
参数:元素Object;
      
返回值:布尔值
    
示例:
    
// 无参构造
TreeSet<String> set = new TreeSet<String>();
// 添加元素
set.add("sakuna");
set.add("yousa");
set.add("xusong");

// 增强for循环遍历
for (String s : set) {
    System.out.println("s = " + s);
}

first()

java
作用:返回集合中的第一个元素。
  
参数:无;
      
返回值:Object元素
    
示例:
    
// 无参构造
TreeSet<String> set = new TreeSet<String>();
// 添加元素
set.add("sakuna");
set.add("yousa");
set.add("xusong");
// 查找第一个元素
String first = set.first();
System.out.println("first = " + first); // first = sakuna

...

方法如同TreeMap类中的方法使用(虽然代码调用的是TreeMap,但是方法名有所不同,使用的仍是HashSet方法名格式)。

集合特点

有序;

双向链表;

源码解析

构造函数

java
public TreeSet() {
    this(new TreeMap<E,Object>());
}
// TreeSet
TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
}
// 返回的是 TreeMap对象
public TreeSet() {
    this(new TreeMap<E,Object>());
}

元素添加时

java
// 底层使用的TreeMap中的put方法,但是在Set集合中使用的方法名是 add方法
public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

Set去重

  • set集合去重过程就相当于HashMap类中去除重复的键的过程。

去除的规则

两个对象的equals比较为true,并且hashCode相同,认为是重复的对象

HashSet类中去重过程示例:

java
package com.collectPart.SetPart;

import java.util.Objects;

public class Student implements  Comparable<Student> {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    // 重写方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    // 重写比较顺序方法
    @Override
    public int compareTo(Student stu) {
        // return  this.getAge() > stu.getAge() ? 1 :  (this.getAge() < stu.getAge() ? -1  : 0) ;
        return stu.getAge() - this.getAge();
    }
    // 重写equals方法

    // 重写equals和HashCode方法,使用 alt + insert 快捷键添加的,详细见方法重写章节
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
java
package com.collectPart.SetPart;

import java.util.HashSet;

public class TestSetRmDup {
    public static void main(String[] args) {
        // 创建HashSet接口
        HashSet<Student> hashSet = new HashSet<Student>();
        // 创建对象
        Student stu1 = new Student("九月",16);
        Student stu2 = new Student("九月",16);
        Student stu3 = new Student("九月",16);
        // 添加三个元素,这三个元素是重复的,在未重写 equals 方法之前,这三个都可以添加进去

        // 使用equals比较是否为true,如果Student类重写equals则使用Student重写后的;如果没有重写,则用父类(默认比较对象地址)
        boolean stu12 = stu1.equals(stu2);
        boolean stu13 = stu1.equals(stu3);
        System.out.println("stu12 = " + stu12); // 未重写前 false; 重写后 true
        System.out.println("stu13 = " + stu13); // 未重写前 false; 重写后 true

        // 打印hash值,调用hashCode方法,如果Student类重写hashCode方法则使用本类重写的方法; 如果没有重写则用父类;
        int stu1Code = stu1.hashCode();
        int stu2Code = stu2.hashCode();
        int stu3Code = stu3.hashCode();
        System.out.println("stu1Code = " + stu1Code); // 未重写前 1163157884; 重写后 相同 20097254
        System.out.println("stu2Code = " + stu2Code); // 未重写前 1956725890; 重写后 相同 20097254
        System.out.println("stu3Code = " + stu3Code); // 未重写前 356573597; 重写后 相同 20097254

        // 未重写 equals和hashCode方法,会被认为是不同的对象,可以被添加到列表中

        hashSet.add(stu1);
        hashSet.add(stu2);
        hashSet.add(stu3);

        // 遍历
        for (Student student : hashSet) {
            System.out.println(student); // 未重写前列表汇中有三个;重写后只有一个;
        }
    }
}

面试题:

为什么重写 equals,必须重写HashCode方法

java
原因:

现在我们重写了equals改变了对象的比较规则,所以应当继续重写hashCode,以维持两个对象equals比较为true,则hashCode必须是相同的,哈希值的计算运用了equals方法的比较规则。

重写hashCode方法之后:

java
// 只重写equals方法时,相同的元素还是可以添加进去的。
// 相同的元素,在重写了HashCode方法之后,相同的元素只能添加进去一个。

两个对象调用equals方法比较为true;并且调用hashCode方法hashCode值相同才认为是重复的对象;