TreeSet与HashSet
由于内部数据结构不同,判断元素重复的条件也不同,
HashSet是判断元素的hashCode值是否相同,如果相同,还会继续判断元素的equals是否为真。
TreeSet
我们用如下的例子体现:
TreeSet<String>ts = new TreeSet<String>();
ts.add("aaa");
ts.add("ddd");
ts.add("eee");
ts.add("bbb");
for(Strings:ts)
{
System.out.println(s);
}
结果:
aaa
bbb
ddd
eee
说明TreeSet可以按一定顺序(自然的顺序)进行排序。这也是它最大的特点。
之后存一些自定义的对象。
classStudent
{
privateStringname;
privateintage;
publicStudent(String name,int age) {
super();
this.name =name;
this.age =age;
}
publicString getName() {
returnname;
}
publicvoidsetName(String name) {
this.name =name;
}
publicintgetAge() {
returnage;
}
publicvoidsetAge(int age) {
this.age =age;
}
}
TreeSet<Student>ts = new TreeSet<Student>();
ts.add(newStudent("001",1));
ts.add(newStudent("003",2));
ts.add(newStudent("002",3));
ts.add(newStudent("004",4));
运行抛出异常。
原因是这个对象不具备比较性。
。。。。
TreeSet要求存入的元素具有比较性,怎么才能有比较性?
要 实现一个comparable接口。并实现其中的方法。就具有了比较性。
把相应的方法加入到student中
publicintcompareTo(Object o)
{
Student s = (Student)o;
System.out.println(this.name+"compareTo"+s.name);
returnthis.age-s.age;
}
publicString toString()
{
returnthis.name+"..."+this.age;
}
再一次的调用
TreeSet<Student>ts = new TreeSet<Student>();
ts.add(newStudent("001",1));
ts.add(newStudent("003",2));
ts.add(newStudent("002",3));
ts.add(newStudent("004",4));
System.out.println(ts);
输出
003compareTo001
002compareTo001
002compareTo003
004compareTo003
004compareTo002
[001...1,003...2, 002...3, 004...4]
则说明正好是treeset底层调用了比较函数去进行了一系列的比较。
并最终排好了序(根据compareTo中特殊的比较方式排序)。。
但是。当出现
ts.add(newStudent("009",4));
时候,由于年龄一致
所以输出
[001...1,003...2, 002...3, 004...4]
009没有被存入。
所以必须要判断一下姓名。
publicintcompareTo(Object o)
{
Student s = (Student)o;
System.out.println(this.name+"compareTo"+s.name);
if(this.age==s.age)
return this.name.compareTo(s.name);
returnthis.age-s.age;
}
这样,只有年龄和姓名一致后,才不被存入集合中,当年龄不一样时候,用年龄排序
当年龄一致。则使用姓名进行排序。
则输出
[001...1,003...2, 002...3, 004...4, 009...4]
所以要记住,在对自定义的对象排序时候,先对主要条件进行排序后,再对次要条件进行排序。
下面,我们来看一看treeSet的内部实现机制。
底层使用了一个特殊的数据结构。
就使用上边的例子
上述图的得出分析
4存入,
2进来,与4进行比较,小于4,放在左边。
3进来,与4比较,在左,与2比较,大于2,故放在2的右边
1进来,与4比较,在左,与2比较,小于2,故放在2的左边。
也就是底层实现了一个二叉树。从而避免了加入一个元素的时候和集合中的每个元素
进行比较。提高了比较 性能。
那么如何进行取元素呢?
按照定义的比较顺序去取,比如从小到大。
那么先去左子树的最左边的值。往上依次走。
有一个问题,想要是元素按原来的排序逆序输出呢?千万不要用
returnthis.age-s.age;置换这里的this和s
而是用java中有其他的特殊的方式去实现。
对于TreeSet来说,它只用compareTo去看是负数,0,正数,而与其他方法无关。
始终使用默认的比较的(小----->大)去存储和输出数据。
也就是无论使用什么比较方式,都是按其比较的最小------》最大去对元素进行排序。
也就是从二叉树的最左端到二叉树的最右端进行比较。
综上所述:
TreeSet可以对set集合中的元素进行排序。底层的数据结构是二叉树。保证元素唯一性的依据叫做compareTo()方法return 0;与hashcode没有关系。与equals也没有关系。TreeSet排序的第一种方式,让元素自身具备了比较性。元素需要实现comparable接口覆盖compareTo方法。这种方式也称为元素的自然顺序,也叫做默认顺序。
这时,我们就考虑一个问题,当元素真的不具备比较性的时候,应该怎么办?
也就是不能实现comparable接口和其中的compareTo();
还有,你实现的比较方式和现在的需求不符合了,去强制改变类中的方法也是不现实的。
这就有了TreeSet的第二种排序方式。
这时就需要让集合自身具备比较性。
让容器去决定元素哪个大或小,也就是两个人去找集合,让集合去比较二个人,而不是两个人相互的去比较。
也就是在集合一初始化的时候,就有了比较的方式。
TreeSet
(Comparator<? superE> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。
就用到了这个构造函数。
比方说,需求是按照姓名排序,而上边是按照年龄优先排序
classMyCompimplementsComparator
{
publicintcompare(Object o1,Object o2)
{
Student s1 = (Student)o1;
Student s2 = (Student)o2;
returns1.getName().compareTo(s2.getName());
}
}
就使用上边的方式去创建一个本身具备比较的集合。
当集合自身具备比较性质和对象也具有比较性质的时候,优先使用集合自身的比较方法。也就是以比较器为主。
比较器比较常用一些。
以return 0 来确定元素是否存在。