黑马程序员——34,TreeSet与泛型

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

黑马程序员——34,TreeSet与泛型

旧博客整理归纳:

一:TreeSet----》

        TreeSet集合的底层数据结构是二叉树结构,元素进入容器后会自动被排序,不可以存放重复元素,如果有重复元素,那么该重复元素会被剔除。

如果TreeSet集合内部存放的元素是字符串,那么内部排序的时候,会按照自然顺序把字符串逐位比较,然后排序,常见的有效顺序是:数字>大写字母>小写字母

TreeSet排序的原理是二叉树排序:(底层数据结构二叉树结构),所谓的二叉树,例如:就是第一个元素进去,第二个元素进去判断若是比第一个元素大则放在右下边,(返回正数)

         若是比第一个元素小则是放在左下边(返回负数)若是相同元素(返回0)则是被剔除掉。如此类推下去就会形成一个叉型树。

         不过,如果元素个数过多的话,进来的元素会挑二叉树中的某一个元素做比较:

 1,若是比那某个元素大则放在那某个元素的右下边,若是相等则是被剔除,

 2,若是比那某个元素小泽放在左下边,

3,但是如果比那某个元素右下边的元素都大就跳到那某个元素的上一层继续比较,就这样子,一步步确定自己在二叉树中的位置。

        也可以利用这个原理,让compareTo方法返回值固定为正数或者负数,使得添加的元素按照添加顺序排序了(正序或者倒序)。

         最后逐个输出的时候也是从最左边的开始按照二叉树顺序输出。

        当存放的对象是自定义类的实例的时候,怎么办?

        这个时候就需要用到Comparable接口,用例子演示:

import java.util.*;//导入相关包
class  Student      implements   Comparable//Comparable接口强制Student具备了比较性
 
{
          private  String  name  ;
          private    int  age;
         Student(String  name,int age)
         {
               this.name=name;
                this.age=age;
 
         }
         public    int  compareTo(Object  obj)  //覆盖Comparable类中的compareTo方法        
         {
                 
              if( ! ( obj  instanceof Student  ) )
                    {
                         thrownew  RuntimeException("不是学生");         
                    }
                   Student  a=(Student)obj;//记得要有类型转换
                    System.out.println("这是"+this.name+"和"+a.name+"比较");
                    if(this.age>a.age    )
                    {
                         return 1;  
                    }
                   if(this.age==a.age)
                    {
                             //主要条件相同要判断次要条件
 
                         return  this.name.compareTo(a.name);
                              //String类也实现了Comparable接口,也有compareTo方法
                              //如果这里直接写return  0;的话就表示了对象是相同的
                     }
 
                        return  -1;
         }
   public   String  getName()//获取名字的方法
         {
                 return  name;   
         }
   public   int  getAge()//获取年龄的方法
         {
                 return  age;   
         }
 
 
}
class   Xjhe4
{
         public  static void  main(String[] args)//主函数
         {
                  TreeSet   a=  new TreeSet();
                  a.add(new  Student("haha01",12));
                  a.add(new  Student("haha02",19));
                  a.add(new   Student("haha03",26));
                  a.add(new  Student("haha04",15));
                  a.add(new  Student("haha05",1));
                  a.add(new  Student("haha06",16));
                  a.add(new  Student("haha07",76));
                  a.add(new  Student("haha08",17));
                  a.add(new  Student("haha09",17));
                  a.add(new  Student("haha06",16));
                 //TreeSet与一个Comparable接口相关,该接口强制对实现它的每一个类的对象排序
          /*    Comparable接口之所以有这种强制排序能力,因为该接口有一个用来比较的compareTo方法。
                所以这对于TreeSet存储自定义对象时候就可以利用实现Comparable接口,并且复写compareTo方法
            */
                  Iterator   it= a.iterator();
                  while(it.hasNext())//遍历元素
                   {
                       Student  stu=(Student)it.next();//注意类型转换 
                       sop("名字---"+stu.getName()+"---年龄---"+stu.getAge());
                   }
         }
 
