【Java的集合框架之set 28】
一、Set集合
元素不可以重复,并且是无序的(也就是说不保证排序)
Set集合的方法和Collection方法一样,拿来直接用就行
二、常用的两个子类对象
|-- HashSet 内部数据结构是哈希表,是不同步的 -->实现排序是通过hashCode 和 equals进行比较
|-- TreeSet 按照元素字典顺序排序的,是不同步的
-->实现排序的方法一:让元素自身具备比较功能,也就是需要实现comparable接口,覆盖compareTo方法
判断元素唯一性的方法:就是根据比较方法的返回结果是否是0 ,是0 就不存
-->实现排序的方法二:如果对象中不具备自然排序或者是对象类不能进行修改,这时候就需要让集合本身具备排序功能(定义一个类实现comparator接口,覆盖compare方法,将该类对象作为 参数传递给TheeSet集合的构造函数)
三、详细介绍HashSet
--------举例演示HashSet----------
package com.JavaStudy01.Set; import java.util.HashSet; import java.util.Iterator; /** * @Author wufq * @Date 2020/7/31 14:39 */ public class HashSetDemo01 { public static void main(String[] args){ HashSet hs = new HashSet(); hs.add("haha"); hs.add("xixi"); hs.add("hehe"); hs.add("hehe"); hs.add("hehe"); hs.add("hehe"); hs.add("heihei"); Iterator it=hs.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } } =====执行结果==== haha heihei xixi hehe
通过执行结果证明:Set集合确实是存放不重复的对象,并且还是无序的
1、Set集合的HashCode值
Set集合里面放的对象其实是通过hashCode算出来的值
哈希表:哈希是一种算法,算出来的值存储起来 就形成了哈希表
2、HashSet存储自定义对象(要求:往HashSet集合里面存放person对象,如果姓名和年龄相同,视为同一个人。视为相同元素)
HashSet集合数据结构是哈希表,所以存储元素的时候使用元素的hashCode方法来确定位置,如果位置相同的话在用元素的equals方法来确定是否相同
举例说明:
package com.JavaStudy01.Set; import com.JavaStudy01.Bean.Person; import java.util.HashSet; import java.util.Iterator; /** * @Author wufq * @Date 2020/7/31 16:32 * HashSet存储自定义对象 */ public class HashSetDemo02 { public static void main(String[] args){ HashSet hs = new HashSet(); hs.add(new Person("lisi1",21)); hs.add(new Person("lisi2",22)); hs.add(new Person("lisi9",23)); hs.add(new Person("lisi9",23)); hs.add(new Person("lisi8",25)); Iterator it=hs.iterator(); while (it.hasNext()){ Person p = (Person)it.next(); System.out.println(p.getName()+"::"+p.getAge() ); } } } =====执行结果===== lisi1::21 lisi8::25 lisi9::23 lisi9::23 lisi2::22
通过执行结果发现,相同的姓名和年龄不是同一个元素,并且被打印出来了(不符合我们的要求)
分析原因:
因为person对象也是继承了Object对象,而Object类里面同样包含了hashcode方法,所以只要new了一个对象,就会有一个hashcode值,所以即使有两个hs.add(new Person("lisi9",23));
那么也会有两个不同的hashCode值,因此会打印出来
如何解决:
Person类重写hashCode()方法和equals()方法
package com.JavaStudy01.Bean; /** * @Author wufq * @Date 2020/7/31 11:24 */ public class Person extends Object{ private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public int hashCode() { System.out.println(this+"........hachCode");//验证是否真的在调用hashCode方法 return name.hashCode()+age; } @Override public boolean equals(Object obj) { Person p= (Person)obj; System.out.println(this+"........equals"+obj);//验证是否真的在调用equals方法 //this表示谁去调用就是谁 return this.name.equals(p.name)&&this.age == p.age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } public String toString(){ return name+"::"+age; } }
这样在执行HashSetDemo02,结果就没有重复的内容了,并且可以看出hashSet确实是先计算hashcode的值,然后在计算equals
======重写equals,hashCode以后的执行结果=====
lisi1::21........hachCode
lisi2::22........hachCode
lisi9::23........hachCode
lisi9::23........hachCode
lisi9::23........equals lisi9::23
lisi8::25........hachCode
lisi1::21
lisi2::22
lisi8::25
lisi9::23
------>
可以看出lisi9::23 确实是先判断hashcode的值,然后在判断的是equals
-------->额外的知识点补充:<------------
如果是存储对象,那么重写equals方法如下:
public boolean equals(Object obj) { Person p= (Person)obj; if(this == obj){ return true; } if(!(obj instanceof Person)){ throw new ClassCastException("类型不对!"); } }
3、框架练习:
------->定义功能去除ArrayList中的重复元素
package com.JavaStudy01.Set; import java.util.ArrayList; import java.util.Iterator; /** * @Author wufq * @Date 2020/8/4 17:20 * 定义功能去除ArrayList里面的重复元素--->去除重复的字符串 */ public class ArrayListDemo03 { public static void main(String[] args){ ArrayList al = new ArrayList(); al.add("abc1"); al.add("abc2"); al.add("abc3"); al.add("abc3"); System.out.println("排重之前:"+al); al= getGetsingleElement(al); System.out.println("排重之后:"+al); } public static ArrayList getGetsingleElement(ArrayList al) { //1、定义一个临时容器 ArrayList temp = new ArrayList(); //2、迭代al集合 Iterator it = al.iterator(); while (it.hasNext()){ Object obj=it.next(); //3、判断比迭代到的元素是否在临时容器内 if(!temp.contains(obj)){ temp.add(obj); } } return temp; } } ======执行结果====== 排重之前:[abc1, abc2, abc3, abc3] 排重之后:[abc1, abc2, abc3]
-------->定义功能去除ArrayList中的重复自定义对象(这里只需要重写person类内的equals方法以及toString()方法)
package com.JavaStudy01.Set; import com.JavaStudy01.Bean.Person; import java.util.ArrayList; import java.util.Iterator; /** * @Author wufq * @Date 2020/8/4 18:17 * ArrayList去除重复的自定义对象 */ public class ArrayListDemo02 { public static void main(String[] args){ ArrayList ali = new ArrayList(); ali.add(new Person("lisi1",23)); ali.add(new Person("lisi2",22)); ali.add(new Person("lisi3",21)); ali.add(new Person("lisi4",20)); ali.add(new Person("lisi1",23)); System.out.println(ali); ali = StingElement(ali); System.out.println(ali); } private static ArrayList StingElement(ArrayList ali) { ArrayList temp = new ArrayList(); Iterator it = ali.iterator(); while (it.hasNext()){ Object obj = it.next(); if(!temp.contains(obj)){ temp.add(obj); } } return temp; } }
=======执行结果======
[lisi1::23, lisi2::22, lisi3::21, lisi4::20, lisi1::23]
[lisi1::23, lisi2::22, lisi3::21, lisi4::20]
注意:hashSet集合无论是包含或者删除一个元素,就需要判断该元素是否和容器内的元素是否相同,例如:对于ArrayList这样的集合,contains、remove需要重写hashCode和equals方法。hashSet集合只需要重写equals方法
4、HashSet是无序并且不保证重复,为了改进引进了子类LinkedHashSet(---->有序并且是唯一的)
package com.JavaStudy01.Set; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; /** * @Author wufq * @Date 2020/8/6 09:28 */ public class LinkedHashSet01 { public static void main(String[] args){ HashSet hs = new LinkedHashSet(); hs.add("haha"); hs.add("xixi"); hs.add("hehe"); hs.add("hehe"); hs.add("hehe"); hs.add("hehe"); hs.add("heihei"); Iterator it=hs.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } } ====执行结果==== haha xixi hehe heihei
四、详细介绍TreeSet
1、比较基本数据类型
package com.JavaStudy01.Set; import java.util.Iterator; import java.util.TreeSet; /** * @Author wufq * @Date 2020/8/6 09:53 */ public class TreeSetDemo01 { public static void main(String[] args){ TreeSet ts = new TreeSet(); ts.add("aa"); ts.add("abc"); ts.add("zz"); ts.add("hh"); Iterator it = ts.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } } ====执行结果==== aa abc hh zz
通过执行结果发现:基本数据类型的元素按照元素字典进行排序
2、比较自定义类型(第一种比较方式:通过元素自身的比较方式)
------->TreeSetDemo02类:<---------
package com.JavaStudy01.Set; import com.JavaStudy01.Bean.Person; import java.util.Iterator; import java.util.TreeSet; /** * @Author wufq * @Date 2020/8/6 10:06 */ public class TreeSetDemo02 { public static void main(String[] args){ TreeSet ts = new TreeSet(); ts.add(new Person("zhangsan",23)); ts.add(new Person("lisi",24)); ts.add(new Person("wangwu",25)); ts.add(new Person("zhaoliu",26)); Iterator it = ts.iterator(); while (it.hasNext()){ Person p = (Person)it.next(); System.out.println(p.getName()+"::"+p.getAge()); //报com.JavaStudy01.Bean.Person cannot be cast to java.lang.Comparable异常 } } /* Comparable:此接口强行对实现他的每一个类的对象进行整体排序,这种排序被称为类的自然排序,类的compareTo方法被称为自然比较方法 如何才能解决可以比较的对象: */ }
=====执行结果=====
报com.JavaStudy01.Bean.Person cannot be cast to java.lang.Comparable异常
通过查Comparable异常发现:此接口强行对实现他的每一个类的对象进行整体排序,这种排序被称为类的自然排序,类的compareTo方法被称为自然比较方法
如何才能解决可以比较的对象:person类继承Comparable接口,并重写compareTo方法
int compareTo():比较此对象与指定对象的大小,如果该对象小于,大于,等于指定对象,则分别返回负整数,正整数,零
--------->Person类继承Comparable接口<---------
package com.JavaStudy01.Bean; /** * @Author wufq * @Date 2020/7/31 11:24 */ public class Person implements Comparable{ private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public int hashCode() { // System.out.println(this+"........hachCode");//验证是否真的在调用hashCode方法 return name.hashCode()+age; } @Override public boolean equals(Object obj) { Person p= (Person)obj; // System.out.println(this+"........equals"+obj);//验证是否真的在调用equals方法 //this表示谁去调用就是谁 return this.name.equals(p.name)&&this.age == p.age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } public String toString(){ return name+"::"+age; } @Override public int compareTo(Object o) { Person p = (Person)o; //通过年龄的大小进行判断,如果年龄相同在通过名字进行判断 if(this.age < p.age){ return -1; }else if(this.age > p.age){ return 1; }else if(this.age == p.age){ return this.name.compareTo(p.name); } //另外一种判断写法 int temp = this.age - p.age; return temp==0?this.name.compareTo(p.name):temp;//temp=0吗,等于就对name进行比较,不等于就返回temp } }
重新执行Person的结果:
zhangsan::23
lisi::24
wangwu::25
huqi::26
zhaoliu::26
------->通过结果可以看出已经完成了排序
3、比较自定义类型(第二种比较方式:通过集合本身具备比较功能)
------->定义一个ComparatorByName类实现Comparator接口<---------
package com.JavaStudy01.Set; import com.JavaStudy01.Bean.Person; import java.util.Comparator; /** * @Author wufq * @Date 2020/8/6 14:37 */ public class ComparatorByName implements Comparator{ @Override public int compare(Object o1, Object o2) { Person p1 = (Person)o1; Person p2 = (Person)o2; int temp = p1.getName().compareTo(p2.getName()); return temp==0?p1.getAge()-p2.getAge():temp; } }
------->TreeSetDemo02类<-------
package com.JavaStudy01.Set; import com.JavaStudy01.Bean.Person; import java.util.Iterator; import java.util.TreeSet; /** * @Author wufq * @Date 2020/8/6 10:06 */ public class TreeSetDemo02 { public static void main(String[] args){ TreeSet ts = new TreeSet(new ComparatorByName()); ts.add(new Person("zhangsan",23)); ts.add(new Person("lisi",24)); ts.add(new Person("wangwu",25)); ts.add(new Person("zhaoliu",26)); ts.add(new Person("huqi",26)); Iterator it = ts.iterator(); while (it.hasNext()){ Person p = (Person)it.next(); System.out.println(p.getName()+"::"+p.getAge()); //报com.JavaStudy01.Bean.Person cannot be cast to java.lang.Comparable异常 } } } ======执行结果====== huqi::26 lisi::24 wangwu::25 zhangsan::23 zhaoliu::26
4、TreeSet底层结构,来判断如何确定元素的比较
5、字符串长度排序
------->定义一个ComparatorByString类实现Comparator接口<---------
package com.JavaStudy01.Set; import java.util.Comparator; /** * @Author wufq * @Date 2020/8/6 15:27 */ public class ComparatorByString implements Comparator{ @Override public int compare(Object o1, Object o2) { String s1 = (String)o1; String s2 = (String)o2; int temp = s1.length() - s2.length(); return temp==0?s1.compareTo(s2):temp; //这里的compareTo是String类自身的比较方法,而非集合内的比较方法 } }
------->TreeSetDemo03排序类<---------
package com.JavaStudy01.Set; import java.util.Iterator; import java.util.TreeSet; /** * @Author wufq * @Date 2020/8/6 15:27 */ public class TreeSetDemo03 { public static void main(String[] args){ TreeSet ts = new TreeSet(new ComparatorByString()); ts.add("aa"); ts.add("abcd"); ts.add("zzzzzz"); ts.add("hhl"); ts.add("kkk"); Iterator it = ts.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } } ====执行结果===== aa hhl kkk abcd zzzzzz