一、Set集合的使用注意事项

(1)set接口下有三个实现类HashSet、TreeSet、LinkedHashSet。常用前两个。

(2)要知道set集合中的元素是无序的(是指存入的顺序跟取出的顺序是不一致的),且不可以重复。

(3)但TreeSet又说是有序的,但是这个有序不是跟上面set无序相反概念,这个有序是指,按照存入元素本身的自然顺序自动进行排序,最后输出一列有序元素。

 

二、HashSet集合

(1)底层是一个HashMap实现的。他用HashMap来保存HashSet的所有元素,会定义一个虚拟的Object对象来作为HashMap的value值,并且将此对象定义为static final。 

源码:(莫等闲老哥解释的,搬来学习学习)

// 底层使用HashMap来保存HashSet中所有元素。  
    private transient HashMap<E,Object> map;  
      
    // 定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。  
    private static final Object PRESENT = new Object();  
  
    /** 
     * 默认的无参构造器,构造一个空的HashSet。 
     *  
     * 实际底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。 
     */  
    public HashSet() {  
    map = new HashMap<E,Object>();  
    }  
  
    /** 
     * 构造一个包含指定collection中的元素的新set。 
     * 
     * 实际底层使用默认的加载因子0.75和足以包含指定 
     * collection中所有元素的初始容量来创建一个HashMap。 
     * @param c 其中的元素将存放在此set中的collection。 
     */  
    public HashSet(Collection<? extends E> c) {  
    map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));  
    addAll(c);  
    }

(2)HashSet中元素不可重复,是用hashCode和equals方法来判断依据的。

      1)如果hashCode相同,equals方法比较不同,那在内存中就会存在同一片区域(计算出hash地址相同,hashCode不是存储地址),那么就会顺次后延一位存入(实际上底层是一个数组和一个链表,首先地址相同找到要存放的数组的位置,然后比较equals,不同就放入链表中,相同不存)

      2)如果hashCode相同,equals比较相同,则认定为相同元素,不允存储。

      3)如果hashCode不同,那就直接认定不同元素,不会调用equals方法,直接存储

 

三、TreeSet集合

(0)TreeSet集合会根据存入元素自身的自然顺序进行排序,所以输出是一个有序的集合。

(1)其实TreeSet下面也是封装了一个TreeMap来实现的,跟HashSet类似,再底层就是一个红黑树来实现了(并不懂红黑树)。

(2)当TreeSet中没有指定泛型时候,且也没有自定义比较器,那么存入的元素就要是同一种类型的,否则就会报java.lang.ClassCastException,那是因为TreeSet内部是有序的,会有一个自动的排序比较过程。如果两种不同的类型,那么就不具有可比性,两种类型之间也不能强制相互转换。代码如下

TreeSet tSet = new TreeSet();
        tSet.add(10);
        tSet.add(15);
        tSet.add(3);
        tSet.add(1);
        tSet.add(55);
        tSet.add(36);
        tSet.add(66);
//      不指定泛型的时候,存入的只能是同一个类型的数据,因为TreeSet内部是有序的,会有一个自动的排序比较过程。
//        如果是两种不同的类型,那就不具有可比性,自然抛出异常
        tSet.add("jjj");

(3)自定义比较规则:

    1)TreeSet集合中存入的自定义类型自身具有比较性(该类实现Comparable接口,重写compareTo方法,定义比较规则)。

public class Animal implements Comparable<Animal> {

    private String name;
    private String color;
    private double weight;

    public Animal() {}
    
    public Animal(String name, String color, double weight) {
        this.name = name;
        this.color = color;
        this.weight = weight;
    }
    
    @Override
    public int compareTo(Animal o) {

        // 判断错误输入null
        if (this.name == null || this.color == null) {
            throw new RuntimeException("属性值不能为null");
        }
        // 判断是否重复元素
        if (this.name.equals(o.getName()) && this.color.equals(o.getColor()) && this.weight == o.getWeight()) {
            return 0;
        }
       //按照体重排序
        if (this.weight >= o.getWeight()) {
            return 1; //正数排在后面
        } else {
            return -1; //负数排在前面
        }
    }

    2)在声明一个TreeSet集合的时候,传入一个比较器,使用匿名内部类的方式实现Compartor接口,重写compare方法。

Set<Animal> treeSet = new TreeSet<>(new Comparator<Animal>() { //传入一个比较器   匿名内部的形式

            @Override
            public int compare(Animal o1, Animal o2) {
                
                //判断属性值为null
                if (o1.getName() == null || o1.getColor() == null) {
                    throw new RuntimeException("属性值不能为null");
                }
                
                //所有属性都相等时候,重复元素 不能存储
                if (o1.getName().equals(o2.getName()) && 
                    o1.getColor().equals(o2.getColor()) &&
                    o1.getWeight() == o2.getWeight()) {
                    return 0;
                }else {
                    if(o1.getWeight()>=o2.getWeight()) {
                        return -1;
                    }else {
                        return 1;
                    }
                }
            }
        });

        treeSet.add(new Animal("狼狗", "黑色", 30.5));
        treeSet.add(new Animal("柴犬", "黄色", 11.0));
        treeSet.add(new Animal("杜宾犬", "灰色", 20.0));
        treeSet.add(new Animal("泰迪", "棕色", 8.0));
        treeSet.add(new Animal("哈士奇", "黑白", 25.5));
        treeSet.add(new Animal("哈士奇", "黑白", 25.5));
//        treeSet.add(new Animal(null,"黑白",25.5));

        for (Animal animal : treeSet) {
            System.out.println(animal);
        }