【Java学习笔记】Set接口及其实现类

目录

1.Set接口概述

2.HashSet集合

2.1 概述

2.2 HashSet如何保证元素唯一性

2.3 HashSet案例

3.LinkedHashSet集合

4.TreeSet集合

4.1 TreeSet存储Integer类型的元素并遍历

4.2 TreeSet存储自定义对象并遍历


1.Set接口概述

一个不包含重复元素的Collection。

2.HashSet集合

2.1 概述

  • 不保证set的迭代顺序
  • 特别是它不保证该顺序恒久不变

2.2 HashSet如何保证元素唯一性

  • HashSet集合底层依赖哈希表结构
  • 而哈希表结构底层依赖hashcode()方法和equals()方法

2.3 HashSet案例

2.3.1 HashSet存储字符串并遍历

HashSet存储字符串并遍历,输出结果元素唯一,是因为String类重写了hashcode()方法和equals()方法,所以可以把内容相同的字符串去掉,保证元素唯一性。

底层原理分析如下:

  • 首先比较hashcode()值是否相同
  • 如果相同,继续走equals()方法
    • 返回true,说明元素重复,就不添加
    • 返回false,说明元素不重复,就添加到集合中
  • 如果不同,直接把元素添加到集合
// 创建集合对象
HashSet<String> hs = new HashSet<String>();

// 添加元素
hs.add("Python");
hs.add("Fiddler");
hs.add("Selenium");
hs.add("Fiddler");

// 增强for遍历集合
for (String str : hs) {
	System.out.println(str);
}

运行结果:

Selenium
Fiddler
Python

2.3.2 HashSet存储自定义对象并遍历

  • Student(name, age)
  • 要求:如果两个对象的成员变量值都相同,则为同一个元素。

Student.java的代码如下:

package com.hw.set;

public class Student {
	private String name;
	private int age;

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = 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;
	}

}

 测试代码如下:

// 创建集合对象
HashSet<Student> hs = new HashSet<Student>();

// 创建学生对象
Student s1 = new Student("西施", 20);
Student s2 = new Student("貂蝉", 22);
Student s3 = new Student("昭君", 18);
Student s4 = new Student("貂蝉", 22);

// 把学生对象添加到集合中
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);

// 增强for遍历集合
for (Student stu : hs) {
	System.out.println(stu.getName() + "---" + stu.getAge());
}

运行结果如下:

西施---20
昭君---18
貂蝉---22
貂蝉---22

案例要求如果两个对象的成员变量值都相同,则为同一个元素,实际应该输出三个学生对象,然而四个学生对象全部输出了。原因是Student类没有重写hashcode()方法和equals()方法,默认使用的是Object类的这两个方法,这个时候学生的哈希值是不一样的,所以这四个学生对象会全部输出。

解决方法:在Student类中重写hashcode()方法和equals()方法。

重写hashcode()和equals()的Student.java的代码如下:

package com.hw.set;

public class Student {
	private String name;
	private int age;

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = 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;
	}

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

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

}

3.LinkedHashSet集合

  • LinkedHashSet底层数据结构由哈希表和链表组成
  • 由哈希表表示元素的唯一性
  • 链表保证元素有序(存储顺序和取出顺序一致)
package com.hw.set;

import java.util.LinkedHashSet;

/**
 * LinkedHashSet存储字符串并遍历
 * 
 * @author HW
 * 
 */
public class LinkedHashSetDemo {
	public static void main(String[] args) {
		// 创建集合对象
		LinkedHashSet<String> lhs = new LinkedHashSet<String>();

		// 添加元素
		lhs.add("Python");
		lhs.add("Fiddler");
		lhs.add("Selenium");

		// 增强for循环遍历集合
		for (String str : lhs) {
			System.out.println(str);
		}
	}
}

运行结果如下:

Python
Fiddler
Selenium

4.TreeSet集合

TreeSet能够对元素按照某种规则进行排序。

排序有两种,使用哪一种方法,取决于使用的构造方法,当构造方法为空时,使用自然顺序进行排序。

  • 自然排序:让元素所属的类实现自然排序接口Comparable<T>,下面先来了解一下Comparable<T>接口以及compareTo()方法:
    • public interface Comparable<T>:此接口强行对实现它的每个类的对象进行整体排序,这种排序被称为类的自然排序。类的compareTo()方法被称为类的自然比较方法。
    • int compareTo(T o):如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
  • 比较器排序:让集合的构造方法接收一个比较器接口的子类对象Comparator

