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