Set接口

Set接口

  • 特点:无序、不允许重复,是Collection接口的子接口
  • 没有定义新方法,所有的方法都是Collection接口中所定义的方法

实现类

  • HashSet存储采用哈希表的方式进行存储,HashSet采用HashCode算法来存取集合中的元素,因此具有比较好的读取和查找性能
  • LinkedHashSet是在HashSet的基础上添加一个额外的链表结构可以记录存储数据的顺序
  • TreeSet采用的是树状结构进行数据存储

HashSet

  • 底层实现方法:存储到Set中的所有数据最终都存储在一个HashMap中,其中存储的数据采用key的方式进行存储,值为PRESENT常量

常用算法

  • boolean add(E e)向集合Set中添加元素,注意不保证顺序
    • 同一个内容的对象,为什么没有出现覆盖的效果?(设置hashCode和equals方法的调用)
Set<Person> set=new HashSet<>();
Person p1=new Person(1L,"aa");
Person p2=new Person(1L,"aa");
System.out.println(p1==p2);//false,因为p1、p2是两个对象
set.add(p1);
set.add(p2);
System.out.println(set.size);//2

比对两个对象相等,调用流程为:

  • 调用对象的hashcode方法,如果hashCode不相等则返回,认为两个对象不相等。

  • 如果hash值相等则调用equals判断

  • 潜规则要求:定义类时需要定义对应的hashCode和equals方法,要求:当equals为true时,hash值必须相等;当hash值相等时不一定equals为true

package come.day0307;

import java.util.HashSet;
import java.util.Set;

public class A{
	public static void main(String args[]){
	Set<Person> set=new HashSet<>();
	Person p1=new Person(1L,"aa");
	Person p2=new Person(1L,"aa");
	System.out.println(p1==p2);//false,因为p1、p2是两个对象
	set.add(p1);
	set.add(p2);
	System.out.println(set.size());//1
	System.out.println(set);
	}
}
class Person{
	
	private long id;
	private String name;
	
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}
	public Person() {}
	public Person(long id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		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;
		Person other = (Person) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

//输出
//false
//1
//[Person [id=1, name=aa]]

  • boolean remove(Object o) 删除指定对象,同样需要hashCode和equals方法
  • void clear()清空集合中的所有元素
  • boolean contains(Object o)判断集合中是否有指定的对象,同样需要hashCode和equals方法
  • int size()获取集合中的元素个数
  • Iterator<E> iterator()用于遍历所存储的数据
Set<String> set = new LinkedHashSet<>();
		set.add("abcd");
		set.add("123");
		Iterator<String> it=set.iterator();
		while(it.hasNext()) {
			String tmp=it.next();
			System.out.println(tmp);
		}

HashSet的特征

  • 无序:不仅不能保证元素插入的顺序(如果需要顺序则可以使用LinkedHashSet),而且在元素在以后的顺序中也可能变化(这是由HashSet按HashCode存储对象(元素)决定的,对象变化则可能导HashCode变化)
    • 如果需要访问的顺序和插入的顺序一致,可以使用HashSet的子类LinkedHashSet
  • 不允许重复 [equals和hashcode]
  • 结论:当HashSet判定对象重复时,首先调用的是对象的hashCode方法,如果两个对象的hashCode值相同时,才调用equals进行判定。如果hashCode值不相等则不会调用equals判断。如果hashcode相等而且equals为true,则后盖前。
  • HashSet是线程非安全的,方法上没有同步约束
  • HashSet元素值可以为NULL

LinkedHashSet

  • LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素
  • 在HashSet的基础上添加了一个链表结构记录存取的顺序

TreeSet

  • TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合
  • 在map中以key为需要存放的数据,以PERSENT常量为值存放数据

内部实现

  • 底层是用TreeMap实现的,本质上是一个红黑树原理。正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(),
    last(), lower(), higher(), subSet(), headSet(), tailSet()
**基本用法**
Set<Integer> set = new TreeSet<Integer>();
Random r = new Random();
for (int i = 0; i < 10; i++)
	set.add(r.nextInt(100));
set.forEach(System.out::println);

TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。

public class A{
	public static void main(String args[]){
	Set<Person> set=new TreeSet<Person>();
	Person p1=new Person(1L,"aa");
	Person p2=new Person(1L,"aa");
	set.add(p1);
	set.add(p2);
	System.out.println(set.size());
	}
}
class Person{
	private long id;
	private String name;
	public Person(long id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
}

执行报错,原因是:添加到TreeSet中要求对象必须是可比较的

  • 注意: 要求添加到TreeSet中的元素类型必须实现Comparable接口
public class A {
	public static void main(String[] args) {
		Set<Person> set=new TreeSet<Person>();
		Person p1=new Person(1L,"aa");
		Person p2=new Person(1L,"aa");
		set.add(p1);
		set.add(p2);
		System.out.println(set.size());
		System.out.println(p1==p2);
		System.out.println(p1.equals(p2));
	}
}

class Person implements Comparable<Person>{
	private Long id;
	private String name;
	public Person(long id, String name) {
		this.id=id;
		this.name=name;
	}
	@Override
	public int compareTo(Person o) {
		//判空处理省略
		int res=name.compareTo(o.name);
		if(res==0) {
			res=id.compareTo(o.id);
		}
		return res;
	}

}

如果使用TreeSet时不会依靠hashcode和equals进行比较,相等性判断是依靠compareTo实现的

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值