Skip to main content

Set接口

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

HashSet类

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

集合特点

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

无下标;

键唯一;

允许null元素;

基本使用

构造方法

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

// 构造一个新的空集合; 背景HashMap实例具有指定的初始容量和指定的负载因子
HashSet(int initialCapacity, float loadFactor)

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

add()

作用:向列表中添加元素。

参数:元素Object;

返回值:布尔值

示例:

// 创建HashSet列表
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
System.out.println("reine = " + reine); // reine = true

clear()

作用:清空列表元素。

参数:无;

返回值:无

示例:

// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 清空列表
hashSet.clear();

contains()

作用:列表内是否包含元素。

参数: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()

作用:列表是否为空。

参数:无;

返回值:布尔值

示例:
// 创建HashSet列表 指定键的泛型为String
HashSet<String> hashSet = new HashSet<String>();
// 向列表中添加元素
boolean reine = hashSet.add("Reine");
// 列表是否为空
boolean empty = hashSet.isEmpty();
System.out.println("empty = " + empty); // false

remove()

作用:删除列表中的元素。

参数: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()

作用:获取迭代器对象

参数:无;

返回值:迭代器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()

作用:获取列表中元素数量

参数:无;

返回值:int类型;

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

遍历集合

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

// 创建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遍历

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

源码解析

构造函数

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

add方法直接调用put方法

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

LinkedHashSet类

  • LinkedHashSet继承自HashSet类。

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

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

基本使用

构造方法

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

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

方法

add()

作用:向列表中添加元素。

参数:元素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方法名格式)

集合特点

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

双向链表;

源码解析

构造方法的源码

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

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

添加元素的源码

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

TreeSet类

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

基本使用

构造方法

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


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

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

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("---------------------------------------------------------");
}
}

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();
}
}

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

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());
}
}

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

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()

作用:向列表中添加元素。

参数:元素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()

作用:返回集合中的第一个元素。

参数:无;

返回值: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方法名格式)。

集合特点

有序;

双向链表;

源码解析

构造函数

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

元素添加时

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

Set去重

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

去除的规则

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

HashSet类中去重过程示例:

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

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方法

原因:

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

重写hashCode方法之后:

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

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