         public   static void  sop(Object  obj)
         {
               System.out.println(obj);
         }
}
 
/*
以上代码编译运行结果:
这是haha01和haha01比较
这是haha02和haha01比较
这是haha03和haha01比较
这是haha03和haha02比较
这是haha04和haha02比较
这是haha04和haha01比较
这是haha05和haha02比较
这是haha05和haha01比较
这是haha06和haha02比较
这是haha06和haha01比较
这是haha06和haha04比较
这是haha07和haha02比较
这是haha07和haha03比较
这是haha08和haha02比较
这是haha08和haha01比较
这是haha08和haha04比较
这是haha08和haha06比较
这是haha09和haha02比较
这是haha09和haha01比较
这是haha09和haha06比较
这是haha09和haha08比较
这是haha06和haha06比较
名字---haha05---年龄---1
名字---haha01---年龄---12
名字---haha04---年龄---15
名字---haha06---年龄---16
名字---haha08---年龄---17
名字---haha09---年龄---17
名字---haha02---年龄---19
名字---haha03---年龄---26
名字---haha07---年龄---76
*/    
 

        但是,有时候元素本身不具备比较性或者具备的比较性不适合。例如,学生个体具备年龄的比较性,可是实际却需要按照名字排序,这种时候要用到Comparator接口。该接口中有一个compare方法。

        如果一个类实现了Comparator类,那么该类就是一个比较器类,  该类的实例对象就是一个比较器。我们在写自定义类的时候可以另外写一个类实现这个Comparator接口 ,并且覆盖compare方法,该类就是一个比较器类,新建一个该类的对象,该对象就是一个可用的比较器。

        比较器可以放进TreeSet集合的构造函数内,无论进入TreeSet集合的元素是否具有比较性,都只会按照比较器的比较规则来做元素比较确定元素排序位。

import java.util.*;
//Collection接口的体系放在Java.util包里面
class  Student      implements   Comparable//Comparable接口强制学生类的实例个体具备了比较性
 
{
          private  String name  ;
          private    int  age;
         Student(String  name,int age)
         {
               this.name=name;
                     this.age=age;
 
         }
         public    int  compareTo(Object  obj)
         {
                 
              if( ! ( obj  instanceof Student  ) )
                    {
                         thrownew  RuntimeException("不是学生");         
                    }
                   Student  a=(Student)obj;
                    System.out.println("这是"+this.name+"和"+a.name+"比较");
             if(this.age>a.age    )
                    {
                         return 1;  
                    }
                   if(this.age==a.age)
                    {
                             //主要条件相同要判断次要条件
 
                         return this.name.compareTo(a.name);
                     }
 
                        return  -1;
 
         }
   public   String  getName()//获取名字
         {
                 return  name;   
         }
   public   int  getAge()//获取年龄
         {
                return   age;   
         }
 
 
}
class  Jihe15
{
         Public  static   void  main(String[]args)
         {
                 TreeSet   a= new  TreeSet(new   Bijiao());//把比较器放进TreeSet构造函数中
                  a.add(new   Student("vdsfb1",12));
                  a.add(new  Student("惹我02",19));
                  a.add(new   Student("吧03",26));
                  a.add(new  Student("haha04",15));
                  a.add(new Student("qwqwe5",1));
                  a.add(new  Student("ahs6",16));
                  a.add(new  Student("ibbf07",76));
                  a.add(new  Student("cuhn8",17));
                  a.add(new Student("kvjakn09",17));
                  a.add(new  Student("kvjakn09",16));
 
                   Iterator   it= a.iterator();
                  while(it.hasNext() )//遍历元素
                   {
                       Student stu=(Student)it.next()    ; 
                       soc("名字---"+stu.getName()+"---年龄---"+stu.getAge());
                   }
         }
 
