Set集合简介
Set集合中的对象不会按插入顺序的方式排序,而是按照默认(如哈希地址和自然排序)或者指定的方式排序,Set集合中不能包含重复对象。Set集合由Set接口和Set接口的实现类组成,Set接口继承了Collection接口,因此包含Collection接口的所有方法。Set接口常用的实现类有“TreeSet类(树集合)”和“HashSet类(散列集合)”。
TreeSet类和HashSet类的对比
TreeSet类除了实现了Set接口,还是实现了java.util.SortSet接口,因此TreeSet类实现的Set集合在遍历集合的时候按照自然顺序递增排序,也可以强制实现Comparable接口,重写其中的toString方法按照自己的意愿排序。此类不允许添加null。
HashSet类实现了Set接口,它不保证Set的迭代顺序(实际上是更加哈希值排序的),特别是它不保证该顺序恒久不变(原因是某些操作会改变元素的哈希值),此类允许添加null元素。
TreeSet类的使用

package MyPackage_2;import java.util.Iterator;import java.util.Set;import java.util.TreeSet;public class TestClass {public static void main(String[] args) {Set s1=new TreeSet();Person p1=new Person(1,17,"b-阿离");Person p2=new Person(2,16,"a-瑶妹");Person p3=new Person(3,25,"d-马可波罗");Person p4=new Person(4,78,"c-老夫子");s1.add(p1);s1.add(p2);s1.add(p3);s1.add(p4);s1.add(p4);//重复的对象不会被添加进去// s1.add(null);是不被允许的,会抛出空指针异常System.out.println("元素个数:"+s1.size());Iterator it= s1.iterator();while(it.hasNext()){//遍历s1中的实例对象,如果实现了Comparable类,会按照重写的compareTo方法进行排序,否则按自然顺序递增排序System.out.println(it.next());}}}
package MyPackage_2;public class Person implements Comparable{int id;int age;String name;public Person(int id,int age,String name){this.id=id;this.age=age;this.name=name;}@Overridepublic String toString() {//重写toString方法,把对象的哈希地址转化成对象的属性便于观察return ("id="+id+" age="+age+" name="+name);}@Overridepublic int compareTo(Object o) {//重写Comparable接口的方法Person p=(Person)o; //向下转型为Person类实例对象/*int diff=this.age-p.age;//按年龄排序if(diff!=0){diff=diff/Math.abs(diff);}return diff;*/int x=this.name.compareTo(p.name);//按照字典顺序排序return x;}}
运行结果:
下面重点讲一下什么什么时候需要重写toString方法:
toString方法存在于所有的类中,在Object类里面定义toString()方法的时候返回的对象的hashcode码,这个hashcode码不能简单明了的表示出对象的属性,所以要重写toString()方法。当需要将一个对象输出到显示器时,通常要调用他的toString()方法,将对象的内容转换为字符串。java中的所有类默认都有一个toString()方法,默认情况下 System.out.println(对象名)或者System.out.println(对象名.toString())输出的是此对象的类名和此对象对应内存的首地址如果想自定义输出信息必须重写toString()方法。
下面看一下不重写toString方法的运行结果:
HashSet类的使用
在使用HashSet来存对象的时候,一般要指定由对象的类的哪一个(或多个)属性来决定哈希地址(类似于MySQL中创建表的时候指定主键一样),指定的属性如果相同,那么哈希地址就相同,相同的哈希地址的对象是不会被重复放进HashSet集合里面的。如果被指定的属性在程序中被重新赋值,那么它的哈希地址也会被改变。
实例代码:
package MyPackage_2;import java.util.HashSet;import java.util.Iterator;import java.util.Set;public class TestClass {public static void main(String[] args) {Set s1=new HashSet();Person p1=new Person(1,17,"b-阿离");Person p2=new Person(1,16,"a-瑶妹");Person p3=new Person(3,25,"d-马可波罗");Person p4=new Person(4,78,"c-老夫子");s1.add(p1);s1.add(p2);s1.add(p3);s1.add(p4);s1.add(null);//HashSet类允许添加null元素System.out.println("元素个数:"+s1.size());Iterator it= s1.iterator();while(it.hasNext()){System.out.println(it.next());}}}
package MyPackage_2;
import java.util.Objects;
public class Person {
int id;
int age;
String name;
public Person(int id,int age,String name){
this.id=id;
this.age=age;
this.name=name;
}
@Override
public String toString() {
return ("id="+id+" age="+age+" name="+name);
}
/*以下方法用于确定那个属性决定哈希地址*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id;
}
@Override
public int hashCode() {
return Objects.hash(id);//使用id来决定哈希地址
}
}

(选择那个属性来决定哈希地址,添加hashcode方法的时候选择的)
运行结果:
下面看一下改变“阿离”的id:

发现阿离并没有被删除,原因是阿离的哈希地址被改变了,删除的是id=5的哈希地址,而原来阿离的id=1的哈希地址并没有被删除,所有阿离没有被删除,所有这里的删除无效了(同理add方法也就无效了)。这说明决定哈希地址的属性尽量不要随意更改(类似主键也不要随意更改),否则remove和add方法就会变得比较复杂了。
