1.Set子接口的操作特点以及常用子类
2.深入分析两个常用子类的操作特征
3.分析TreeSet如何实现排序/排序情况下,判断重复元素
4.分析重复元素判断依据(Object类的HashCode,equals)/HashSet去重/非排序情况下,判断重复元素
在Collection接口下又有另外一个比较常用的子接口Set接口(20%).
Set接口并不像List接口那样对Collection接口进行大量的扩充。而是简单的继承了Collection接口。也就没有了之前List集合所提供的get方法
Set接口下有两个常用的子类:HashSet(基于HashMap,而HashMap基于数组和链表,无序排列),TreeSet(基于TreeMap 而TreeMap基于红黑树,Comparable接口实现的有序排序)
范例:观察HashSet子类的特点
public class TestDemo{
public static void main(String[] args) throws Exception{
Set<String> all=new HashSet<String>();
all.add("NIHAO");
all.add("Hello");
all.add("Hello");//重复数据
all.add("World");
System.out.println(all);
}
}
结果
image.png
发现数据不重复而且无序
通过演示可以发现,Set集合下没有重复元素(Set接口特征),并且保存的数据是没有顺序的,即:HashSet子类的特征属于无序排列。
范例:观察TreeSet子类
public static void main(String[] args) throws Exception{
Set<String> all=new TreeSet<String>();
all.add("X");
all.add("B");
all.add("A");//重复数据
all.add("B");
System.out.println(all);
}
结果:
image.png
此时程序使用了TreeSet子类,发现没有重复数据(Set接口特征),保存的内容自动排序(通过Comparable接口实现的排序)。
关于数据排序的说明
既然TreeSet子类保存的内容可以进行排序,下面就不如编写一个自定义的类来保存数据:
class Book{
private String title;
private double price;
public Book(String title,double price){
this.title=title;
this.price=price;
}
@Override
public String toString() {
return "Name:"+this.title+"price:"+this.price;
}
}
public class TestDemo{
public static void main(String[] args) throws Exception{
Set<Book> all=new TreeSet<Book>();
System.out.println("length:"+all.size()+" is empty:"+all.isEmpty());
all.add(new Book("Java",20.8));
all.add(new Book("Java",20.8));//全部信息重复
all.add(new Book("Android",20.8));//价格信息重复
all.add(new Book(".NET",30.7));//都不重复
System.out.println(all);
}
}
报错了,报错信息为
image.png
很明显,在进行对象数组排序的时候,一定要使用比较器。应该使用Comparable进行比较。(定义比较规则)并且在比较方法中,需要将这个类的所有属性都参与到比较之中
class Book implements Comparable<Book>{
private String title;
private double price;
public Book(String title,double price){
this.title=title;
this.price=price;
}
@Override
public String toString() {
return "Name:"+this.title+"price:"+this.price;
}
@Override
public int compareTo(Book o) {
if(this.price>o.price){
return 1;
}
if(this.price<o.price){
return -1;
}
return 0;
}
}
public class TestDemo{
public static void main(String[] args) throws Exception{
Set<Book> all=new TreeSet<Book>();
System.out.println("length:"+all.size()+" is empty:"+all.isEmpty());
all.add(new Book("Java",20.8));
all.add(new Book("Java",20.8));//全部信息重复
all.add(new Book("Android",20.8));//价格信息重复
all.add(new Book(".NET",30.7));//都不重复
System.out.println(all);
}
}
结果:
image.png
我们发现,价格相同的,它也认为重复了。
因此通过检测我们可以判断,TreeSet是通过Comparable接口的CompareTo方法来判断是否是重复数据,如果返回的是0,就认为是重复数据,不会被保存。
所以最终的代码需要比较title与price:
class Book implements Comparable<Book>{
private String title;
private double price;
public Book(String title,double price){
this.title=title;
this.price=price;
}
@Override
public String toString() {
return "Name:"+this.title+"price:"+this.price;
}
@Override
public int compareTo(Book o) {
if(this.price>o.price){
return 1;
}
if(this.price<o.price){
return -1;
}
return this.title.compareTo(o.title);
}
}
public class TestDemo{
public static void main(String[] args) throws Exception{
Set<Book> all=new TreeSet<Book>();
System.out.println("length:"+all.size()+" is empty:"+all.isEmpty());
all.add(new Book("Java",20.8));
all.add(new Book("Java",20.8));//全部信息重复
all.add(new Book("Android",20.8));//价格信息重复
all.add(new Book(".NET",30.7));//都不重复
System.out.println(all);
}
}
最终结果也实现了按照title和price两个属性排序。
image.png
关于重复元素的说明
很明显,Comparable接口只能够负责TreeSet子类进行重复元素的判断,它并不是真正的能够用于进行重复元素验证的操作。如果要想判断重复元素,那么只能够依靠Object类中所提供的方法。
取得哈希码:public int hashCode()
先判断对象的哈希码是否相同,依靠哈希码取得一个对象的内容
对象比较:public boolean equals(Object obj)
再将对象的属性进行依次的比较。
Tips:可以使用IDEA的generate生成hashCode和equals。
由于HashSet基于HashMap,基于数组和链表,有基本的哈希算法进行比较,所以其仍会出现重复数据(虽然内存地址不同),此时如果要进行真正意义上重复数据的比较,则覆写Object的hashCode和equals方法来去重。
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
@Override
public String toString() {
return "Name:" + this.title + "price:" + this.price;
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
Set<Book> all = new HashSet<Book>();
System.out.println("length:" + all.size() + " is empty:" + all.isEmpty());
all.add(new Book("Java", 20.8));
all.add(new Book("Java", 20.8));// 全部信息重复
all.add(new Book("Android", 20.8));// 价格信息重复
all.add(new Book(".NET", 30.7));// 都不重复
System.out.println(all);
}
}
上面代码加上IDE生成的hashCode和equals完成hashSet的去重。
以后在非排序的情况下,只要是判断重复元素依靠的永远都是hashCode与equals
总结:
1.在开发之中,Set接口绝对不是首选,如果真要使用也建议使用HashSet子类
2.Comparable这种比较器大部分情况下只会存在于Java理论范畴内,例如:要进行TreeSet。
3.Set不管如何操作,必须始终保持一个前提:数据不能够重复。
网友评论