Java集合(十一)TreeSet解读
Java集合(十一)TreeSet解读
向TreeSet中添加的数据,要求的是相同类的对象
两种排序方式:自然排序和定制排序
我们查看treeset的构造器的时候,发现TreeSet的构造器除了那种无参构造器之外,还提供了另外四种。其中TreeSet(Comparator<E>)可以传比较器。
public interface Comparator<T> {
int compare(T o1, T o2);
Comparator是一个接口,他有一个方法叫compare,我们可以在他的compare方法指定他的一个排序规则。
我们在设计代码过程中用到的compareTo方法的源码如下所示:
public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2);char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) {char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {return c1 - c2;}k++;}return len1 - len2;}
就是把value值取出来,然后进行循环比较,如果发现不相等,就返回c1-c2,然后谁的ACCIS码值大,就返回大于0的值,否则返回小于0的值。然后根据返回的值进行排序。
我们设计代码如下所示:
package com.rgf.set;import java.util.Comparator;
import java.util.TreeSet;@SuppressWarnings({"all"})
public class TreeSet_ {public static void main(String[] args) {//1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。//2.我们希望添加的元素按照字符串大小来排序//3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)//并指定排序规则TreeSet treeSet = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {//调用String的compareTo方法,进行字符串大小比较return ((String)o1).compareTo((String)o2);}});//添加数据treeSet.add("jack");treeSet.add("tom");treeSet.add("sp");treeSet.add("a");System.out.println("treeSet="+treeSet);}
}
运行界面如下所示:
这种排序是从大到小排,我们也可以将他拍成从小到大排:
return ((String)o2).compareTo((String)o1);
运行之后如下所示:
我们下来进行源码的分析:
我们在 TreeSet treeSet = new TreeSet(new Comparator() {代码处进行断点的设置:
我们点击Force Step进入之后,在退出来。
我们可以通过f9直接跳出来,我们也可以进去后再退出来,我们进行如下所示:
这个构造器接收了一个comparator,这个接口的实现对象。
public TreeSet(Comparator<? super E> comparator) {this(new TreeMap<>(comparator));}
我们继续进去如下所示:他把这个对象传给了如下所示:
这个comparator(比较器)传给了底层的TreeMap,
我们继续进行下一步,我们进入了add方法:
我们继续进行下一步:
我们再次进入了TreeMap里面了,进入了TreeMap里面的put方法。
我们继续进行下一步:
//我们将comparator赋给了cpr,comparator为我们传进去的comparator。 Comparator<? super K> cpr = comparator; //在调用treeSet.add("tom"),在底层会执行到if (cpr != null) {//是有比较器的,比较器不为空。cpr,就是我们的匿名内部类(对象)do {parent = t;//动态绑定到我们的匿名内部类(对象)compare方法cmp = cpr.compare(key, t.key); if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else //如果相等,即返回0,这个key就没有加入return t.setValue(value);} while (t != null);}else {if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;fixAfterInsertion(e);size++;modCount++;return null;}
我们继续进行下一步之后,确实进入到如下所示:
我们进行设计重复值如下所示:
package com.rgf.set;import java.util.Comparator;
import java.util.TreeSet;@SuppressWarnings({"all"})
public class TreeSet_ {public static void main(String[] args) {//1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。//2.我们希望添加的元素按照字符串大小来排序//3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)//并指定排序规则TreeSet treeSet = new TreeSet(new Comparator() {//实现了把匿名对象传给了TreeSet的底层TreeMap的Comparator的一个属性。@Overridepublic int compare(Object o1, Object o2) {//调用String的compareTo方法,进行字符串大小比较return ((String)o2).compareTo((String)o1);}});//添加数据treeSet.add("jack");treeSet.add("tom");treeSet.add("sp");treeSet.add("a");treeSet.add("a");System.out.println("treeSet="+treeSet);//1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator。}
}
我们运行界面如下所示:
我们发现运行结果里面没有重复值a,我们进行debug,如下所示:
我们进入add方法:
在进入put方法:
public V put(K key, V value) {Entry<K,V> t = root;if (t == null) {compare(key, key); // type (and possibly null) checkroot = new Entry<>(key, value, null);size = 1;modCount++;return null;}int cmp;Entry<K,V> parent;// split comparator and comparable paths
//比较器赋值给cpr.Comparator<? super K> cpr = comparator;
//比较器赋值之后,不等于空if (cpr != null) {do {parent = t;cmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else
//我们在有值相同的时候是没有进去的。return t.setValue(value);} while (t != null);}else {if (key == null)throw new NullPointerException();@SuppressWarnings("unchecked")Comparable<? super K> k = (Comparable<? super K>) key;do {parent = t;cmp = k.compareTo(t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;elsereturn t.setValue(value);} while (t != null);}Entry<K,V> e = new Entry<>(key, value, parent);if (cmp < 0)parent.left = e;elseparent.right = e;fixAfterInsertion(e);size++;modCount++;return null;}
再次继续之后,我们发现退出来了:
所以相同的值是没有进去的。
如果我们修改按照长度大小排序:
我们修改代码如下所示:
//按照长度大小排序:return ((String)o2).length()-((String)o1).length();
运行界面如下所示:
如果长度大小从小到大,代码修改如下所示:
//按照长度大小排序:return ((String)o1).length()-((String)o2).length();
运行界面如下所示:
我们在添加如如下代码的时候,添加不进去:
package com.rgf.set;import java.util.Comparator;
import java.util.TreeSet;@SuppressWarnings({"all"})
public class TreeSet_ {public static void main(String[] args) {//1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。//2.我们希望添加的元素按照字符串大小来排序//3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)//并指定排序规则TreeSet treeSet = new TreeSet(new Comparator() {//实现了把匿名对象传给了TreeSet的底层TreeMap的Comparator的一个属性。@Overridepublic int compare(Object o1, Object o2) {//调用String的compareTo方法,进行字符串大小比较//按照长度大小排序:return ((String)o1).length()-((String)o2).length();}});//添加数据treeSet.add("jack");treeSet.add("tom");treeSet.add("sp");treeSet.add("a");treeSet.add("rgf");//加入不进去。相同长度会覆盖前面的。一个大小//我们所设置的规则为长度相等就是相同的元素。//这里不进行替换,这相当于是key,key不允许重复,//重新了comparetor,所以他的长度就是key,已经有一个key是3了所以加不进去。System.out.println("treeSet="+treeSet);//1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator。}
}
运行界面如下所示:
我们将规则进行改变后,如下所示:
package com.rgf.set;import java.util.Comparator;
import java.util.TreeSet;@SuppressWarnings({"all"})
public class TreeSet_ {public static void main(String[] args) {//1.当我们使用无参构造器,创建TreeSet时,仍然是无序的。//2.我们希望添加的元素按照字符串大小来排序//3.使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)//并指定排序规则TreeSet treeSet = new TreeSet(new Comparator() {//实现了把匿名对象传给了TreeSet的底层TreeMap的Comparator的一个属性。@Overridepublic int compare(Object o1, Object o2) {//调用String的compareTo方法,进行字符串大小比较//按照长度大小排序:return ((String)o2).compareTo((String)o1);}});//添加数据treeSet.add("jack");treeSet.add("tom");treeSet.add("sp");treeSet.add("a");treeSet.add("rgf");//加入不进去。相同长度会覆盖前面的。一个大小//我们所设置的规则为长度相等就是相同的元素。//这里不进行替换,这相当于是key,key不允许重复,//重新了comparetor,所以他的长度就是key,已经有一个key是3了所以加不进去。System.out.println("treeSet="+treeSet);//1.构造器把传入的比较器对象,赋给了TreeSet的底层的TreeMap的属性this.comparator。}
}
运行界面如下所示:
我们发现成功添加了进去。
我们查看TreeSet里面的比较器:自然排序和定制排序。
自然排序:
package com.rgf.jihe;import java.util.Iterator;
import java.util.Objects;
import java.util.TreeSet;/*** TreeSet:可以按照添加对象的指定属性,进行排序。(无法进行存放相同的数据,TreeSet和TreeMap采用红黑树的存储结构)* 特点:有序,查询速度比List快* 1.向TreeSet中添加的数据,要求是相同类的对象。* 2.两种排序方式:自然排序(实现Comparable接口)和定制排序。* 3.自然排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不再是equals()方法**/
public class TreeSetTest {public static void main(String[] args) {TreeSet set = new TreeSet();// set.add(123);//不能添加不同类的对象,//set.add("ABC"); 进行排序的时候会报错,出现ClassCastException/*set.add(456);set.add(789);set.add(452);*///结果按照从小到大顺序排列的,默认为从小到大排序// System.out.println(set);
//set默认排序按照equals,但是treeset就是按照comparable去比较set.add(new User("ABC",25));set.add(new User("A",21));set.add(new User("A",28));set.add(new User("AB",26));//会出现ClassCastException,没有提示应该按照什么排。//首先我们进行自然排序,即我们在该类实现Comparable接口。Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}
}class User implements Comparable{private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}
//我们写出来该方法则不会再继续报错。
//按照姓名从大到小排列,年龄从小到大排列@Overridepublic 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("输入的类型不匹配");}}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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return Objects.equals(name, user.name);}@Overridepublic int hashCode() {int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}
}
运行之后如下所示:
定制排序:
package com.rgf.jihe;import org.junit.Test;import java.util.Comparator;
import java.util.Iterator;
import java.util.Objects;
import java.util.TreeSet;/*** TreeSet:可以按照添加对象的指定属性,进行排序。(无法进行存放相同的数据,TreeSet和TreeMap采用红黑树的存储结构)* 特点:有序,查询速度比List快* 1.向TreeSet中添加的数据,要求是相同类的对象。* 2.两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator接口)。* 3.自然排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不再是equals()方法* 4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()方法。*/
public class TreeSetTest {public static void main(String[] args) {TreeSet set = new TreeSet();// set.add(123);//不能添加不同类的对象,//set.add("ABC"); 进行排序的时候会报错,出现ClassCastException/*set.add(456);set.add(789);set.add(452);*///结果按照从小到大顺序排列的,默认为从小到大排序// System.out.println(set);
//set默认排序按照equals,但是treeset就是按照comparable去比较set.add(new User("ABC",25));set.add(new User("A",21));set.add(new User("A",28));set.add(new User("AB",26));//会出现ClassCastException,没有提示应该按照什么排。//首先我们进行自然排序,即我们在该类实现Comparable接口。Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}@Testpublic void test1(){Comparator com = new Comparator() {//按照年龄从小到大排列@Overridepublic 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("ABC",25));set.add(new User("A",21));set.add(new User("A",28));set.add(new User("AB",26));set.add(new User("AB1",26));Iterator iterator = set.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}}class User implements Comparable{private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}
//我们写出来该方法则不会再继续报错。
//按照姓名从大到小排列,年龄从小到大排列@Overridepublic 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("输入的类型不匹配");}}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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;if (age != user.age) return false;return Objects.equals(name, user.name);}@Overridepublic int hashCode() {int result = name != null ? name.hashCode() : 0;result = 31 * result + age;return result;}
}
运行之后如下所示:
我们的练习如下所示: