容器-TreeSet容器类(十三)
-
TreeSet是一个可以对元素进行排序的容器。底层实际是用TreeMap实现的。内部维持了一个简化版的TreeMap,通过Key来存储Set的元素。TreeSet内部需要对存储的元素进行比较,因此需要对存储的元素进行排序比较,因此,我们需要给定排序的规则。
-
排序规则的实现方式:
- 通过元素自身实现比较的方式
- 通过比较器指定比较规则
-
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); } } }
-
为什么我们没规定规则,字符创会自动排序呢,答:是因为在String类实现了Comparable接口
-
通过元素自身实现比较规则:
在元素自身实现比较规则时,需要实现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接口去定义比较规则 } } }
-
-
通过比较器实现比较规则
通过比较器定义比较规则时,我们需要单独创建一个比较器,比较器需要实现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); }
-
-
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 ? e2==null : 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; }