         public   static void  soc(Object  obj)
         {
               System.out.println(obj);
         }
}
class   Bijiao  /*比较器类*/ implements  Comparator
{
           public  int  compare(Object  a,Object b)
           {
                 Student   na=(Student)a;  
                 Student  nb=(Student)b;
                 int num=na.getName().compareTo(nb.getName());
                 //这里的compareTo方法是字符串本身就具备的方法,按照字典排列字符串
                           if(num==0)
                            {
                              return  na.compareTo(nb);   //按照元素本身具备的比较性进行比较  
                            }
                              return  num;
           }    
}
/*
以上代码编译运行结果:
这是vdsfb1和vdsfb1比较
这是kvjakn09和kvjakn09比较
名字---ahs6---年龄---16
名字---cuhn8---年龄---17
名字---haha04---年龄---15
名字---ibbf07---年龄---76
名字---kvjakn09---年龄---16
名字---kvjakn09---年龄---17
名字---qwqwe5---年龄---1
名字---vdsfb1---年龄---12
名字---吧03---年龄---26
名字---惹我02---年龄---19
 
*/

        另外一道题目:按照字符串长度排序,因为字符串自己已经有了自然比较性,所以用比较器来比较。这种题目值得注意的就是比较器类的编写。      

  

class Cdbijiao   implements  Comparator   //建立比较类
{
     public int  compare(Object  a,Object b)
          {
                    //记得类型转换
                 String na=(String)a;
                            String  nb=(String)b;
                                                                
                           if(na.length()==nb.length())//这里是先考虑了长度相同的特殊情况
                                     return   na.compareTo(nb);//主要条件相同就按照次要条件排序
 
                           if(na.length()>nb.length())
                            {
                                 return  1;   
                            }
 
                            return  -1;
 
                      }
 
 
}
                               

         我们可以从结果观察到在比较器中当a大于b时返回1,相同时比较次要条件,否则返回-1,现在得到的排序是从短的排到长的,那么我们可以把1和-1的位置调换,就得到从长到短的排序。但是在代码内部修改很麻烦,所以在此又做了一些改进。

  

 import  java.util.*;//导入相关包
class Xjhe5
{
        public  static void main(String[]args)
        {
                   TreeSet   a=new TreeSet(new  Cdbijiao(-12));
                   a.add("cyufihjv");
                   a.add("dzsrzx");
                   a.add("ihu");
                   a.add("erasdf");
                   a.add("vyivlbjkbui");
                   a.add("tydfy");
                   a.add("pjokn");
                   a.add("cryxtsbrnr");
 
                   Iterator   it=a.iterator();
                   while(it.hasNext())
                   {
                        soc(it.next()); //逐个打印元素  
                   }
 
                  System.out.println("HelloWorld!");
        }
        public  static   void  soc(Object  obj)//打印方法
        {
             System.out.println(obj);           
                          
        }
 
}
class   Cdbijiao   implements  Comparator   //建立比较类
{
         private    int  shuzi=0;
         Cdbijiao(int   shuzi)
         {
              this.shuzi=shuzi;   //由用户设定                  
         }
         public int  compare(Object  a,Object b)
          {
                    //记得类型转换
                 String na=(String)a;
                            String  nb=(String)b;
                                                                
                           if(na.length()==nb.length())//这里是先考虑了特殊情况
                           return   na.compareTo(nb);//主要条件相同就按照次要条件排序
 
                           if(na.length()>nb.length())
                            {
                                 return  shuzi;   
                            }
 
                            return -1*shuzi;//取相反数
                                                                 /*
                                   shuzi为正数,结果就是从短到长排序,shuzi为负数就是从长到短排序。
                            */  
                      }
 
 
}

 

二:泛型----》

        泛型:jdk1.5版本之后出现的特性,一种安全机制。把运行时出现问题ClassCastException转到编译时期:

        例如原本是这样定义的:ArrayList    a=new  ArrayList();但是添加元素时候既添加了String型的元素,又添加了int型的基本数据,这种情况下编译不会弹出错误,但是运行时候就会出问题,在实际开发中开发出来的产品给客户使用就会出问题。

        泛型就是限定了可以操作数据的类型,以下有例题说明:

importjava.util.*;
//Collection接口的体系放在Java.util包里面
class  Student    
 
