Java中的Set接口

Java中的Set接口

​ Java中Set接口继承自Collection接口,Set接口的实现类无序并且不可重复,这里的无序指的是添加的顺序和内部真实排列顺序无关。Set接口的实现类常用的有HashSet和TreeSet,它们都可以通过增强for循环和迭代器的方式进行循环遍历。


HashSet

​ HashSet底层是由HashMap的key维护的,

//创建HashSet对象,底层创建一个HashMap对象
public HashSet() {
        map = new HashMap<>();
}

​ 实现结构: 哈希表(数组+链表),jdk1.8后还通过红黑树的结构,维护哈希表,避免单条链表过长。它的特点:查询效率和增删效率都比较高,拥有数组和链表的优点。但是这种结构无序,自定义类想要实现按照一定顺序排列,并实现去重,需要重写hashCode()和equals()方法。

哈希表结构

​ Java中提供的数据类型,例如String类、包装类等等都重写了Object的hashCode()方法。hashCode()方法在Object类中定义为native本地的,直接返回对象的内存地址表示,而Integer类重写后返回的就是值本身。 HashSet在添加元素时,首先调用hashCode()方法,再根据哈希算法获得一个值,这个值就是"位桶"的索引,位桶就是哈希表中数组中的一个位置。然后看当前位置有没有储存内容,没有储存就放入这个位置,作为链表的头结点。如果有内容,在调用equals方法比较内容,相同结束,不同则顺着该结点向下查找直到找到相同结束,或者找到为null的结点插入即可。HashSet扩容是在数组储存元素大于总长度的0.75倍,则将长度扩大为2倍。这一部分配合代码或者图解比较好理解,因为HashSet底层是由HashMap的key维护的,可以看这篇自定义类实现HashMap的部分功能

自定义类重写hashCode()和equals()方法
//自定义类
public class Person implements Comparable<Person>{
	private int age;
	private int height;
	private String name;
	
	public Person() {
	}
	public Person(int age, String name, Integer height) {
		this.age = age;
		this.name = name;
		this.height = height;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getHeight() {
		return height;
	}
	public void setHeight(Integer height) {
		this.height = height;
	}
	
	//重写hashCode()方法
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + height;
		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 (age != other.age)
			return false;
		if (height != other.height)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "Person [age=" + age + ", name=" + name + ", height=" + height + "]";
	}
    
    //实现compareTo方法,根据年龄排序
	@Override
	public int compareTo(Person o) {	
		return this.age-o.getAge();
	}
}

TreeSet

​ TreeSet底层是由TreeMap的key维护的,同样是无序和不可重复。TreeSet储存的自定义类根据实现compareTo()方法或自定义比较器类,可以实现比较排序,默认升序。类似上面的Person类,实现Comparable接口,实现compareTo()方法,这是硬编码习惯,不够灵活,每次要根据需求修改源代码。也可以自定义比较器类,实现Comparator接口,在创建TreeSet时传入自定义比较器类的对象,这种问题我们也可以用Lambda表达式,可以看看我之前写的入门–Java中匿名内部类及lambda表达式–,下面根据Person类给出一个实例。

public class Test02 {
	public static void main(String[] args) {
		//lambda表达式在构造时传入参数
		TreeSet<Person> set = new TreeSet<>((o1,o2)->{
			//根据年龄、身高依次排序
			if(o1.getAge() != o2.getAge()) {
				return o1.getAge()- o2.getAge();
			}else if(o1.getHeight() != o2.getHeight()) {
				return o1.getHeight() - o2.getHeight();
			}else {
				return 0;
			}	
		}) ;	
	}
}

遍历方式

public class Test01 {
	public static void main(String[] args) {
		HashSet<Person> set = new HashSet<>();
		set.add(new Person(12,"pu",155));
		set.add(new Person(15,"liang",162));
		set.add(new Person(10,"li",150));
		
        //1、增强for循环
		for(Person p : set) {
			System.out.println(p);
		}
		
        //2、迭代器
		Iterator<Person> it = set.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值