Set集合

Set是Collection子接口.模拟了数学上的集合概念
set集合存储特点:

  • 不允许元素重复,当添加两个一样的元素的时候,添加会失败,add()方法返回false,判断两个对象是否一样使用equals()而不是使用==,返回true的话就是一样的则无法添加到集合.
  • 不会记录元素的添加循序,它会按照H内部的hash算法排序
在HashSet中如何判断两个对象是否相等的问题
  • 两个对象的equalse比较相等,返回true,则说明是相同的对象
  • 两个对象的hashCode方法返回值相等
    对象的HashCode值决定的在哈希表中的存储位置
    二者缺一不可
    当往HashSet集合中添加新的对象的时候,先判断该对象和集合中的对象hashCode值
  • 不等直接把新的对象储存在hashCode指定的位置
  • 相等,在判断新的对象和集合中对象的equals作比较
    1.hashCode相同,equals为true,则视为同一个对象,不保存在哈希表中
    2.hashCode相同,但是equals不同,存储在之前对象同槽位的链表上(非常麻烦)
    对象的hashCode和equals方法的重要性
    每一个存储到哈希表中的对象都得提供HashCode和equals方法来判断是否是同一个对象
    存储在Hash表中的对象应该覆盖equals方法和hashCode方法并且在equals相等的同时hashCode也应该相等(在我们自定一对象类的时候需要注意)
hashSet比较对象的具体步骤

当向hashSet集合中存入一个新的元素的时候,HashSet会先调用对象的HashCode方法来得到该对象的HasCode值,然后决定该对象在HashSet中的存储位置
如果两个元素在equals比较返回为true,但是它们的HashCode值返回不同,则它们HashSet会把两个元素储存在不同位置.

不同的数据如何计算hashCode值

开发工具中可以自动生成equals和hashCode方法
如果需要把我们自定义的对象存储到Hash表中,该类型的对象应该覆盖掉equals方法和hashCode方法,并且在该方法中提供自己的判断规则,可以使用eclipse自动生成方法

LinkedHashSet

底层有Hash算法和链表算法两个算法
hash表:用来保证唯一性
链表:用来记录元素的先后循序
是HashSet集合的子类,它在Set中的关系是这个样子
LinkedHashSet→HashSet→Set

特点

不允许重复,但是记录数添加循序

TreeSet

TreeSet底层使用红黑树算法,会对存储的的元素使用自然排序(从小到大),前提是必须保证TreeSet集合中的元素是相同的数据类型,否则报错(不是同一类型无法比较).
TreeSet实现了NavigableSet和 SortedSet这两个特别的接口,NavigableSet接口又实现了SortedSet接口,SortedSet接口实现了Set接口,所以它们的关系是TreeSet→NavigableSet→SortedSet→Set这个亚子的,

TreeSet的排序规则

自然排序
TreeSet调用其中元素的compareTo方法来比较元素的大小关系,然后将集合按照升序排列(从小到大).所以在存入TreeSet中的元素必须实现 Comparable这个接口,并且重写它的compareTo(T o)方法.
如果存入的对象没有实现Comparable这个接口,存入数据的时候就会报无法转换为Comparable的异常
compareTo()方法就是比较方法,自己定义,案列如下

package com.wp;
import java.util.Set;
import java.util.TreeSet;
//实现这个Comparable接口
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name, int age) {
	super();
	this.name = name;
	this.age = age;
}
public Person() {
	super();
	// TODO Auto-generated constructor stub
}

@Override
public String toString() {
	return "Person [name=" + name + ", age=" + age + "]";
}
//自己编写比较规则,这里是按照年龄大小来排序
@Override
public int compareTo(Person other) {
	if(this.age>other.age) {
		return 1;
	}else if(this.age<other.age) {
		return -1;
	}
	return 0;
}
//测试
public static void main(String[] args) {
	Set<Person> set = new TreeSet<>();
	Person p = new Person("张三", 19);
	Person p1 = new Person("jack", 15);
	Person p2 = new Person("tom", 20);
	Person p3 = new Person("Sparrow", 20);
	System.out.println(set.add(p));
	System.out.println(set.add(p1));
	System.out.println(set.add(p2));
	System.out.println(set.add(p3));
	System.out.println(set);
}
}

