五、集合——3-Set集合
3-Set集合
Set集合与Collection集合基本相同,没有提供额外的方法。
Set集合中不许包含相同的元素,如果试图把两个相同的元素添加到同一个Set中,添加操作会失败add()方法将返回false,新元素也不会被添加。(适用于HashSet、LinkedHashSet、TreeSet)
1.HashSet
(1)HashSet按照哈希算法存储集合中的元素,具有很好的存取和查找性能;
(2)HashSet的特征:
1)不保证元素的排列顺序,可能和添加顺序不同,顺序也可能发生改变;
2)HashSet不是同步的,当多个线程访问同一个HashSet时,必须通过代码保证其同步;
3)集合元素值可以为null;
(3)HashSet判断两个元素是否相同的标准:
①元素通过equals()方法返回true;
②元素的hashCode()方法的返回值相同;
(4)当把对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,那么也应该重写其hashCode()方法,其规则是:当两个对象通过equals()方法比较返回true时,两个对象的hashCode()方法的返回值也相同;
(5)当程序把可变对象添加到HashSet集合中时,尽量不要去修改该集合元素中参与计算hashCode()、equals()的实例变量,否则将会导致HashSet无法正确操作集合元素。
2.LinkedHashSet
(1)LinkedHashSet是HashSet的子类;
(2)LinkedHashSet同样根据hashcode值来确定元素的存储位置,同时也是用链表来维护元素的次序;
(3)由于LinkedHashSet是用了链表来维护元素次序,所以元素的顺序与元素添加时的顺序是一致的,但是LinkedHashSet同样不能够存放相同的元素。
3.TreeSet
(1)TreeSet是SortedSet接口的实现类,TreeSet可以确保元素处于有序的状态(这里的有序状态是指按照一定的规则对元素进行排序);
(2)TreeSet的通用方法:
import java.util.TreeSet; //TreeSet的通用方法 public class TreeSetTest { public static void main(String[] args) { TreeSet nums = new TreeSet(); //看似是放入了基本类型的元素,但实际上这里有一个自动装箱的过程 //在集合中实际的元素是Integer nums.add(1); nums.add(5); nums.add(3); nums.add(2); nums.add(6); //输出元素 System.out.println(nums); //已完成排序 //输出集合第一个元素 System.out.println(nums.first()); //输出集合的最后一个元素 System.out.println(nums.last()); //返回小于4的子集 System.out.println(nums.headSet(4)); //返回大于5的子集 System.out.println(nums.tailSet(5)); //返回大于等于3小于5的子集 System.out.println(nums.subSet(3, 5)); } }
(3)自然排序
1)自然排序,就是TreeSet调用集合元素的compareTo(Object obj)方法来比较元素之间的大小,然后将元素按照升序排列;
2)Comparable接口:
该接口中定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的类对象就可以比较大小,obj1.compareTo(obj2):
当返回值为0,obj1和obj2相等
当返回值为正整数,obj1>obj2
当返回值为负整数,obj1<obj2
3)已经实现了Comparable接口的常用类:
①基本数据类型的包装类;
②String;
③Date、Time;
4)向TreeSet中添加的元素必须实现Comparable接口(当只有一个元素时,可以不用,但这没有任何意义);
5)TreeSet若要正常工作需要存放同一个类型的元素;
6)TreeSet判断两个元素是否相等:compareTo()方法的返回值是否为0,为0则相同;
7)当需要把一个对象放入TreeSet中,重写该对象对应类的equals()方法时,应该保证此方法与compareTo()方法有一致的结果;
8)尽量不要修改放入TreeSet集合中的可变对象元素;
9)自然排序的使用:
Student类:
//需要放入TreeSet中,所以应该实现Comparable接口 public class Student implements Comparable{ private String name; private int age; public Student(){ } public Student(String name,int age){ this.name = name; this.age = 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; } //实现Comparable接口必须实现compareTo()方法 @Override public int compareTo(Object o) { // TODO Auto-generated method stub Student stu = (Student) o; //return 0 this=stu //return>0 this>stu //return<0 this<stu return this.age - stu.age; } //重写toString方法 @Override public String toString() { // TODO Auto-generated method stub return "["+this.name+"]"; } }
CompareToTest类:
import java.util.TreeSet; public class CompareToTest { public static void main(String[] args) { TreeSet set = new TreeSet(); Student a = new Student("A",18); Student b = new Student("B",13); Student c = new Student("C",15); Student d = new Student("D",11); //添加元素 set.add(a); set.add(b); set.add(c); set.add(d); System.out.println(set); } }
(4)定制排序
1)自定义的排序方式,如降序排序等,通过接口Comparator接口实现;
2)在Comparator接口中包含一个compare(T o1,T o2)方法,它的返回值是一个int:
当返回值为0时,o1=o2;
当返回值大于0时,o1>o2;
当返回值小于0时,o1<o2;
3)使用定制排序仍然需要保证TreeSet中为同一类型的元素;
4)定制排序的使用:
import java.util.Comparator; import java.util.TreeSet; public class ComparatorTest { public static void main(String[] args) { //使用定制排序 //需要使用Comparator接口 //并与TreeSet建立关联 TreeSet set = new TreeSet(new Comparator(){ @Override public int compare(Object o1, Object o2) { // TODO Auto-generated method stub Student stu1 = (Student)o1; Student stu2 = (Student)o2; return stu1.getAge()>stu2.getAge()?-1:stu1.getAge()<stu2.getAge()?1:0; }}); } }
4.EnumSet
(1)EnumSet是一个专门为枚举类设计的集合类,在EnumSet中所有元素必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式的指定;
(2)EnumSet集合也是有序的,以枚举值在Enum类内部的定义顺序来决定集合元素的顺序;
(3)EnumSet不允许添加null元素,但是可以判断是否含有null,也可以视图删除null;
(4)使用EnumSet保存枚举类的多个枚举值:
import java.util.EnumSet; enum Season{ SPRING,SUMMER,FALL,WINTER } public class EnumSetTest1 { public static void main(String[] args) { //创建一个EnumSet,集合元素是Enum类Season的全部枚举值 EnumSet es1 = EnumSet.allOf(Season.class); System.out.println(es1); //创建一个EnumSet空集合,指定集合元素为Season类的枚举值 EnumSet es2 = EnumSet.noneOf(Season.class); System.out.println(es2); //为es2添加元素 //集合中的顺序为枚举值的顺序 es2.add(Season.FALL); es2.add(Season.SUMMER); System.out.println(es2); //以指定枚举值创建EnumSet集合 EnumSet es3 = EnumSet.of(Season.FALL,Season.SUMMER); System.out.println(es3); } }
(5)EnumSet的其他用法:
import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; enum Season2{ SPRING,SUMMER,FALL,WINTER } public class EnumSetTest2 { public static void main(String[] args) { Collection set = new HashSet(); set.clear(); set.add(Season2.FALL); set.add(Season2.SPRING); set.add(Season2.WINTER); System.out.println(set); //复制Collection中的所有元素来创建EnumSet集合 EnumSet es = EnumSet.copyOf(set); System.out.println(es); } }
5.Set各实现类的性能分析
(1)HashSet的性能比TreeSet的性能要好,因为TreeSet使用红黑树算法维持元素的顺序,在需要保持排序方式的Set时才选用TreeSet;
(2)遍历LinkedHashSet的性能要优于遍历HashSet的性能;
(3)EnumSet是所有Set中性能最好的,但它只能保存同一个枚举类的枚举值作为元素;
(4)所有的Set集合都是非线程安全的,当需要有多个线程同时访问同一个Set时,需要手动实现Set集合的线程同步,可以使用工具类Collections的synchronizedSet方法来包装该Set集合,例如:
SrotedSet s = Collections.synchronizedSet(new TreeSet());