Java集合框架(二)---Set

上一篇学习了List集合的存储方式,下面继续学习Set集合

Set : 元素是无序的(存入和取出的顺序不一定一致),元素是不可以重复的

Set集合的功能和Collection是一致的,接下来看一下常见的子类

       |----HashSet:底层的结构是hash表.线程是非同步的
         **HashSet是如何保证元素唯一性的呢?
          是通过 *元素* 的两个方法hashCode和equals来完成。如果元素的HashCode值相等,才会判断equals是否为true,如何元素的Hash值不等,不会调用equals**。
          
          **注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的HashCode和equals方法**
          
       |----TreeSet:可以对set集合中的元素进行排序,底层数据结构是二叉树,保证元素唯一性的依据:compareTo方法return 0。
       TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Comprable接口,覆盖compareTo方法,这种方式也成为元素的自然顺序,或者叫做默认顺序。
       TreeSet的第二种排序方式,当元素自身不具备比较性时,或者具备的比较性不是所需要的,这是就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。

演示代码HashSet

public class Demo {
	public static void sop(Object object){
		System.out.println(object);
	}
	public static void main(String[] args) {
		
		HashSet hashSet = new HashSet();
		hashSet.add("java1");
		hashSet.add("java1");
		hashSet.add("java2");
		hashSet.add("java3");
		hashSet.add("java4");
		//也用迭代器取出
		Iterator iterator = hashSet.iterator();
		while (iterator.hasNext()) {
			sop(iterator.next());
		}
	}

元素是无序的取出且不重复:

java1
java3
java2
java4

练习
往hashSet集合中存入自定义对象,姓名和年龄相同视为同一个人,重复元素。

class Person{
	int age;
	String name;
	public Person(int age,String name) {
		this.age = age;
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public String getName() {
		return name;
	}
	//需要重写hash code,因为hash表会先判断每个对象的hash值是否相等
	//我们需要让他们的hash值都相等的情况下,再去执行equal方法,进行内容的判断
	@Override
	public int hashCode() {
		return 60;
	}
	@Override
	public boolean equals(Object obj) {
		if (!(obj instanceof Person)) {
			return false;
		}
		Person person = (Person)obj;
		return this.name.equals(person.getName()) && this.age == person.getAge();
	}
}

public class Demo {
	public static void sop(Object object){
		System.out.println(object);
	}
	public static void main(String[] args) {
		
		HashSet hashSet = new HashSet();
		//person对象中的每一个元素,都会有自己的hash值
		hashSet.add(new Person(11, "a1"));
		hashSet.add(new Person(11, "a2"));
		hashSet.add(new Person(11, "a3"));
		hashSet.add(new Person(11, "a2"));
		
		Iterator iterator = hashSet.iterator();
		while (iterator.hasNext()) {
			Person person = (Person)iterator.next();
			sop(person.getAge() + ".."+person.getName());
		}
	}

原理图:60 == 3c
在这里插入图片描述
运行结果:

11..a3
11..a2
11..a1

思考一下,如果不加HashCode和equals方法会打印什么呢?
结果如下:

11..a2
11..a1
11..a2
11..a3

因为它比较的是每个对象的Hash值,每个对象的Hash值是不一样的,所以会存入相同的元素。

真的上面的HashCode进行优化,可以减少判断的次数,没有必要写死,可以通过元素自身来定义Hash值,*39的目的是为了避免元素的Hash值相等,提高Hash值的唯一性,效率高。

@Override
	@Override
	public int hashCode() {
		//每个字符串都有自己的hash值,使用这种方式来判断hash值是否相等
		return name.hashCode()+age*39;
	}

演示代码TreeSet

public static void main(String[] args) {
		
		TreeSet treeSet = new TreeSet();
		treeSet.add("cds");
		treeSet.add("DD");
		treeSet.add("vvff");
		treeSet.add("aaas");
		
		Iterator iterator = treeSet.iterator();
		while (iterator.hasNext()) {
			sop(iterator.next());
		}
	}

打印结果:可以看到它对元素进行了字母的排序

DD
aaas
cds
vvff

练习1,第一种排序方式
需求:往TreeSet集合中存储自定义对象人,想按照学生的年龄进行排序。

class Person{
	int age;
	String name;
	public Person(int age,String name) {
		this.age = age;
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public String getName() {
		return name;
	}
}

public class Demo {
	public static void sop(Object object){
		System.out.println(object);
	}
	public static void main(String[] args) {
		TreeSet treeSet = new TreeSet();
	
		treeSet.add(new Person(11, "lisi"));
		treeSet.add(new Person(21, "wang"));
		treeSet.add(new Person(19, "zhangsan"));
		treeSet.add(new Person(22, "xiaowang"));
		
		while (iterator.hasNext()) {
			Person person = (Person)iterator.next();
			sop(person.getAge()+"..."+person.name);
		}
	}
}

编译运行出错:48行 对应treeSet.add(new Person(11, “lisi”));

Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to java.lang.Comparable
	at java.util.TreeMap.compare(TreeMap.java:1188)
	at java.util.TreeMap.put(TreeMap.java:531)
	at java.util.TreeSet.add(TreeSet.java:255)
	at Demo.main(Demo.java:48)

Comparable是一个接口,此接口强行实现它的每个对象进行整体排序
在这里插入图片描述
我们再往Treeset存对象的时候,它会帮忙排序,但是Person不具备比较性,所以我们需要让Person具备比较性,需要实现Comparable接口

class Person implements Comparable{
	int age;
	String name;
	public Person(int age,String name) {
		this.age = age;
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public String getName() {
		return name;
	}
	@Override
	public int compareTo(Object o) {
		if(!(o instanceof Person)){
			throw new RuntimeException("不是Person对象");
		}
		Person person = (Person)o;
		if (this.age > person.getAge()) {
			return 1;
		}else if (this.age == person.getAge()) {
			return 0;
		}
		return -1;
	}
}

public class Demo {
	public static void sop(Object object){
		System.out.println(object);
	}
	public static void main(String[] args) {
		
		TreeSet treeSet = new TreeSet();
		
		treeSet.add(new Person(11, "lisi"));
		treeSet.add(new Person(21, "wang"));
		treeSet.add(new Person(19, "zhangsan"));
		treeSet.add(new Person(22, "xiaowang"));
	
		
		Iterator iterator = treeSet.iterator();
		while (iterator.hasNext()) {
			Person person = (Person)iterator.next();
			sop(person.getAge()+"..."+person.name);
		}
	}
}

打印结果:

11...lisi
19...zhangsan
21...wang
22...xiaowang

如果元素有两个相同的年龄,还要比较一下name是否相同,优化compareTo方法如下:

@Override
	public int compareTo(Object o) {
		if(!(o instanceof Person)){
			throw new RuntimeException("不是Person对象");
		}
		Person person = (Person)o;
		if (this.age > person.getAge()) {
			return 1;
		}else if (this.age == person.getAge()) {
			//String字符默认有compareTo的排序方法,按照自然排序
			return this.name.compareTo(person.name);
		}
		return -1;
	}

存入相同年龄不同名字。

TreeSet treeSet = new TreeSet();
		
