Set集合类似于一个罐子,我们可以依次把多个对象丢入Set集合,但是Set集合通常不记住元素的添加顺序。
Set集合与Colletcion基本相同,只不过Set集合不允许出现相同的元素,如果使用add方法添加是出现相同元素,则会返回false值并且该相同元素不会被加入。
Set集合就只有这些东西,我们主要讲的还是Set集合的三个实现类:HashSet、LinkedHashSet、TreSet。
HashSet类作为Set集合的一个实现类,主要是以Hash算法来存储集合中的元素,因此具有很好的存储以及查询性能。
主要原理是,在向HashSet集合中添加一个元素时,它会调用该对象的hashCode( )方法来得到该对象的哈希值,然后通过该对象的哈希值判断在HashSet集合中的存储位置。在后续添加中,如果两个元素如果equals( )方法比较成功,同时hashCode( )方法返回值也相等,则说明他们是相同内容的东西,因此无法添加成功。
但是如果equal( )方法返回值相等,hashCode( )返回值不相等,则该元素还是会被添加到集合中,这个会非常麻烦,在后面会讲到对于此类的解决方法。
public class HashSetDemo1 { public static void main(String[] args) { //正常情况 Set<String> set=new HashSet<String>(); set.add("Tempestissimo 11.50"); set.add("Tempestissimo 11.50"); set.add("Grievous Lady 11.30"); set.add("Feacture Ray 11.20"); set.add("Feacture Ray 11.20"); set.add("SAIKYO STRONGER 11.00"); set.add("Aegleseeker 11.00"); for(String a:set){ System.out.println(a); } } }
在判断时,若没有重写equals( )和hashCode( )方法,则会直接调用Object类中的这两个方法,而这样子做相当于没有比较,因此若我们要重写该对象对应类比较方法时,必须两个方法都要同时重写,即如果重写了equals( )方法,就必须重写hashCode( )方法,反之亦然
如果两个对象的equals( )方法返回的都是true,而hashCode( )方法返回值不相等,则会把这两个相同的对象存储在两个不同的位置。这样子与Set集合的规则 冲突;若如果hashCode( )方法返回的是true但是equals( )方法返回的值是false,则会导致性能下降。
其实很简单,把equals( )方法和hashCode方法( )都重写了就得了
要注意的一点是,在可变对象添加到了HashSet集合之后,不要再去修改集合中参与计算的hashCode( ),equals( )的实例变量,否则会导致HashSet( )无法正确 操作这一些元素。
public class HashSetDemo2 { public static void main(String[] args) { Set<SongInfo> set=new HashSet<SongInfo>(); SongInfo song1=new SongInfo("Tempestissimo",11.50f); SongInfo song2=new SongInfo("Tempestissimo",11.50f); SongInfo song3=new SongInfo("Grievous Lady",11.30f); SongInfo song4=new SongInfo("Feacture Ray",11.20f); SongInfo song5=new SongInfo("SAIKYO STRONGER",11.00f); SongInfo song6=new SongInfo("SAIKYO STRONGER",11.00f); SongInfo song7=new SongInfo("Aegleseeker",11.00f); set.add(song1); set.add(song2); set.add(song3); set.add(song4); set.add(song5); set.add(song6); set.add(song7); for(SongInfo a:set){ String songname=a.getSongname(); float rating=a.getRating(); System.out.println(songname+"-----"+rating); } } } //Songinfo类 public class SongInfo { private String songname; private float rating; public SongInfo() { } public SongInfo(String songname, float rating) { this.songname = songname; this.rating = rating; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SongInfo songInfo = (SongInfo) o; return Float.compare(songInfo.rating, rating) == 0 && songname.equals(songInfo.songname); } @Override public int hashCode() { return Objects.hash(songname, rating); } } //这里重写了equals方法和hashCode方法
和HashSet差不多,最主要的是底层数据结构有个链表,因此记录了插入顺序,同时使用Hash算法来维护这个链表
public class LinkedSetDemo { public static void main(String[] args) { Set<String> set=new LinkedHashSet<String>(); set.add("Hello"); set.add("World"); set.add("World"); set.add("Java"); for(String a:set){ System.out.println(a); } } } 输出结果 Hello World Java
底层数据结构为红黑树,储存的数据有有序性。其排序有两种方式:自然排序和定制排序
自然排序是指利用集合元素的 compareTo( )方法来比较元素之间的大小关系,然后将集合元素按升序排序
注意,实现compareTo方法必须先实现Comparable接口
public class TreeSetDemo1 { public static void main(String[] args) { Set<SongInfo> set=new TreeSet<SongInfo>(); SongInfo song1=new SongInfo("Tempestissimo",11.50f); SongInfo song2=new SongInfo("Tempestissimo",11.50f); SongInfo song3=new SongInfo("Grievous Lady",11.30f); SongInfo song4=new SongInfo("Feacture Ray",11.20f); SongInfo song5=new SongInfo("SAIKYO STRONGER",11.00f); SongInfo song6=new SongInfo("SAIKYO STRONGER",11.00f); SongInfo song7=new SongInfo("Aegleseeker",11.00f); set.add(song1); set.add(song2); set.add(song3); set.add(song4); set.add(song5); set.add(song6); set.add(song7); for(SongInfo a:set){ String songname=a.getSongname(); float rating=a.getRating(); System.out.println(songname+"-----"+rating); } } } //输出结果 Tempestissimo-----11.5 Grievous Lady-----11.3 Feacture Ray-----11.2 SAIKYO STRONGER-----11.0 Aegleseeker-----11.0
SongInfo类
public class SongInfo implements Comparable{ public String songname; public float rating; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SongInfo songInfo = (SongInfo) o; return Float.compare(songInfo.rating, rating) == 0 && songname.equals(songInfo.songname); } @Override public int hashCode() { return Objects.hash(songname, rating); } @Override //这里重写了compareTo方法 public int compareTo(Object o) { SongInfo a = (SongInfo) o; if (a.rating > this.rating) { return 1; } else if(a.rating==this.rating&&a.songname!=this.songname){ return 1; } else return 0; } }
如果想实现定制排序可以通过Comparator接口的帮助,提供一个Comparator对象与该TreeSet集合关联,由该对象来负责该集合元素的排序逻辑
例如下面的代码
public class TreeSetDemo { public static void main(String[] args) { TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { int num = s1.getName().length() - s2.getName().length(); int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; return num3; } }); Student s1 = new Student("linqingxia", 27); Student s2 = new Student("zhangguorong", 29); Student s3 = new Student("wanglihong", 23); Student s4 = new Student("linqingxia", 27); Student s5 = new Student("liushishi", 22); Student s6 = new Student("wuqilong", 40); Student s7 = new Student("fengqingy", 22); Student s8 = new Student("linqingxia", 29); ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); ts.add(s8); for (Student s : ts) { System.out.println(s.getName() + "---" + s.getAge()); } } } //输出结果