Set接口理解 常用实现类 常用方法

本文探讨了Set接口与List接口的区别,Set接口的特点在于元素的无序性和不可重复性。主要讲解了HashSet、TreeSet和LinkedHashSet三个实现类,通过示例代码展示了它们的行为特性,特别是HashSet的元素存储和取出不一致,以及如何通过重写equals()和hashCode()方法确保对象的唯一性。

问题

首先让我们先带着几个问题来进行接下来的学习
1.Set接口和List接口的区别是什么?
2.Set接口有什么特点?
3.Set接口的实现类有哪些?

接下来正式开始分析讲解Set接口


1.Set接口和List接口的区别是什么?

  • List允许存储重复的元素, 而Set接口是不允许存在重复的元素的(元素可以为Null)
  • List存储的数据是有序的(存入和取出一致), Set接口存储的数据是无序的(存入和取出不一致)

Set接口继承了Collection接口, 其继承了Collection的所有方法, 而Set接口比较常用的实现类有HashSet, LinkedHashSet, TreeSet,我们首先来看一段代码, 来感受Set接口中的元素不可重复

以TreeSet举例
在这里插入图片描述

通过代码,我们可以很直观的看到,我已经在TreeSet中添加了三条数据,其中两条数据相同,在打印输出的时候只输出了1和2,这就是Set接口的元素不可重复性,那么它的内部是怎么实现的呢?这个问题后边会有专门的介绍哦

那么接下来我们来看一下Set无序是什么意思
这里我们使用的是HashSet
在这里插入图片描述
可以看到插入的值和取出的值是不对应的,但是要注意,HashSet集合在自己的内部是有自己的排序的,而且随着我们插入顺序的变化,它内部的排序也会发生变化


2.Set接口有什么特点?

其实这个问题在前面早已进行了解答,细心地读者想必应该已经了解了吧,没错,就是无序性不可重复性


3.Set接口的实现类基础理解

Set的实现类(常用)

  • HashSet
  • TreeSet
  • LinkedHashSet

那么首先是第一个 HashSet

此类实现Set接口,由哈希表(实际为HashMap实例)支持。 对集合的迭代次序不作任何保证; 特别是,它不能保证数据在一段时间内保持不变。 这个类允许null元素。注意:HashSet是不同步的

HashSet的方法

  • boolean add(E e)
    将指定的元素添加到此集合(如果尚未存在)。
  • void clear()
    从此集合中删除所有元素。
  • Object clone()
    返回此 HashSet实例的浅层副本:元素本身不被克隆。
  • boolean contains(Object o)
    如果此集合包含指定的元素,则返回 true 。
  • boolean isEmpty()
    如果此集合不包含元素,则返回 true 。
  • Iterator iterator()
    返回此集合中元素的迭代器。
  • boolean remove(Object o)
    如果存在,则从该集合中删除指定的元素。
  • int size()
    返回此集合中的元素数(其基数)。
  • Spliterator spliterator()
    在此集合中的元素上创建late-binding和故障快速 Spliterator 。

因为元素顺序不固定的原因,我们无法使用HashSet来直接获取想要的数据,传入元素时,调用HashCode方法获取hash值,然后决定存储位置,我们可以来总结一下,HashSet对象中不能存储相同的数据,存储数据时是无序的。但是HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同) 是按照哈希值来存的所以取数据也是按照哈希值取的

代码演示

public class SetTest {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<String>();
        // 添加元素
        set.add("c");
        set.add("b");
        set.add("a");
        // Iterator<String> it = set.iterator();   迭代器
        System.out.println(set.size());
        // 从集合中删除元素   传入的参数就是想要删除的数据
        boolean b = set.remove("a");
        System.out.println(set.size());
        // 遍历集合
        for(String s:set){
            System.out.println(s);
        }
    }
}

执行结果
在这里插入图片描述

关于HashSet一些补充

  • HashSet是没有修改方法的,因为它是无序的,所以我们不能通过索引来查找某个元素再对其进行修改
  • 在遍历的时候可以使用增强for循环或者是迭代器

那么,我们现在就来探究一下,HashSet集合到底是怎么实现了不可重复的呢?
它是发现重复元素就不会再进行添加呢?还是会覆盖前面的重复元素呢?

代码测试

import java.util.HashSet;

public class HashSetTest {
	public static void main(String[] args) {
		HashSet<Person> set = new HashSet<Person>();
		set.add(new Person("张宇"));
		set.add(new Person("张宇"));
		set.add(new Person("张宇"));
		set.add(new Person("张宇"));
		System.out.println("对象地址:"+set+"  元素个数"+set.size());
	}
}

// 创建一个新的类  尝试实现HashSet内部的去除重复效果
class Person{
	public String name;
	
	public Person(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
}

在这里插入图片描述
通过运行结果我们可以看出,就算是名字全部一样的对象,照样不被HashSet算作为重复的对象,那么如果我们想要让它们算作同一个对象,实现"去除重复"的效果,我们应该怎么做呢?不妨试一试在Person中重写equals()方法

class Person{
	public String name;
	
	public Person(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
	
	public boolean equals(Object obj) {
		// 如果对象的地址一样那么肯定就是同一个对象啊  这毋庸置疑吧  所以直接返回true就好
		if(this == obj) {
			return true; 
		}
		
		// obj如果是Person类亦或是直接间接地继承了Person类则返回true
		if(obj instanceof Person) {
			Person p = (Person) obj;// 还原类型为Person
			if(this.name.equals(p.name)) { // 判断对象中属性是否相等
				return true;
			}
		}
		return false;
	}
}

重写之后我们再进行测试
在这里插入图片描述
我们发现,结果还是无法满足我们的要求,是我们重写的equals有问题吗?
在这里插入图片描述
新建了两个对象调用重写后的equals()方法进行测试,返回的结果是true,是没问题的,那么就是说在HashSet内部不止这一种无重复原则,还有其他的方法来判断是否重复,那么再重写equals()通常会伴随着另一个方法,就是hashCode方法,那我们就再重写hashCode方法来验证以下猜想是否正确

再次重写方法 HashCode()

class Person{
	public String name;
	
	public Person(String name) {
		this.name = name;
	}
	
	public boolean equals(Object obj) {
		// 如果对象的地址一样那么肯定就是同一个对象啊  这毋庸置疑吧  所以直接返回true就好
		if(this == obj) {
			return true; 
		}
		
		// obj如果是Person类亦或是直接间接地继承了Person类则返回true
		if(obj instanceof Person) {
			Person p = (Person) obj;// 还原类型为Person
			if(this.name.equals(p.name)) { // 判断对象中属性是否相等
				return true;
			}
		}
		return false;
	}
	
	public int hashCode() {
		//如果两个对象的name返回的HashCode码一致,我们就认为它们两个是相同的
		return this.name.hashCode();
	}
	
	public String getName() {
		return this.name;
	}
	
}

执行测试
在这里插入图片描述
现在结果是不是就和我们想要的结果一致了

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值