最后打印结果为:

true
true
true
false
[Person [name=jack, age=15], Person [name=张三, age=19], Person [name=tom, age=20]]

由此可以得出TreeSet认为如果compareTo()方法返回一个0,它就将这个数据认为同一个对象,重复了,不保存,我们这里是只针对年龄进行的比较,相等的时候返回0,所以年龄有相等的数据的时候它没有保存,也就是保存失败,add()方法返回了一个false,返回正数就往后排(我们这里返回的正数为1),返回负数就往前排序(我们这里返回的负数是-1),注意:自然排序意为从小到大排序,还是不要搞反(从大到小)比较好

定制排序
在TreeSet构造器中传递一个实现了Comparator接口的对象,并重写public int compare()方法,这里的比较方法是按照名字长度排序
案列如下

package com.wp;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class Person {
private String name;
private int 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 Person(String name, int age) {
	super();
	this.name = name;
	this.age = age;
}
public Person() {
	super();
	// TODO Auto-generated constructor stub
}
@Override
public String toString() {
	return "Person [name=" + name + ", age=" + age + "]";
}
//测试
public static void main(String[] args) {
	Set<Person> set = new TreeSet<>(new NameLength());
	Person p = new Person("张三", 19);
	Person p1 = new Person("jack", 15);
	Person p2 = new Person("tom", 20);
	Person p3 = new Person("Spa", 20);
	System.out.println(set.add(p));
	System.out.println(set.add(p1));
	System.out.println(set.add(p2));
	System.out.println(set.add(p3));
	System.out.println(set);
}
}
//按名字长度自定义的比较器
class NameLength implements Comparator<Person>{
	@Override
	public int compare(Person o1, Person o2) {
		if(o1.getName().length()>o2.getName().length()) {
			return 1;
		}else if(o1.getName().length()<o2.getName().length()) {
			return -1;
		}
		return 0;
	}	
}

打印结果为:

true
true
true
false
[Person [name=张三, age=19], Person [name=tom, age=20], Person [name=jack, age=15]]
TreeSet排序总结

定制排序和自然排序二选一,或者两个都写但是只会执行其中一个(构造器中的优先级比较高),不然无法保存.
两种排序返回0就认为是相同的对象,不保存

Set接口总结

相同点:

  • 都不允许元素重复
  • 都不是线程安全的(解决方案:Collections中的synchronizedSet(Set s))方法
    HashSet:
  • 不保证元素的先后添加循序
  • 底层采用hash表算法,查询效率极高,Hash表就是一个优化的数组,所以查询效率比数组还高
  • 判断两个对象是否相等
    (1) equals比较为true
    (2) hashCode值相同
    LinkedHashSet:
  • HashSet的子类,底层采用的是哈希表算法,但是也采用了链表算法来维持元素添加的先后循序,判断两个元素是否相等的规则与HashSet相同
  • 应为需要多使用一个链表记录元素的循序,所以性能相当于HashSet较低
    一般少用,除非要求集合纪要保证记录添加循序以及不重复才会使用
    TreeSet:
  • 不保证元素的先后添加循序,但是会对集合中的元素做排序操作,
  • 底层使用红黑树算法(树结构,比较擅长做范围查询)
    -TreeSet要么有自然排序,要么有定制排序
    自然排序:要求TreeSet保存的对象必须实现compareable接口,并且重写compareTo()方法
    定制排序:要求往TreeSet构造器中传递一个实现了compareor接口的对象,并且要求实现它的compare()方法
    判断元素对象是否重复的规则:
    compareTo()/compare()方法返回值为0,即为重复对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值