Java集合之TreeSet
1、TreeSet简介
从名字上可以看出,此集合的实现和树结构有关。与HashSet集合类似,TreeSet也是基于Map来实现,其底层结构为红黑树(特殊的二叉查找树);与HashSet不同的是,TreeSet具有排序功能,分为自然排序(123456)和自定义排序两类,默认是自然排序;在程序中,我们可以按照任意顺序将元素插入到集合中,等到遍历时TreeSet会按照一定顺序输出:倒序或者升序;
TreeSet继承AbstractSet,实现NavigableSet, Cloneable, Serializable接口。
- 与HashSet同理,TreeSet继承AbstractSet类,获得了Set集合基础实现操作;
- TreeSet实现NavigableSet接口,而NavigableSet又扩展了SortedSet接口。这两个接口主要定义了搜索元素的能力,例如给定某个元素,查找该集合中比给定元素大于、小于、等于的元素集合,或者比给定元素大于、小于、等于的元素个数;简单地说,实现NavigableSet接口使得TreeSet具备了元素搜索功能;
- TreeSet实现Cloneable接口,意味着它也可以被克隆;
- TreeSet实现了Serializable接口,可以被序列化,可以使用hessian协议来传输;
public class TestTreeSet {
public static void main(String[] agrs){
TreeSet treeSet = new TreeSet();
System.out.println("TreeSet初始化容量大小:"+treeSet.size());
//元素添加:
treeSet.add("hello");
treeSet.add("!");
treeSet.add("a");
treeSet.add("new");
treeSet.add("world");
System.out.println("TreeSet容量大小:" + treeSet.size());
System.out.println("TreeSet元素顺序为:" + treeSet.toString());
//增加for循环遍历:
for(String str:treeSet){
System.out.println("遍历元素:"+str);
}
//迭代器遍历:升序
Iterator iteratorAesc = treeSet.iterator();
while(iteratorAesc.hasNext()){
String str = iteratorAesc.next();
System.out.println("遍历元素升序:"+str);
}
//迭代器遍历:降序
Iterator iteratorDesc = treeSet.descendingIterator();
while(iteratorDesc.hasNext()){
String str = iteratorDesc.next();
System.out.println("遍历元素降序:"+str);
}
//元素获取:实现NavigableSet接口
String firstEle = treeSet.first();//获取TreeSet头节点:
System.out.println("TreeSet头节点为:" + firstEle);
// 获取指定元素之前的所有元素集合:(不包含指定元素)
SortedSet headSet = treeSet.headSet("new");
System.out.println("new节点之前的元素为:"+headSet.toString());
//获取给定元素之间的集合:(包含头,不包含尾)
SortedSet subSet = treeSet.subSet("hello", "new");
System.out.println("!--world之间节点元素为:"+subSet.toString());
//集合判断:
boolean isEmpty = treeSet.isEmpty();
System.out.println("TreeSet是否为空:"+isEmpty);
boolean isContain = treeSet.contains("who");
System.out.println("TreeSet是否包含who元素:"+isContain);
//元素删除:
boolean jiaboyanRemove = treeSet.remove("world");
System.out.println("jiaboyan元素是否被删除"+jiaboyanRemove);
//集合中不存在的元素,删除返回false
boolean whoRemove = treeSet.remove("who");
System.out.println("who元素是否被删除"+whoRemove);
//删除并返回第一个元素:如果set集合不存在元素,则返回null
String pollFirst = treeSet.pollFirst();
System.out.println("删除的第一个元素:"+pollFirst);
//删除并返回最后一个元素:如果set集合不存在元素,则返回null
String pollLast = treeSet.pollLast();
System.out.println("删除的最后一个元素:"+pollLast);
treeSet.clear();//清空集合:
}
}
输出:
TreeSet初始化容量大小:0
TreeSet容量大小:5
TreeSet元素顺序为:[a, hello, new, world, !]
遍历元素:a
遍历元素:hello
遍历元素:new
遍历元素:world
遍历元素:!
遍历元素升序:a
遍历元素升序:hello
遍历元素升序:new
遍历元素升序:world
遍历元素升序:!
遍历元素降序:!
遍历元素降序:world
遍历元素降序:new
遍历元素降序:hello
遍历元素降序:a
TreeSet头节点为:a
new节点之前的元素为:[a, hello]
!--world之间节点元素为:[hello]
TreeSet是否为空:false
TreeSet是否包含who元素:false
jiaboyan元素是否被删除true
who元素是否被删除false
删除的第一个元素:a
删除的最后一个元素:!
3、TreeSet元素排序
在前面的章节,我们讲到了TreeSet是一个有序集合,可以对集合元素排序,其中分为自然排序和自定义排序,那么这两种方式如何实现呢?
首先,我们通过JDK提供的对象来展示,我们使用String、Integer:
public class TreeSetTest {
public static void main(String[] agrs){
naturalSort();
}
//自然排序顺序:升序
public static void naturalSort(){
TreeSet treeSetString = new TreeSet();
treeSetString.add("a");
treeSetString.add("z");
treeSetString.add("d");
treeSetString.add("b");
System.out.println("字母顺序:" + treeSetString.toString());
TreeSet treeSetInteger = new TreeSet();
treeSetInteger.add(1);
treeSetInteger.add(24);
treeSetInteger.add(23);
treeSetInteger.add(6);
System.out.println(treeSetInteger.toString());
System.out.println("数字顺序:" + treeSetString.toString());
}
}
输出:
字母顺序:[a, b, d, z]
数字顺序:[1, 6, 23, 24]
接下来,我们自定义对象,看能否实现:
class Location {
private String name;
private Integer ID;
public Location(String name, Integer ID) {
this.name = name;
this.ID = ID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getID() {
return ID;
}
public void setID(Integer ID) {
this.ID = ID;
}
}
public class TestTreeSet1 {
public static void main(String[] agrs){
customSort();
}
//自定义排序顺序:升序
public static void customSort(){
TreeSet treeSet = new TreeSet<>();
//排序对象:
Location location1 = new Location("a" , 1);
Location location2 = new Location("new" , 2);
Location location3 = new Location("world" , 3);
//添加到集合:
treeSet.add(location1);
treeSet.add(location2);
treeSet.add(location3);
System.out.println("TreeSet集合顺序为:"+treeSet);
}
}
输出报错:
Exception in thread "main" java.lang.ClassCastException: java4interview.Location cannot be cast to java.lang.Comparable
通过查看源码发现,在TreeSet调用add方法时,会调用到底层TreeMap的put方法,在put方法中会调用到compare(key, key)方法,进行key大小的比较;在比较的时候,会将传入的key进行类型强转,所以当我们自定义的App类进行比较的时候,自然就会抛出异常,因为App类并没有实现Comparable接口;
将App实现Comparable接口,在做比较:
class Location implements Comparable{ private String name; private Integer ID; public Location(String name, Integer ID) { this.name = name; this.ID = ID; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getID() { return ID; } public void setID(Integer ID) { this.ID = ID; } @Override public int compareTo(Location location) { int num = this.name.length() - location.name.length(); return num == 0 ? this.ID - location.ID :num; } @Override public String toString() { return "Location{" + "name='" + name + '\'' + ", ID=" + ID + '}'; } } public class TestTreeSet1 { public static void main(String[] agrs){ customSort(); } //自定义排序顺序:升序 public static void customSort(){ TreeSet treeSet = new TreeSet<>(); //排序对象: Location location1 = new Location("a" , 10); Location location2 = new Location("new" , 4); Location location3 = new Location("world" , 3); Location location4 = new Location("hello" , 5); //添加到集合: treeSet.add(location1); treeSet.add(location2); treeSet.add(location3); treeSet.add(location4); System.out.println(treeSet); } } 输出: [Location{name='a', ID=10}, Location{name='new', ID=4}, Location{name='world', ID=3}, Location{name='hello', ID=5}]
此外,还有另一种方式,那就是实现Comparetor
//自定义location类的比较器: public class locationComparator implements Comparator不用在数据类中实现Comparerable接口了,单纯的定义一个比较器类即可:{ public int compare(Location location1, Location location2) { int num = location1.getID() - location2.getID(); return num == 0 ? location1.getName().length() - location2.getName().length() : num; } }
//自定义location类的比较器: public class LocationComparator implements Comparator{ public int compare(Location location1, Location location2) { int num = location1.getID() - location2.getID(); return num == 0 ? location1.getName().length() - location2.getName().length() : num; } }
关于compareTo()、compare()方法:
- 结果返回大于0时,方法前面的值大于方法中的值;
- 结果返回等于0时,方法前面的值等于方法中的值;
- 结果返回小于0时,方法前面的值小于方法中的值;
public class TreeSet4.2、构造方法extends AbstractSet implements NavigableSet , Cloneable, java.io.Serializable { //TreeSet中保存元素的map对象: private transient NavigableMap m; //map对象中保存的value: private static final Object PRESENT = new Object(); }
public class TreeSet4.3、添加元素add 向TreeSet中添加元素:extends AbstractSet implements NavigableSet , Cloneable, java.io.Serializable { //最底层的构造方法,不对外。传入一个NavigableMap接口的实现类 TreeSet(NavigableMap m) { this.m = m; } //无参构造:向底层构造传入一个TreeMap对象: public TreeSet() { this(new TreeMap ()); } //传入比较器的构造:通常传入一个自定义Comparator的实现类; public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); } //将集合Collection传入TreeSet中: public TreeSet(Collection<? extends E> c) { this(); addAll(c); } //将集合SortedSet传入TreeSet中: public TreeSet(SortedSet s) { this(s.comparator()); addAll(s); } }
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
4.4、删除元素remove
删除TreeSet中元素o:
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
5、SortedSet和NavigableSet
在介绍TreeSet的时候都会提到NavigableSet,NavigableSet是个"导航Set集合",提供了一系列"导航"方法。什么是"导航"方法?通过接口的定义,可以看到NavigableSet继承了SortedSet接口,实现了对其的扩展;而通过下面的方法得出NavigableSet实际提供了一系列的搜索匹配元素的功能,能获取到某一区间内的集合元素 :
public interface NavigableSet说完了NavigableSet,再看父类SortedSet接口:通过名字,我们可以得出此接口跟排序有关,会提供跟排序的方法;extends SortedSet { E lower(E e);//返回此set集合中小于e元素的最大元素 E floor(E e);//返回此set集合中小于等于e元素的最大元素 E ceiling(E e);//返回此set集合中大于等于e元素的最小元素 E higher(E e);//返回此set集合中大于e元素的最小元素 E pollFirst(); //获取并移除此set集合中的第一个元素 E pollLast();//获取并移除此set集合中的最后一个元素 Iterator iterator();//返回此set集合的迭代器--升序 NavigableSet descendingSet();//以倒序的顺序返回此set集合 Iterator descendingIterator();//返回此set集合的迭代器--倒序 //返回此set集合的部分元素--从fromElement开始到toElement结束,其中fromInclusive、toInclusive意为返回的集合是否包含头尾元素 NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive); //返回此set集合的部分元素--小于toElement,inclusive意味返回的集合是否包含toElement NavigableSet headSet(E toElement, boolean inclusive); //返回此set集合的部分元素--从fromElement开始到toElement结束,包含头不含为尾 SortedSet subSet(E fromElement, E toElement); //返回此set集合的部分元素--小于toElement SortedSet headSet(E toElement); //返回此set集合的部分元素--大于等于toElement SortedSet tailSet(E fromElement); }
public interface SortedSetextends Set { //返回与排序有关的比较器 Comparator<? super E> comparator(); //返回从fromElement到toElement的元素集合: SortedSet subSet(E fromElement, E toElement); //返回从第一个元素到toElement元素的集合: SortedSet headSet(E toElement); //返回从fromElement开始到最后元素的集合: SortedSet tailSet(E fromElement); //返回集合中的第一个元素: E first(); //返回集合中的最后一个元素: E last(); }