TreeSet的底层是二叉树结构,保证元素排序和唯一。

4.1 TreeSet存储Integer类型的元素并遍历

// 创建集合对象
TreeSet<Integer> ts = new TreeSet<Integer>();
		
// 添加元素
ts.add(20);
ts.add(23);
ts.add(15);
ts.add(6);
ts.add(18);
ts.add(23);
		
// 增强for遍历集合
for (Integer i : ts) {
	System.out.println(i);
}

运行结果:

6
15
18
20
23

4.2 TreeSet存储自定义对象并遍历

  • Student(name, age)
  • 按照年龄从小到大排序

4.2.1 使用自然排序 

要想使用自然排序对学生对象排序,那么Student类就要实现Comparable<T>接口,并重写compareTo()方法。如果Student类没有实现Comparable<T>接口,运行结果就会报错:Exception in thread "main" java.lang.ClassCastException: com.hxs.day06.Student cannot be cast to java.lang.Comparable,如下所示:

下面在Student类中实现这一接口并重写compareTo()方法:

package com.hw.treeset;

/**
 * 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
 * 
 * @author HW
 * 
 */
public class Student implements Comparable<Student> {
	private String name;
	private int age;

	public Student() {
		super();
	}

	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = 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 int compareTo(Student s) {
		// 主要条件,按照年龄排序
		int num = this.age - s.age;
		// 次要条件,年龄相同的时候,比较姓名是否相同
		// 如果年龄和姓名都相同,才是同一个元素
		int num2 = num == 0 ? this.name.compareTo(s.name) : num;
		return num2;
	}
}

测试类代码如下:

package com.hw.treeset;

import java.util.TreeSet;

import com.hw.set.Student;

/**
 * TreeSet存储自定义对象并遍历
 * 自然排序,按照年龄从小到大排序
 * 
 * @author HW
 * 
 */
public class TreeSetDemo2 {
	public static void main(String[] args) {
		// 创建集合对象
		TreeSet<Student> ts = new TreeSet<Student>();

		// 创建学生对象
		Student s1 = new Student("linqingxia", 27);
		Student s2 = new Student("zhangguorong", 29);
		Student s3 = new Student("wanglihong", 23);
		Student s4 = new Student("linqingxia", 27);
		Student s5 = new Student("liushishi", 22);
		Student s6 = new Student("wuqilong", 40);
		Student s7 = new Student("fengqingy", 22);

		// 把学生对象添加到集合中
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);
		ts.add(s7);

		// 增强for遍历集合
		for (Student stu : ts) {
			System.out.println(stu.getName() + "---" + stu.getAge());
		}

	}
}

运行结果如下:

fengqingy---22
liushishi---22
wanglihong---23
linqingxia---27
zhangguorong---29
wuqilong---40

 4.2.2 比较器排序

让集合的构造方法接收一个比较器接口的子类对象Comparator。

package com.hw.treeset;

import java.util.Comparator;
import java.util.TreeSet;

import com.hw.set.Student;

/**
 * TreeSet存储自定义对象并遍历 
 * 比较器排序
 * 
 * @author HW
 * 
 */
public class TreeSetDemo3 {
	public static void main(String[] args) {
		// 创建集合对象
		// 如果一个方法的参数是接口,那么真正要的是接口的实现类的对象
		// 而匿名内部类就可以实现这个东西
		TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {

			public int compare(Student s1, Student s2) {
				int num = s1.getAge() - s2.getAge();
				int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
						: num;
				return num;
			}

		});

		// 创建学生对象
		Student s1 = new Student("linqingxia", 27);
		Student s2 = new Student("zhangguorong", 29);
		Student s3 = new Student("wanglihong", 23);
		Student s4 = new Student("linqingxia", 27);
		Student s5 = new Student("liushishi", 22);
		Student s6 = new Student("wuqilong", 40);
		Student s7 = new Student("fengqingy", 22);

		// 把学生对象添加到集合中
		ts.add(s1);
		ts.add(s2);
		ts.add(s3);
		ts.add(s4);
		ts.add(s5);
		ts.add(s6);
		ts.add(s7);

		// 增强for遍历集合
		for (Student stu : ts) {
			System.out.println(stu.getName() + "---" + stu.getAge());
		}
	}
}

​​​​​运行结果如下:

liushishi---22
wanglihong---23
linqingxia---27
zhangguorong---29
wuqilong---40

猜你喜欢

转载自blog.csdn.net/weixin_44679832/article/details/105461598