		treeSet.add(new Person(11, "lisi"));
		treeSet.add(new Person(21, "wang"));
		treeSet.add(new Person(21, "wang02"));
		treeSet.add(new Person(19, "zhangsan"));
		treeSet.add(new Person(22, "xiaowang"));

打印结果如下:

11...lisi
19...zhangsan
21...wang
21...wang02
22...xiaowang

练习2,第二种排序方式
当元素自身不具备比较性,或者具备的比较性不是所需要的,这是需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

class Person implements Comparable{
	int age;
	String name;
	public Person(int age,String name) {
		this.age = age;
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public String getName() {
		return name;
	}
	@Override
	public int compareTo(Object o) {
		if(!(o instanceof Person)){
			throw new RuntimeException("不是Person对象");
		}
		Person person = (Person)o;
		if (this.age > person.getAge()) {
			return 1;
		}else if (this.age == person.getAge()) {
			//String字符默认有compareTo的排序方法,按照自然排序
			return this.name.compareTo(person.name);
		}
		return -1;
	}
}

public class Demo {
	public static void sop(Object object){
		System.out.println(object);
	}
	public static void main(String[] args) {
		//传入自己的比较器,比Person的比较器优先级高
		TreeSet treeSet = new TreeSet(new MyCompare());
		
		treeSet.add(new Person(11, "lisi"));
		treeSet.add(new Person(21, "wang"));
		treeSet.add(new Person(21, "wang3"));
		treeSet.add(new Person(19, "zhangsan"));
		treeSet.add(new Person(22, "xiaowang"));
	
		
		Iterator iterator = treeSet.iterator();
		while (iterator.hasNext()) {
			Person person = (Person)iterator.next();
			sop(person.getAge()+"..."+person.name);
		}
	}
}
class MyCompare implements Comparator{

	@Override
	public int compare(Object o1, Object o2) {
		Person person = (Person)o1;
		Person person2 = (Person)o2;
		
		int num = person.getName().compareTo(person2.getName());
		if (num == 0) {
			//如果name是相同的,就比较年龄
			return new Integer(person.getAge()).compareTo(person2.getAge());
		}
		return num;
	}
	
}

打印效果,按照name排序:

11...lisi
21...wang
21...wang3
22...xiaowang
19...zhangsan

练习3,第二种排序方式
需求:按照字符串长度进行排序。
字符串本身具备比较性,但是它的比较方式不是所需的,这时就只能使用比较器。

public class Demo {
	public static void sop(Object object){
		System.out.println(object);
	}
	public static void main(String[] args) {
		TreeSet treeSet = new TreeSet(new MyCompare());
		treeSet.add("dd");
		treeSet.add("uu");
		treeSet.add("asf");
		treeSet.add("eeee");
		treeSet.add("cvfss");
		treeSet.add("bfxxt");
	
		Iterator iterator = treeSet.iterator();
		while (iterator.hasNext()) {
			sop(iterator.next());
		}
	}
}
class MyCompare implements Comparator{
	@Override
	public int compare(Object o1, Object o2) {
		String s1 = (String)o1;
		String s2 = (String)o2;
		int num = new Integer(s1.length()).compareTo(new Integer(s2.length()));
		if (num == 0)  {
			return s1.compareTo(s2);
		}
		return num;
	}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值