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

来源:互联网 发布:怎么搭建linux测试环境 编辑:程序博客网 时间:2024/05/29 11:12

集合框架--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,所以elseif(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发现两者的年龄相同,又一次调用returnthis.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学习型技术博客、期待与您交流! ------------

 

原创粉丝点击