java集合Collection子接口之二: Set接口 (总结于尚硅谷java)

Collection子接口之二:Set接口
  • Set接口是Collection的子接口,set接口没有提供额外的方法
  • Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set 集合中,则添加操作失败
  • Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法

1.set接口的整体框架

|----Collection接口:单列集合,用来存储一个一个的对象
  |----Set接口:存储无序的、不可重复的数据   -->高中讲的“集合”
  |----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
  |----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
  |----TreeSet:可以按照添加对象的指定属性,进行排序。

2.set接口的说明

HashSet

​ 1.HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
​ 2.HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。

​ HashSet 具有以下特点:

1. 不能保证元素的排列顺序
2. HashSet 不是线程安全的
3. 集合元素可以是 null

​ 3.HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相

​ 4.对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

Set:存储无序的、不可重复的数据
    以HashSet为例说明:
    1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

    2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。

    二、添加元素的过程:以HashSet为例:
        我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
        此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断
        数组此位置上是否已经有元素:
            如果此位置上没有其他元素,则元素a添加成功。 --->情况1
            如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
                如果hash值不相同,则元素a添加成功。--->情况2
                如果hash值相同,进而需要调用元素a所在类的equals()方法:
                       equals()返回true,元素a添加失败
                       equals()返回false,则元素a添加成功。--->情况3

        对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
        jdk 7 :元素a放到数组中,指向原来的元素。
        jdk 8 :原来的元素在数组中,指向元素a
        总结:七上八下

        HashSet底层:数组+链表的结构。

向HashSet中添加元素的过程:

1. 当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好) 
  1. 如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
  2. 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相
    等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

在这里插入图片描述

Set set = new HashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
	/* 不同顺序输出 但不表示每一次启动都是不同的顺序,只是添加到set里面的那次不同
	    AA
        CC
        129
        456
        123
        User{name='Tom', age=12}*/

====================重写方法[idea 自动导入]========================
   @Override
    public boolean equals(Object o) {
        System.out.println("User equals()....");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (age != user.age) return false;
        return name != null ? name.equals(user.name) : user.name == null;
    }

    @Override
    public int hashCode() { //return name.hashCode() + age;
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
//复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

LinkedHashSet

  • LinkedHashSet 是 HashSet 的子类
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
  • LinkedHashSet 不允许集合元素重复。
    //LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个
    //数据和后一个数据。
    //优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
    @Test
    public void test2(){
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
            /* 就是按照添加顺序的方式输出
                456
                123
                AA
                CC
                User{name='Tom', age=12}
                129
            */
        }
    }

在这里插入图片描述

Set set = new LinkedHashSet();
set.add(new String("AA"));
set.add(456);
set.add(456);
set.add(new Customer("刘德华", 1001));

TreeSet

  • TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

  • TreeSet 两种排序方法:自然排序定制排序。默认情况下,TreeSet 采用自然排序。

  • 有序,查询速度比List快

TreeSet 要注意的点:

​ 1.向TreeSet中添加的数据,要求是相同类的对象。

        TreeSet set = new TreeSet();

        //失败:不能添加不同类的对象
        set.add(123);
        set.add(456);
        set.add("AA");
        set.add(new User("Tom",12));

​ 2.两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator),默认使用自然排序

        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

/*Input:
        User{name='Tom', age=12}
        User{name='Mike', age=65}
        User{name='Jim', age=2}
        User{name='Jerry', age=32}
        User{name='Jack', age=33}
        User{name='Jack', age=56}

*/

​ 3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().

	3.1 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 

接口。因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同
一个类的对象

​ 3.2 向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。

​ 3.3 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值。

    @Override
    public int compareTo(Object o) {
        if(o instanceof User){
            User user = (User)o;
//            return -this.name.compareTo(user.name);
            int compare = -this.name.compareTo(user.name);
            if(compare != 0){
                return compare;
            }else{
                return Integer.compare(this.age,user.age);
            }
        }else{
            throw new RuntimeException("输入的类型不匹配");
        }

    }

​ 4.定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

​ 4.1 利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。 要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。
​ 4.2 此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。
​ 4.3 使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。

    Comparator com = new Comparator() {
            //按照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

/*Input
        User{name='Jim', age=2}
        User{name='Tom', age=12}
        User{name='Jerry', age=32}
        User{name='Mary', age=33}
        User{name='Jack', age=56}
        User{name='Mike', age=65}
*/

注意点:

Collection coll = new ArrayList();

        coll.add(123);
        coll.add(456);
        coll.add(343);
        coll.add(343);
		循环方式
        coll.forEach(System.out::println);
public static List duplicateList(List list){
        HashSet h = new HashSet();
        h.addAll(list);
        return  new ArrayList(h);
    }

    @Test  
    public void Test2(){ //除去重复的元素 使用Hashset的特性
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(new Integer(1));
        arrayList.add(new Integer(1));
        arrayList.add(new Integer(2));
        arrayList.add(new Integer(2));
        arrayList.add(new Integer(4));
        arrayList.add(new Integer(4));

        List list2 = duplicateList(arrayList);
        for (Object i :list2){
            System.out.println(i);
        }
    }
    public void Test3(){
        HashSet<Object> objects = new HashSet<>();
        Person p1 = new Person(80281, "Tom");
        Person p2 = new Person(30281, "MMA");
        Person p3 = new Person(40581,"JoY");


        objects.add(p1);
        objects.add(p2);
        objects.add(p3);
        System.out.println(objects);
        //[Person{id=30281, name='MMA'}, Person{id=40581, name='JoY'}, Person{id=80281, name='Tom'}]

        p1.name = "DDD"; //修改集合中某个属性 = 添加了新的
        objects.remove(p1); //原来的删除  后面的还保留
        System.out.println(objects);
        //[Person{id=30281, name='MMA'}, Person{id=40581, name='JoY'}, Person{id=80281, name='DDD'}]

        objects.add(new Person(80281,"DDD")); //多一个新的
        System.out.println(objects);
        //[Person{id=10001, name='DDD'}, Person{id=30281, name='MMA'}, Person{id=40581, name='JoY'}, Person{id=80281, name='DDD'}]

        objects.add(new Person(80281,"Tom"));
        System.out.println(objects);
        //[Person{id=10001, name='DDD'}, Person{id=30281, name='MMA'}, Person{id=40581, name='JoY'}, Person{id=80281, name='DDD'}, Person{id=80281, name='Tom'}]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值