***3.Set子接口(不重复)

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 all=new HashSet();
        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 all=new TreeSet();
        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 all=new TreeSet();
        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{
    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 all=new TreeSet();
        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{
    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 all=new TreeSet();
        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 all = new HashSet();
        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不管如何操作,必须始终保持一个前提:数据不能够重复。

你可能感兴趣的:(***3.Set子接口(不重复))