{
    private String  name  ;
          private    int  age;
         Student(String name,int age)
         {
                this.name=name;
                      this.age=age;
 
         }
 
   public  String   getName()
         {
                return   name;   
         }
   public  int   getAge()
         {
                 return  age;   
         }
 
 
}
class  Jihe17
{
         publicstatic void main(String[] args)
         {
       ArrayList<Student>   a= new  ArrayList<Student>();
                   /*
                   这里的<>就是一种泛型的应用,一种保护机制。
                   这里的例子表示的就是ArrayList类型的容器中只能够装Student类型的元素,这样使用泛型这种保护机制后,之前编译会出现的两行注意提示也没有了。                    但是,如果此时往容器a里面添加的不是Student类型的元素的时编译就会出问题
                   */
                   a.add(new   Student("haha01",12));
                   a.add(new  Student("haha02",19));
                   a.add(new   Student("haha03",26));
                   a.add(new   Student("haha04",15));
                   a.add(new  Student("haha05",1));
                   a.add(new   Student("haha06",16));
                   a.add(new   Student("haha07",76));
                   a.add(new   Student("haha08",17));
                   a.add(new  Student("haha09",17));
                   a.add(new   Student("haha06",16));
                   // a.add("haha06");//这句话编译会出问题
 
                   Iterator<Student>   it= a.iterator();
                   /*
                   表示返回迭代器里面的也是Student类型的元素,
                   那么到时候迭代器调用next方法时返回的就不是Object类型的元素,
                   而是Student类型的元素了。
                   */
                   while(it.hasNext()        )
                   {
                        //Student stu=(Student)it.next()    ; 
                        Student  stu=it.next()    ;
                        //这里就省去了类型转换的麻烦
                    soc("名字---"+stu.getName()+"---年龄---"+stu.getAge());
                   }
         }
 
         public  static  void  soc(Object obj)
         {
                System.out.println(obj);
         }
}
/*
以上代码编译运行结果:
名字---haha01---年龄---12
名字---haha02---年龄---19
名字---haha03---年龄---26
名字---haha04---年龄---15
名字---haha05---年龄---1
名字---haha06---年龄---16
名字---haha07---年龄---76
名字---haha08---年龄---17
名字---haha09---年龄---17
名字---haha06---年龄---16
 
*/

        另外还有一些关于泛型的知识点:更加确切的说,<>里面就是定义了需要操作的引用数据类型

        Comparator也可以使用泛型,例如前面的比较器类的泛型改写:

class   Bijiao  implements  Comparator<Student>
//Comparator<Student>强制指定比较器只可以比较Student类型的元素
//实际上也是Bijiao类实现了只能够接收Student类型参数作比较的Comparator接口
{
           public  int  compare(Student  a,Student b)
           {
                 //Student   na=(Student)a;  
                 //Student  nb=(Student)b;
                 //这里的类型转换就可以省略了
                 //其他内容…
           }
}

       Comparable接口也可以有泛型,但是,这里需要非常注意:并不是说用了泛型之后就可以省略类型转换了,如果是遇到hashCode和equals这些复写Object类的方法,接收过来的参数类型都是Object的,就要用到类型转换了,因为Object类中没有泛型。

        当需要操作的引用数据类型不确定时候就使用泛型类或者泛型方法。

泛型类:

class  Person<TTT>//这种类也被称之为泛型类,带着泛型的类
//表示接收由调用者定义的类型的参数
{
    private   TTT  a;
          public  void  seta(TTT  a )
          {
                 this.a=a;
          }
          public  TTT  geta()
          {
              return a;             
          }
}
class  Teacher   /*老师类*/
{
 
    private   String  name="张三";
            private  int  age=25;
         public   int getAge()
         {
             return  age;         
         }
         public   String getName()
         {
                returnname;      
         }
}
class  Student      /*学生类*/         
{
 
          private  String  name="李四";
             private   int  age=13;
         public   int getAge()
         {
             return  age;         
         }
         public   String getName()
         {
                returnname;      
         }
}
 
