赞
踩
1.Set接口和List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格。与List接口不同的是,Set接口中的元素无序(不是按照元素插入顺序来排序)且以某种规则(自然排序或定制排序)保证存入的元素不重复(自定义类型的一定要重写Object类中的equals方法和hashCode方法)。
2.Set接口主要有2个实现类:HashSet和TreeSet。HsahSet是根据对象的哈希值来确定元素在集合中的位置,因此具有良好的存取和查找性能。TreeSet是以二叉树的方式来存储元素,它可以实现对集合中的元素进行排序。
1.是Set接口的典型实现类,大多数使用Set接口的都是使用HashSet类。
2.集合元素可以是null;无序且不能保证元素的排序顺序(存储与插入顺序不一致);线程不安全的。
3.HashSet底层:数组+链表。底层也有数组,数组初始长度是16,当使用率超过原始长度的0.75倍(12),就会扩容到原来的2倍:16->32->64....
1.HashSet集合之所以能确保不出现重复的元素,是因为在存入元素时做了很多工作。当调用HashSet集合的add()方法存入元素时,首先调用存入元素的hashCode()方法获得对象的哈希值,再根据对象的哈希值算出一个存储位置(可以理解为在数组中的索引);如果该位置没有元素,则添加成功;如果该位置上有元素存在,则会调用equals()方法让当前存入的元素依次和该位置上的元素进行比较。如果返回的结果为false,就该将元素存入集合;若返回为true,则说明有重复元素,就该将元素舍弃。
根据前面的分子不难看出,当向集合中存入元素时,为了保证HashSet集合正常工作,要求在存入对象时,需要重写Object类的hashCode()方法和equals()方法。如果将开发者自定义的类型对象存入HashSet,结果又如何呢?接下来通过一个案例进行演示将Student1类型对象存入HashSet。
- package 集合;
-
- import java.util.HashSet;
- import java.util.Objects;
-
- public class HashSetDemo1 {
- public static void main(String[] args) {
- HashSet set=new HashSet();
- Student1 stu=new Student1("1","张翼德");
- Student1 stu1=new Student1("2","关羽");
- Student1 stu2=new Student1("3","刘备");
- Student1 stu3=new Student1("3","刘备");
- set.add(stu);
- set.add(stu1);
- set.add(stu2);
- set.add(stu3);
- System.out.println(set);
-
-
- }
- }
- class Student1{
- String id;
- String name;
-
- public Student1(String id, String name) {
- this.id = id;
- this.name = name;
- }
-
- @Override
- public String toString() {
- return "Student1{" +
- "id='" + id + '\'' +
- ", name='" + name + '\'' +
- '}';
- }
- // @Override
- // public boolean equals(Object o) {
- // if (this == o) return true;
- // if (o == null || getClass() != o.getClass()) return false;
- // Student1 student1 = (Student1) o;
- // return Objects.equals(id, student1.id) && Objects.equals(name, student1.name);
- // }
- //
- // @Override
- // public int hashCode() {
- // return Objects.hash(id, name);
- // }
- }
明显可以看出,“ Student1{id='3', name='刘备'}”重复了,本来是不允许出现在HashSet集合中的。之所以没有去掉这样的重复元素是因为在定义Student1类时没有重写hashCode方法和equals方法,因此创建的这两个学生对象stu2和stu3所引用的对象地址不同,所以HashSet集合会认为这是2个不同的对象。接下来将重写的hashCode()方法和equals()方法解开注释 ,再次运行。
重写hashCode()方法和equals()方法后,明显没有重复元素。
LinkedHashSet是HashSet的子类,它使用双向链表来维护元素的顺序,使得元素看起来像是插入顺序存储的。
- package 集合;
-
- import java.util.LinkedHashSet;
-
- public class LinkedHashSetDemo {
- public static void main(String[] args) {
- LinkedHashSet lhs=new LinkedHashSet();
- lhs.add("111");
- lhs.add("222");
- lhs.add("333");
- lhs.add("444");
- System.out.println(lhs);
-
- }
- }
1.SortedSet是Set的另一个子接口,TreeSet是SortedSet接口的实现类
2.TreeSet可以确保集合元素处于排序状态
3.TreeSet底层使用红黑树结构
4.TreeSet两种排序方式:1.自然排序。2.定制排序。
特点:有序(自然排序或定制排序),查询速度比List集合快
TreeSet集合中的元素在进行比较时,都会调用CompareTo()方法,该方法是在Comparable接口中定义的,因此想要对集合中的元素进行排序,就必须实现Comparable接口。Java中的大部分类都实现了Comparable接口,并默认实现了compareTo()方法,如Integer、Double、String等。
在实际开发中,除了会向TreeSet集合中存储一些Java中默认的数据类型外,还会存储一些用户自定义的类型数据,如Student类型数据、Teacher类型数据等。由于这些自定义类型的数据没有实现Comparable接口,也就无法在TreeSet集合中进行排序操作。为了解决此问题,Java提供了2种TreeSet集合的排序规则:自然排序和定制排序。在默认情况下,TreeSet集合都是采用自然排序。
自然排序:
1.自然排序要求向TreeSet集合中存储的元素所在类必须实现Comparable接口,并重写compareTo()方法,然后TreeSet集合就会对该类型元素使用compareTo()方法进行比较,并默认进行升序排列。
2.比较两个对象的大小(this/o)
当返回值为0的时候,表示this和o相同
当返回值为1的时候,表示this大于o,按照升序排列(由小到大)
当返回值为-1的时候,表示this小于o,按照降序排列(由大到小)
案例:将Student2类的对象(有int id,String name,int age属性)添加进TreeSet集合中,且按照id升序,age降序的规则进行排序。
- package 集合;
-
- import java.util.Objects;
- import java.util.TreeSet;
-
- public class TreeSetDemo {
- public static void main(String[] args) {
- TreeSet tr=new TreeSet();
- tr.add(new Student2(1,"鲁班七号",20));
- tr.add(new Student2(1,"鲁班七号",26));
- tr.add(new Student2(5,"小乔",24));
- tr.add(new Student2(3,"李白",25));
- System.out.println(tr);
- }
- }
- class Student2 implements Comparable{
- int id;
- String name;
- int age;
-
- public Student2(int id, String name, int age) {
- this.id = id;
- this.name = name;
- this.age = age;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Student2 student2 = (Student2) o;
- return id == student2.id && age == student2.age && Objects.equals(name, student2.name);
- }
-
- @Override
- public String toString() {
- return "Student2{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
-
- public int getId() {
- return id;
- }
-
- public void setId(int id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- @Override
- public int compareTo(Object o) {
- //1.判断o是否可以转为Student2类型
- if(o instanceof Student2){
- //2.可以转的话,将o强制转换成Student2类型
- Student2 student2=(Student2) o;
- //3.比较id(按照升序)
- int compare=Integer.compare(this.id,((Student2) o).id);
- //4.若compare为0,则说明比较对象的id相等
- if(compare==0){
- //5.再比较年龄(按照降序)
- return -Integer.compare(this.age,((Student2) o).age);
- }else{
- //6.若id不相同,直接返回id排序顺序
- return compare;
- }
- }else{
- //7.若不能转换成Student2类型,则new 异常,抛出去
- throw new RuntimeException("转换异常!!!");
- }
- }
- }
根据最终的比较结果显示,的确是按照id升序,age降序的规则进行排序的。
定制排序:
1.有时候,用户自定义的类型数据所在类没有实现Comparable接口或者实现了Comparable接口的类并不想按照定义的compareTo()方法进行排序。例如:希望存储在TreeSet集合中的字符串可以按照字符串长度而不是按照英文字母的顺序来排序,这时,我们就可以使用定制排序来进行排序。
2.要实现定制排序,需要将实现Comparator接口的实现类作为参数,传递给TreeSet集合
2.1需要写一个类实现Comparator接口(因为要重写Comparator里面的方法)
2.2需要创建Comparator实现类的对象
所以可以直接new 接口类型,重写方法即可(匿名内部类)
- package 集合;
-
-
-
- import java.util.Comparator;
- import java.util.Objects;
- import java.util.TreeSet;
-
- public class TreeSetDemo1 {
- public static void main(String[] args) {
- Comparator com=new Comparator() {
- @Override
- public int compare(Object o1, Object o2) {
- //判断o1和o2是否可以转为Teacher类型
- if(o1 instanceof Teacher&&o2 instanceof Teacher){
- //若可以转,则将o1和o2强转为Teacher类型
- Teacher th1=(Teacher) o1;
- Teacher th2=(Teacher) o2;
- //比较o1和o2的名字name(按照升序)
- int compare=th1.name.compareTo(th2.name);
- //若比较结果为0,说明名字name相同,再比较age(按照降序)
- if (compare==0){
- return -Integer.compare(th1.age,th2.age);
- }else{
- //若比较结果不为0,说明名字name不相同,此时直接输出按照name的排序规则的顺序
- return compare;
- }
- }else{
- //若不能转换成Teacher类型,直接new 异常 ,抛出去。
- throw new RuntimeException("类型转换异常");
- }
-
- }
- };
- TreeSet set=new TreeSet(com);
- set.add(new Teacher("888",8));
- set.add(new Teacher("111",1));
- set.add(new Teacher("111",8));
- set.add(new Teacher("666",6));
- set.add(new Teacher("222",2));
- System.out.println("按照name升序且age降序的排序结果为:"+set);
-
- }
- }
- class Teacher{
- String name;
- int age;
-
- public Teacher(String name, int age) {
- this.name = name;
- this.age = age;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "Teacher{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Teacher teacher = (Teacher) o;
- return age == teacher.age && Objects.equals(name, teacher.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(name, age);
- }
- }
根据最终的比较结果显示,的确是按照name升序,age降序的规则进行排序的。
注意:在使用TreeSet集合存储数据时,TreeSet集合会对存入元素进行比较排序,所以为了保证程序的正常运行,一定要保证存入TreeSet集合中的元素是同一种数据类型。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。