容器-TreeSet容器类(十三)

容器-TreeSet容器类(十三)

  1. TreeSet是一个可以对元素进行排序的容器。底层实际是用TreeMap实现的。内部维持了一个简化版的TreeMap,通过Key来存储Set的元素。TreeSet内部需要对存储的元素进行比较,因此需要对存储的元素进行排序比较,因此,我们需要给定排序的规则。

  2. 排序规则的实现方式:

    • 通过元素自身实现比较的方式
    • 通过比较器指定比较规则
  3. TreeSet的使用,和HashSet实现的方法一样的,因为他们都实现了Set接口。

    import java.util.Set;
    import java.util.TreeSet;
    
    public class TreeSetTest {
          
          
        public static void main(String[] args) {
          
          
            //实例化TreeSet
            Set<String> set=new TreeSet<>();// TreeSet对字符串进行排序处理了,但是还是不允许有重复的元素出现
            //添加元素
            set.add("c");
            set.add("a");
            set.add("d");
            set.add("b");
            set.add("a");
    
            //获取元素
            for (String str:set){
          
          
                System.out.println(str);
            }
        }
    }
    
  4. 为什么我们没规定规则,字符创会自动排序呢,答:是因为在String类实现了Comparable接口

在这里插入图片描述

  1. 通过元素自身实现比较规则:

    在元素自身实现比较规则时,需要实现Comparable接口的compareTo方法,该方法中用来**定义比较规则。**TreeSet调用该方法来完成对元素的排序处理,

    • 创建User类

      import java.util.Objects;
      
      public class Users implements Comparable<Users> {
              
              
          private String username;
          private int userage;
      
          public Users(String username, int userage) {
              
              
              this.username = username;
              this.userage = userage;
          }
      
          public Users() {
              
              
          }
      
          @Override
          public boolean equals(Object o) {
              
              
              System.out.println("equals............");
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Users users = (Users) o;
              return userage == users.userage &&
                      Objects.equals(username, users.username);
          }
      
          @Override
          public int hashCode() {
              
              
              return Objects.hash(username, userage);
          }
      
          public String getUsername() {
              
              
              return username;
          }
      
          public void setUsername(String username) {
              
              
              this.username = username;
          }
      
          public int getUserage() {
              
              
              return userage;
          }
      
          public void setUserage(int userage) {
              
              
              this.userage = userage;
          }
      
          @Override
          public String toString() {
              
              
              return "Users{" +
                      "username='" + username + '\'' +
                      ", userage=" + userage +
                      '}';
          }
      
          //定义比较规则
          //返回的是正数:大,负数:小 ,0:相等
          @Override
          public int compareTo(Users o) {
              
              //o 参数的类型取决于 Comparable<Users>的泛型类型
              //通过年龄去定义比较规则
              if (this.userage > o.getUserage()){
              
              //当前的年龄比传进来的年龄大,则返回正数,
                  return 1;                       //这是由小到大的规则
              }
              //年龄相同则通过名字的排序来进行比较
              if (this.userage == o.getUserage()){
              
              
                  //名字是字符串,我们怎么来比较,因为Stringl类已经实现了compareTo的方法进行排序了
                  //所以,我们直接调用compareTo就可以了
                 return this.username.compareTo(o.getUsername());
              }
              return -1;//当前的年龄小传进来的年龄大,则返回负数
                          //相等的话,谁在谁前,谁在谁后,就没有关系了
          }
      }
      
    • 在TreeSet中存放Users对象,进行排序处理

      import java.util.Set;
      import java.util.TreeSet;
      
      public class TreeSetTest {
              
              
          public static void main(String[] args) {
              
              
              //实例化TreeSet
              Set<String> set=new TreeSet<>();// TreeSet对字符串进行排序处理了,但是还是不允许有重复的元素出现
              //添加元素
              set.add("c");
              set.add("a");
              set.add("d");
              set.add("b");
              set.add("a");
      
              //获取元素
              for (String str:set){
              
              
                  System.out.println(str);
              }
      
              System.out.println("-----------------");
      
              Set<Users> set1=new TreeSet<>();
              Users u=new Users("oldlu",18);
              Users u1=new Users("admin",22);//年龄相同,通过名字来排序,我们已经在Users类定义好这个规则
              Users u2=new Users("sxt",22);
              set1.add(u);
              set1.add(u1);
              set1.add(u2);
              for (Users users:set1){
              
              
                  System.out.println(users);//没有定义任何排序的规则是会报错的,没有实现Comparable接口
                                          //解决的办法是去Users实现Comparable接口去定义比较规则
              }
          }
      }
      
  2. 通过比较器实现比较规则

    通过比较器定义比较规则时,我们需要单独创建一个比较器,比较器需要实现Comparator接口中的compare方法来定义比较规则。在实例化TreeSet时将比较器对象交给TreeSet来完成元素的排序处理。此时元素自身就不需要实现比较规则了。

    • 创建比较器

      import java.util.Comparator;
      
      public class StudentComparator implements Comparator<Student> {
              
              
      
          //定义比较规则
          @Override
          public int compare(Student o1, Student o2) {
              
              
              //比较年龄比较,从小到大
              if (o1.getAge()>o2.getAge()){
              
              
                  return 1;
              }
      
              //如果年龄相同,则用名字进行排序
              if (o1.getAge()==o2.getAge()){
              
              
                  return o1.getName().compareTo(o2.getName());
              }
      
              return -1;
      
          }
      }
      
      
    • 创建Student类

      import java.util.Objects;
      
      public class Student {
              
              
          private String name;
          private int age;
      
          @Override
          public String toString() {
              
              
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      
          public Student() {
              
              
          }
      
          public Student(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 boolean equals(Object o) {
              
              
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Student student = (Student) o;
              return age == student.age &&
                      Objects.equals(name, student.name);
          }
      
          @Override
          public int hashCode() {
              
              
              return Objects.hash(name, age);
          }
      }
      
      
    • 在TreeSet中存储User对象,进行排序处理

      //通过外部比较器,进行排序
              System.out.println("------------------------------------");
              Set<Student> set2=new TreeSet<>(new StudentComparator());//new StudentComparator() 实现外部比较器去定义规则
              Student s=new Student("oldlu",18);
              Student s1=new Student("admin",22);
              Student s2=new Student("sxt",22);
      
              set2.add(s);
              set2.add(s1);
              set2.add(s2);
      
              for (Student student:set2){
              
              
                  System.out.println(student);
              }
      
  3. TreeSet源码的底层分析

    • 通过TreeSet对象,用Ctrl+鼠标左键进入源代码

        Set<Student> set2=new TreeSet<>(new StudentComparator());
      
    • 看TreeSet的继承结构和成员变量

      public class TreeSet<E> extends AbstractSet<E>//继承了AbstractSet<E>类
          implements NavigableSet<E>, Cloneable, java.io.Serializable
      {
              
              	//实现了NavigableSet<E>接口,是间接实现了map接口
          /**
           * The backing map.
           */
          private transient NavigableMap<E,Object> m;//定义了NavigableMap类型的m,你可以直接看成NavigableMap接口
          // Dummy value to associate with an Object in the backing Map
          private static final Object PRESENT = new Object();//还是一个new了object的对象,用于填补map的Value的值
      
    • 通过map,进入源代码,在用Ctrl+H看Map的继承关系,可以发现TreeMap类继承了NavigableMap接口,NavigableMap继承了SortedMap接口,SortedMap接口继承了Map接口,所以NavigableSet接口,是间接实现了map接口

在这里插入图片描述

  • 看无参构造方法

        public TreeSet() {
          
          
            this(new TreeMap<E,Object>());//又调用了自己的有参构造,new TreeMap<E,Object>()可以看出TreeSet的底层使用TreeMap来完成元素的存储
        }
    
  • 在看this();

      /**
         * Constructs a set backed by the specified navigable map.
         */
        TreeSet(NavigableMap<E,Object> m) {
          
          
            this.m = m;//把TreetSet赋给了m
        }
    
    
  • 当我们创建TreeSet出来,其实TreeMap对象也创建出来了

  • 我们来看TreeSet的add方法,先用Ctrl+鼠标左键进入源代码,再用Ctrl+Alt选择add方法TreeSet的实现接口类,进入源代码

    /**
         * Adds the specified element to this set if it is not already present.
         * More formally, adds the specified element {@code e} to this set if
         * the set contains no element {@code e2} such that
         * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
         * If this set already contains the element, the call leaves the set
         * unchanged and returns {@code false}.
         *
         * @param e element to be added to this set
         * @return {@code true} if this set did not already contain the specified
         *         element
         * @throws ClassCastException if the specified object cannot be compared
         *         with the elements currently in this set
         * @throws NullPointerException if the specified element is null
         *         and this set uses natural ordering, or its comparator
         *         does not permit null elements
         */
        public boolean add(E e) {
          
          //就是把元素添加到TreeMap的Key当中,PRESENT填补部分的Value值
            return m.put(e, PRESENT)==null;
        }
    

猜你喜欢

转载自blog.csdn.net/Xun_independent/article/details/114735065