Set<E>之实现类HashSet&TreeSet&LinkedHashSet浅析

本文介绍了Java中Set接口的基本概念及其实现类HashSet、LinkedHashSet、TreeSet的特点与应用场景。详细解释了不同Set实现如何保证元素的唯一性,以及它们之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简要介绍:

public interface Set<E> extends Collection<E>

一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。

在所有构造方法以及 addequalshashCode 方法的协定上,Set 接口还加入了其他规定,这些规定超出了从 Collection 接口所继承的内容。出于方便考虑,它还包括了其他继承方法的声明(这些声明的规范已经专门针对 Set 接口进行了修改,但是没有包含任何其他的规定)。

对这些构造方法的其他规定是(不要奇怪),所有构造方法必须创建一个不包含重复元素的 set(正如上面所定义的)。 (引用JDK_API_1.6版本)

1.Set接口的基础特点

①Set这个接口没有扩展Collection的方法,但它的实现类们对Collection的方法更为严格;

②Set系列的集合中的元素是不能重复 ;

代码演示1:

在这里插入图片描述

③Set和Collection一样,支持foreach、Iteratord迭代器;

④Set的实现类有:HashSet、TreeSet、LinkedHashList

2.为什么我们不讨论Set的顺序?

因为Set有序无序有争议,从存储的结构、物理的结构来说,是无序的;从遍历的结果来说,是有序的,其中TreeSet是按照大小顺序,LinkedHashSet是按照添加的顺序

代码演示2:

在这里插入图片描述

3.Set的不可重复性表现在哪?

HashSet :添加到HashSet中的元素,不可重复,依赖hashCode和equals方法;

代码演示3:

package com.daxia.case4;
import java.util.HashSet;
import org.junit.Test;
/**/
public class SetTest02 {
	@Test
	public void test02() {
		// 此时泛型是Student,引用数据类型
		HashSet<Student> set = new HashSet<>();
		set.add(new Student("1", "大侠"));
		set.add(new Student("1", "大侠"));
		set.add(new Student("1", "大侠"));
		/* 如果没有重写hashCode和equals方法,等价于"==" */
		// System.out.println("元素的个数:"+set.size());//元素的个数:3
		/* 如果重写hashCode和equals方法,就按重写的来比较 */
		System.out.println("元素的个数:" + set.size());// 元素的个数:1
	}
}

class Student {
	private String id;
	private String name;

	public Student(String id, String name) {
		super();
		this.id = id;
		this.name = name;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		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 (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}

LinkedHashSet :添加到LinkedHashSet中的元素,不可重复,依赖于hashCode和equals方法;

代码演示4(原理同上):

@Test
	public void test03() {
		// 此时泛型是Student,引用数据类型
		LinkedHashSet<Student> set = new LinkedHashSet<>();
		set.add(new Student("1", "大侠"));
		set.add(new Student("1", "大侠"));
		set.add(new Student("1", "大侠"));
		/* 如果没有重写hashCode和equals方法,等价于"==" */
		// System.out.println("元素的个数:"+set.size());//元素的个数:3
		/* 如果重写hashCode和equals方法,就按重写的来比较 */
		System.out.println("元素的个数:" + set.size());// 元素的个数:1
	}

TreeSet:

(1)添加到TreeSet中的元素必须实现Comparble接口,元素不可重复调用compareTo()比较的,它认为大小相等的

就是重复的元素;

代码演示5:

public class SetTest02 {
	@Test
	public void test03() {
		//如果学生的对象没有实现java.lang.comparable接口,就不能添加到TreeSet中
		//TreeSet是要按元素的大小顺序存储和遍历,无法比较大小
		TreeSet<Student> set = new TreeSet<>();
		set.add(new Student(1, "大侠"));
		set.add(new Student(1, "大侠"));
		set.add(new Student(1, "大侠"));
		
		System.out.println("元素的个数:" + set.size());// 元素的个数:1
	}
}

class Student implements Comparable<Student> {
	private int id;
	private String name;
	public Student(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public int compareTo(Student o) {
		return this.id-o.id;
	}
}

(2)如添加到TreeSet中的元素没有实现Comparable接口,或者实现Comparable接口的方式不满足当前的排序需

求,那么需要给TreeSet指定一个Comparator定制比较器对象。

代码演示6:

public class SetTest02 {
	@Test
	public void test03() {
		// 如果学生的对象没有实现java.lang.comparable接口,
		//指定一个<font color =red size=3>Comparator定制比较器</font>对象
		TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				return o1.getId() - o2.getId();
			}
		});
		set.add(new Student(1, "大侠"));
		set.add(new Student(1, "大侠"));
		set.add(new Student(1, "大侠"));

		System.out.println("元素的个数:" + set.size());// 元素的个数:1
	}
}
class Student {
	private int id;
	private String name;

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

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

①HashSet:依赖元素hashCode()和equals()方法,底层存储的是哈希表

②TreeSet:依赖元素compareTo()和定制比较器compare(),只有当你要求元素按照大小

顺序(Comparable|Comparator),并不可重复,才使用它;

③LinkedHashSet:依赖元素hashCode()和equals()方法;它是HashSet的子类;比HashSet中的结点类型,多维

护了一个前后结点的引用。

(1)场景应用:

如果既要保证元素的添加的顺序(有序),又要保证元素不可重复,那么就选择LinkedHashSet

如果不需要保证元素添加顺序(无序),只是不可重复,就用HashSet。因为如果你每次添加、删除的时

候,还要维护前后元素的关系,效率就比较低。

(2)Set系列其实本质上就是Map

①HashSet内部是HashMap,添加到HashSet中的元素是作为HashMap的key,value使用一个Object;

②TreeSet内部是TreeMap,添加到TreeSet中的元素是作为TreeMap的key,value使用一个Object;

③LinkedHashSet内部是LinkedHashMap,添加到LinkedHashSet中的元素是作为LinkedHashMap的key,value使用一个Object。

推荐阅读往期博文:

JavaSE集合篇#Collection&Map等系列#结构关系图解

JavaSE集合篇#List之实现类ArrayList&Vector&LinedList&Stack浅析

JavaSE集合篇#Map集合之实现类HashMap&Hashtable&TreeMap&LinkedHashMap&Properties浅析

#轻松一刻

在这里插入图片描述


☝上述分享来源个人总结,如果分享对您有帮忙,希望您积极转载;如果您有不同的见解,希望您积极留言,让我们一起探讨,您的鼓励将是我前进道路上一份助力,非常感谢!我会不定时更新相关技术动态,同时我也会不断完善自己,提升技术,希望与君同成长同进步!

☞本人博客:https://coding0110lin.blog.youkuaiyun.com/  欢迎转载,一起技术交流吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值