java中Collection的子接口Set接口

一.Set接口概述

  • Set接口是Collection的子接口,Set接口没有提供额外的方法,它用来存储无序的,不可重复的数据。
  • Set集合不允许包含相同的元素,如果试把二个相同的元素加入到同一个Set集合中,则添加操作失败。
  • Set判断二个对象是否相同不是使用 ==运算符,而是根据equals()方法。
  • Set接口有三个实现类,HashSet,LinkedHashSet,TreeSet,但它的典型实现是HashSet,大多数时候使用Set集合时都是用这个实现类。
  • 对于存放在Set容器中的对象,对应的类一定要重写equals()方法和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。

重写hashCode()方法的基本原则:

  • 在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值。
  • 当二个对象的equals()方法比较返回true时,这二个对象的hashCode()方法返回值也应相等。
  • 对象中用作equals()方法比较的Field,都应该用来计算hashCode值。

重写equals()方法的基本原则:

  • 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),二个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()他们仅仅是二个对象。
  • 因此,违反了“相等的对象必须具有相等的散列码”。
  • 结论:复写equals方法的时候一般都需要同时复写HashCode方法,通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。

1.HashSet:是按Hash算法来存储集合中的元素,,因此具有很好的存取,查找,删除性能,底层是数组加链表。

HashSet的特点:不能保证元素的排列顺序,HashSet不是线程安全的,集合元素可以是null

HashSet集合判断二个元素相等的标准:二个对象通过hashCode()方法比较相等,并且二个对象的equals()方法返回值也相等。

2.LinkedHashSet:是HashSet的子类,在添加数据的同时,每个数据还维护了二个引用,记录此数据前一个数据和后一个数据,遍历其内部数据时,可以按照添加时的顺序遍历。

优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

LinkedHashSet是根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素是有很好的性能。

LinkedHashSet不允许集合元素重复。

3.TreeSet:放入的数据是同一个类的,可以按照添加对象的指定属性排序

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

TreeSet底层是使用红黑树结构存储数据

TreeSet二种排序方法:自然排序(实现comparable接口)和定制排序(comparator),默认情况下,采用自然排序。

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

 TreeSet set1 = new TreeSet();
        //不能添加不同类的对象:
        //set1.add("SS");
        //set1.add(123);
       // set1.add(456);
        //set1.add(new Person("Tom",12));
        //1.能添加同类型的对象,按从从小到大排序
        set1.add(456);
        set1.add(-456);
        set1.add(6);
        Iterator iterator = set1.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //2.输出会报异常,要想执行排序,要重写comparableTo()方法
        set1.add(new Person("Tom",12));
        set1.add(new Person("Jerry",2));
        set1.add(new Person("Mike",18));

重写compareTo()如下:

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("输入的类型不匹配");
}
}

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

  Comparator comparator = new Comparator() {
            //按照年龄从小到大
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Person && o2 instanceof Person){
                    Person p1 = (Person)o1;
                    Person p2 = (Person)o2;
                    return  Integer.compare(p1.getAge(),p2.getAge());
                }else{
                    throw new RuntimeException("输入数据类型不匹配");
                }
            }
        };
        TreeSet set = new TreeSet();
        set1.add(new Person("Tom",12));
        set1.add(new Person("Jerry",2));
        set1.add(new Person("Mike",18));
        Iterator iterator = set1.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

新增的方法如下:

Comparator comparator()
Object first()
Object last()
Object lower(Object e)
Object higher(Object e)
SortedSet subSet(fromElement,toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement)

  二.Set接口存储无序,不可重复的数据

1.无序性:不等于随机性。存储的数据在底层数组中,并非按照数组索引的顺序添加,而是根据数据的哈希值

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

三.添加元素的过程:以HashSet为例

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

 jdk7中,元素a与已经存在指定索引位置上的数据以链表的方式存储,元素a放到数组中指向原来的元素,jdk8是原来的元素放在数组中指向元素a。

               

猜你喜欢

转载自blog.csdn.net/weixin_44022886/article/details/90054288
今日推荐