您的位置:首页 > 职场人生

黑马程序员--07.集合框架--07.【TreeSet】【TreeSet底层的二叉树】

2013-07-25 17:56 330 查看

集合框架--7

      TreeSet概述 TreeSet底层的二叉树

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

1.    TreeSet概述

(1). Set常用子类的回顾

[1]. Set集合具有无序性唯一性

[2]. HashSet 底层数据结构是哈希表线程非同步

保证元素唯一性的办法就是调用元素的hashCode方法equals方法

[3]. TreeSet 底层数据结构是二叉树线程非同步

       【TreeSet的优势】可以对元素进行排序

       【TreeSet的无序性】由于TreeSet会对添加的元素进行排序,所以这一般会和添加元素的顺序不一致,这样就会导致从TreeSet中取出元素的顺序和存入时候的顺序不一致。所以说是无序的。

(2). TreeSet产生的一个异常



【产生异常的原因】

TreeSet能够进行排序。但是自定义的Person类并没有给出排序的规则。即普通的自定义类不具备排序的功能,所以要实现Comparable接口

(3). 让TreeSet中存储的元素具有比较性 ----实现Comparable接口

[1]. Comparable接口的原型

public
interface
Comparable<T>{
    public
int
compareTo(T o);
}
Comparable接口仅仅有一个方法需要实现,compareTo方法需要被子类实现。

[2]. Comparable接口的描述:

Compares this object with the specifiedobject for order. 

Returns a
negative integer, zero, or apositive integer as this object is less
than,equal to, or greater than the specified object
翻译下:因为这个对象(指的是实现Comparable接口的类的对象) 小于等于或者大于指定对象,所以,返回一个负数0或者一个正数

[注意]
compareTo方法返回0的时候,表示这两个对象相同。

依据Set接口的特点相同的对象不能出现一个Set集合中的,否则有悖于Set集合无序性。

【结论】当两个对象通过compareTo的比较返回0的时候,从compareTo方法参数接受的对象不能被存入TreeSet集合。

[2]. 示例代码:

import java.util.*;
class Student implements Comparable
{
private String name;
private int age;

public Student(String name, int age){
this.name =name;
this.age =age;
}

public String getName(){
return this.name;
}

public void setName(String name){
this.name =name;
}

public int getAge(){
return this.age;
}

public void setAge(int age){
this.age = age;
}

public String toString(){
return this.name+"::::"+ this.age;
}

public int compareTo(Object o){
if(!(o instanceof Student))
throw new RuntimeException("This is not a instance of Class \"Student\" ");

Students =(Student)o;
sop(this.name +"......compareTo...."+ s.name);
if(this.age > s.age)
return 1;
else if(this.age ==s.age)
return 0;
return -1;
}

public static void sop(Object o){
System.out.println(o);
}
}

class TreeSetDemo
{
public static void sop(Object o){
System.out.println(o);
}

public static void main(String[] args){
TreeSetts =new TreeSet();
ts.add(new Student("lisi02", 22));
ts.add(new Student("lisi007", 20));
ts.add(new Student("lisi09", 19));

Iteratorit =ts.iterator();
while(it.hasNext()){
sop(it.next());
}
}
}
打印结果:



除了第一个元素添加进来的时候,没有调用compareTo()方法,其余的元素添加进来的时候,TreeSet都调用了这些元素的compareTo方法。

输出的时候,这些元素都被按照年龄来排序输出

(4). 细化排序的条件

[1]. 现在将数据变化一下,多加入一个元素:

ts.add(new Student("lisi02", 22));
ts.add(new Student("lisi007", 20));
ts.add(new Student("lisi09", 19));
ts.add(new Student("lisi08", 19));
也就是添加了两个年龄一样的Student对象。



【产生的问题】发现lisi08在被TreeSet添加的时候,确实调用了这个元素的compareTo方法。但是最后TreeSet集合中却没有加入这个元素。

【产生的原因】因为当;lisi08......compareTo....lisi09打印的时候,说明这两个元素进行了比较。但是,比较的时候,两者的年龄均为19,所以else
if
(this.age ==s.age)这个分支执行,返回0。
返回0就意味着TreeSet判定这两个对象是一样的,就不能被存入集合中。
[2]. 细化判断条件

【要点】当主要条件相同的时候,一定要判断次要条件。

这里面,只有当Student类的年龄和姓名都一样的时候,才应该被视为是同一个对象。所以当年龄相同的时候,应该继续判断这两个对象的姓名是否相同。

修改代码示例:

public int compareTo(Object o){
if(!(o instanceof Student))
throw new RuntimeException("This is not a instance of Class \"Student\" ");

Students =(Student)o;
sop(this.name +"......compareTo...."+ s.name);
if(this.age > s.age)
return 1;
else if(this.age ==s.age)
return this.name.compareTo(s.getName());
return -1;
}
打印结果:



可以看到年龄相同但是姓名不同的lisi08和lisi09都被成功地添加到TreeSet集合中来。

2.    TreeSet底层的二叉树

1). TreeSet的二叉树存储元素的过程

(1). 示例代码

TreeSet ts =new TreeSet();
ts.add(new Student("lisi02", 22));
ts.add(new Student("lisi007", 20));
ts.add(new Student("lisi09", 19));
ts.add(new Student("lisi08", 19));
ts.add(new Student("lisi11", 40));
ts.add(new Student("lisi16", 30));
ts.add(new Student("lisi12", 36));
ts.add(new Student("lisi10", 29));
ts.add(new Student("lisi22", 90));
(2). 存储过程分析

[1]. 先存入一个元素22。

[2]. 20进来时,调用自身的compareTo方法,要和22比较。发现20 <22,compareTo返回负数,此时存入二叉树的左边。如图:



[3]. 19进来,先和22比较一下。19 <22,返回负数。此时19往22的左边走。22的左边还有一个20,再次比较一下:19 < 20, 19也放在20的左边。如图:



[4]. 有一个lisi08—19的元素要加入进来。分别和22, 20比较之后,compareTo方法都返回负数。这个元素要放在22, 20的左边。当lisi08—19和已存在的lisi09—19比较的时候,TreeSet发现两者的年龄相同,又一次调用return
this
.name.compareTo(s.getName()); 结果lisi08—19所在的compareTo返回负数(字符串lisi08 < lisi09)。则此时lisi08—19仍然放到lisi09—19的左边。如图:



[5]. "lisi11", 40进来。和22比较之后,40 > 22,  compareTo返回正数。放在22的右边。【这样减少了很多次的比较】。如图:



 

[6].30 >22--->放在22的右边   30 <40--->放在40的左边。如图:



[7].36 >22--->放在22的右边   36 <40--->放在40的左边  36 > 30--->放在30的右边。如图:



[8].29 >22--->放在22的右边   29 <40--->放在40的左边  29 < 30--->放在30的左边。如图:



[9].90 >22--->放在22的右边   90 > 40--->放在40的右边。如图:



打印结果:【TreeSet内部存在了优化, 将lisi007---20作为了根节点】

【二叉树的优势】减少了比较的次数。



(3). 取出过程分析



[1]. 根元素的左叉的数 <右叉的数  先遍历左边

[2]. 再遍历右边

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