class  Jihe19
{
         public  static void  main(String[] args) //主函数
         {
         method();
                 
         }
         public   static  void  method()
         {
                   soc("下面是method----------------");
                   Person<Teacher>   a=new  Person<Teacher>();
                   //限制了接收的只能是Teacher类型的参数
                  a.seta(new  Teacher());
                  soc(a.geta().getName());
 
 
                   Person<Student>   b=new   Person<Student>();
                   //对于Person类的b来说,接收的只能够是Student类的参数
                   //与上面的没有冲突
                   b.seta(new  Student());
                   soc(b.geta().getName());          
         }
         public  static   void   soc(Object  obj)
         {
            System.out.println(obj);  
         }
}
/*
以上代码编译运行结果:
下面是method----------------
张三
李四
 
*/

泛型方法的应用:

class  Person   //注意此时的类名后面没有加泛型!!!
{
    //注意:泛型统一放在返回值类型前面。 
  public  <E> void     study(E e)//这个就是泛型方法
   //这里<E>的E只在这个方法内有效
 
   {
      System.out.println("Person类study---"+e);
   }
  public  <E> void     sleep(E e)//这个就是泛型方法
   // sleep方法的E和study方法的E不是同一个E!!!
   {
      System.out.println("Person类sleep---"+e);
   }
}
   class    Fxin2
{
         public  static void main(String[] args)
         {
                   soc("下面是menthod------");
                   Person   per=new Person();
                  per.study(new  Integer(25));
                  per.study(62);
                  per.study("hushu");
                  //此时study和sleep接收的参数各不干扰
                   per.sleep(new  Integer(27));
                  per.sleep(67);
                  per.sleep("人民");
                  per.sleep(new  int[]{12,30,5});
 
         }
 
         public  static   void   soc(Object  obj)//打印方法
         {
             System.out.println(obj);  
         }
}

        如果泛型类里面还有泛型方法,也会有一些小知识点需要注意的:

class   Student<E>  //注意这里加了<E>
{
   public    void  drink(E   e) //注意这里的void前面没有加<E>
   //drink方法中的E和 Student<E>的E是一样的
   {
      System.out.println("drink---"+e);      
   }
   public  <E>void     run(E e)//这个就是泛型方法,注意这里void前面加了<E>
   //run方法中的E和 Student<E>的E是不相同的!run方法中的E只在run内部有效
   {
      System.out.println("run---"+e);
   }
  public  <Q> void     eat(Q q)//这个就是泛型方法
   //这里的Q与Studnet<E>中的E是不一样的
   {
      System.out.println("eat---"+q);
   }
 
   /*特殊例子:
  public  static void    fanfa3(E   e)
   这里的E与Studnet<E>中的E是一样的,由于这里是静态方法,在对象建立之前就存在的,但是调用该方法需要建立对象时E对应的明确类型,
   所以这种写法编译出问题。为了解决这个问题,可以把泛型就定义在方法上
   */
   public  static <R>void     fanfa3(R r)//这个就是泛型方法
         //这种写法才是正确的,而且泛型要放在返回值类型前!
         //public <R>static  void     fanfa3(R r)//这种写法是错误的
   {
       System.out.println("这是fanfa3的r---"+r);
   }
}
class  Jihe20
{
         public  static void main(String[] args)
         {
                   method2();
 
         }
         public  static void method2()
         {
                   soc("下面是menthod2------");
                   Student<String>   stu=newStudent<String>();//只能够接收String类型
 
                   stu.drink("hfabf");//drink方法只能够接收String类型的参数
                   //stu.drink(45);//这句话编译出问题
                   //stu.drink(new  Integer(25));//这句话编译出问题
 
                   //以下都可以编译运行成功
                   stu.run("hfabf");
                   stu.run(45);
                   stu.run(new Integer(25));
                  //以下都可以编译运行成功   
                   stu.eat(78);
                   stu.eat("rtct");
                   stu.fanfa3("hfabf");
 
         }
         public  static   void   soc(Object  obj)
         {
            System.out.println(obj);  
         }
}                  


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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值