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

集合框架--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学习型技术博客、期待与您交流! ------------

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值