一、【基本定义和特点】
TreeSet::这个集合就是为了实现该集合中元素是自然数排序,具有无序性,和不可重复性的特点!
我们来验证一下:
<span style="font-size:18px;"><span style="font-size:18px;">import java.util.*;
class TreeSetDemo1
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet();
ts.add("abcd");
ts.add("bcd");
ts.add("accd");
ts.add("bdd");
ts.add("bdd");
ts.add("Acd");
//迭代取出
Iterator i=ts.iterator();
while (i.hasNext())
{
sop(i.next());
}
/*执行的结果:
Acd
abcd
accd
bcd
bdd
可以看出存入的顺序和取出的顺序是无序性,ts.add("bdd")添加了两次,取出只有一个,
即是无重复性的反应,OK
*/
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}</span></span>
因为TreeSet集合是为了实现该集合中元素是自然数排序这时候应该想到在String类中也有个int compareTo(String str)的方法,
此方法是为了让两个字符串进行自然数排序,返回int类型:负数,正数,和0
二、【TreeSet集合中添加自定义的类】
不用问,肯定知道,都是实现了一个comparable的接口去调用自身的int compareTo(Object obj)
为什么会这样呢?
我们用练习说明:
自定一个学生类,把该学生类添加到TreeSet集合中后,通过年龄去排序出来定义基本的简单类吧:学生类中只有基本的name和age属性为例,最终通过年龄的自然数去排序出来
<span style="font-size:18px;"><span style="font-size:18px;">/*
其实:TreeSet的底层数据结构是二叉树
comparareTo()方法,返回值是0,是说明元素是相同的……直接删除,不再存入其中
【重点】当排序是,主要条件相同时,一定判断一下次要条件
*/</span></span>
<span style="font-size:18px;"><span style="font-size:18px;">import java.util.*;
//学生类
class Student implements Comparable //强制让学生类具备比较性
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//去重写接口中compareTo方法,因为这时是先按照年龄去排序的
public int compareTo(Object obj)
{
//第一步,按照年龄去排序
//第二步,如果年龄相同的情况,再具体判断学生类中的姓名属性去排序,
//这时候就交给原来String类中compareTo的方法去自动排序即可
//所以:
//判断添加的类是不是都是学生类,不是就用抛出异常的方式提醒!
if (!(obj instanceof Student))
throw new RuntimeException("添加的不是学生类对象");
//把添加的对象缩小到Student的具体实例话类上进行年龄的判断
Student stu=(Student)obj;
//按照具体需求去进行
if (this.age>stu.age)
return 1;
if (this.age==stu.age)
{
return this.name.compareTo(stu.name);
}
return -1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class TreeSetAddObject2
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet();
//把学生实例化对象加入其中
ts.add(new Student("wangming",28));
ts.add(new Student("wangming",20));
ts.add(new Student("wangming",20));
ts.add(new Student("zhijie",26));
ts.add(new Student("zhiainan",26));
ts.add(new Student("yanghua",23));
ts.add(new Student("wankez",30));
Iterator i=ts.iterator();
//迭代的方式取出去遍历就OK
while (i.hasNext())
{
Student stu=(Student)i.next();
//打印在Student对象在TreeSet集合内部的排序情况
sop(stu.getName()+","+stu.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}</span></span>
最终的总结:【默认排序】第一种方式:让元素自身具备比较性;元素需要实现Comparable接口,覆盖compareTo()方法,这种方式的排序也可以说是自然排序,或者是默认排序!
当加入自定的类在TreeSet中,要根据具体的需求去重写实现Comparable接口的方法
compareTo的方法,依次让TreeSet集合的内部去按照需求内容就行排序就行!
三、【比较器的定义】
<span style="font-size:18px;"><span style="font-size:18px;">/*
TreeSet的第二种排序方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的,
那样,就需要让集合自身具备比较性
这样的话,就在集合才在初始化时就具备比较性!
在api文档中查到想让集合自身具有比较性,那样就让要定义一个
【比较器】,将比较器对象作为参数传递给TreeSet集合的构造函数中
*/
import java.util.*;
//学生类
class Student implements Comparable //强制让学生类具备比较性
{
private String name;
private int age;
Student(String name,int age)
{
this.name=name;
this.age=age;
}
//去重写接口中compareTo方法,因为这时是先按照年龄去排序的
public int compareTo(Object obj)
{
//第一步,按照年龄去排序
//第二步,如果年龄相同的情况,再具体判断学生类中的姓名属性去排序,
//这时候就交给原来String类中compareTo的方法去自动排序即可
//所以:
//判断添加的类是不是都是学生类,不是就用抛出异常的方式提醒!
if (!(obj instanceof Student))
throw new RuntimeException("添加的不是学生类对象");
//把添加的对象缩小到Student的具体实例话类上进行年龄的判断
Student stu=(Student)obj;
//按照具体需求去进行
if (this.age>stu.age)
return 1;
if (this.age==stu.age)
{
return this.name.compareTo(stu.name);
}
return -1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class TreeSetAddObjectSecond3
{
public static void main(String[] args)
{
TreeSet ts=new TreeSet(new Mycomparator());
//把学生实例化对象加入其中
ts.add(new Student("wangming",28));
ts.add(new Student("wangming",28));
ts.add(new Student("wangming",20));
ts.add(new Student("zhijie",26));
ts.add(new Student("zhiainan",26));
ts.add(new Student("yanghua",23));
ts.add(new Student("wankez",30));
Iterator i=ts.iterator();
//迭代的方式取出去遍历就OK
while (i.hasNext())
{
Student stu=(Student)i.next();
//打印在Student对象在TreeSet集合内部的排序情况
sop(stu.getName()+","+stu.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//自定义比较器
class Mycomparator implements Comparator
{
public int compare(Object o1,Object o2)
{
Student s1=(Student)o1;
Student s2=(Student)o2;
if (!((s1 instanceof Student) && (s2 instanceof Student)))
throw new RuntimeException("注意:要比较的对象不是学生类对象");
//这时候,如果对象中两个同学的名字是一样的,那样就需要进一步判断年龄了
//所以,这时候就需要对年龄进一步做出判断才行!
/*
if (kid==0)
{
//对年龄进行判断:
if (s1.getAge() > s2.getAge())
{
return 1;
}
if (s1.getAge() == s2.getAge())
{
return 0;
}
return -1;
}
return kid;*/
//做完这根kid的判断之后,发现实际上有另外一种方法,那就是用基本数据类型类去判断更便捷
int kid=s1.getName().compareTo(s2.getName());
if (kid == 0)
{
return new Integer(s1.getAge()).compareTo(s2.getAge());
}
return kid;
}
}</span></span>
四、【泛型的应用】
<span style="font-size:18px;"><span style="font-size:18px;">/*
下面用泛型的思想来做按照字符串长度的排序
【泛型】定义:在JDK1.5之后:为了提高安全性,所以在定义集合的时候,就必须指定存入集合
中元素的类型
【格式】将要接受的类型写在“<>”中
【好处】
1.避免在运行的时候才出现ClassCastException(类型不能转换异常),提前在编译时就能进行提示
2.避免强制转换的问题出现,让程序看起来简单明了
*/
import java.util.*;
class GenericityDemo5
{
public static void main(String[] args)
{
//在定义集合时就要指定类型,类型写在“<>”中,这里我接受的是字符串类型
TreeSet<String> ts=new TreeSet<String>(new MyGenerComparator());
ts.add("sjfklasjfs");
ts.add("afadfa");
ts.add("fasfaewfa");
ts.add("asfadsfassfds");
ts.add("asfadsfas");
//在定义迭代器时,也必须标注才行,这样免得在
//while中还要强制转换类型,简化了代码
Iterator<String> i=ts.iterator();
while (i.hasNext())
{
//Stirng s=(String)i.next();因为有泛型,就不用这句话,不需要强制转换了
sop(i.next());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//一样的原理,在定义比较器时,实现的接口Comparator也要指定接受类型
class MyGenerComparator implements Comparator<String>
{
//所以当覆盖方法的时候,就直接用接受到的String类型就行
public int compare(String s1,String s2)
{
int num=(new Integer(s1.length())).compareTo (new Integer(s2.length()));
if (num==0)
{
return s1.compareTo(s2);
}
return num;
}
}</span></span>
泛型的具体事例分析:
<span style="font-size:18px;"><span style="font-size:18px;">/*
当不知道需要引用的数据类型是什么时,就需要自定义一个工具类来完成一个万能的自动转换
下面以学生和工人类为例:当不知道是接受谁时,这是就可以用泛型的思想来完成
*/
//定义工人类
class Worker
{
}
//定义学生类
class Student
{
}
//定义一个自己的工具,实现接受后,智能转换的过程
//SuperUtil是自己定义的需要接受的引用对象而已
class MyUtil<SuperUtil>
{
private SuperUtil su;
//set,get方法很简单,不再介绍了
public void setObject(SuperUtil su)
{
this.su=su;
}
public SuperUtil getObject()
{
return su;
}
}
class MyGenericity6
{
public static void main(String[] args)
{
//这时指定传入的是如果是Student的类,那么他会自动进行转换,很方便
//即便是出错了,也会在编译时报出错误!
MyUtil<Student> my=new MyUtil<Student>();
my.setObject(new Student());
//所以以下就不用进行手动强转了
Student s=my.getObject();
}
}
//下面是看一下,没有泛型之前的做法:
//用的都是Object的基础类接受的引用对象,接受之后,需要自己做对应的强转才能正常使用!
//定义工人类
/*
class Worker
{
}
//定义学生类
class Student
{
}
class MyUtil
{
private Object obj;
//set,get方法很简单
public void setObject(Object obj)
{
this.obj=obj;
}
public Object getObject()
{
return obj;
}
}
class MyGenericity
{
public static void main(String[] args)
{
MyUtil my=new MyUtil();
my.setObject(new Student());
//需要进行手动强转了,否则报错
Student s=(Student)my.getObject();
}
}
*/</span></span>
五、【泛型可以应用到类上或者方法上】
<span style="font-size:18px;"><span style="font-size:18px;">/*
【泛型应用在方法中的情况】
在开发的过程中,还有一种情况,那就是当一个类被确定是,但是其方法中的引用对象是不确定的,
这时候也可以用到泛型来定义,在同一个方法中操作不同的引用对象
*/
//实例:
class Demo
{
//定义一个在展示方法中去输出引用对象的简单方法
public <T> void showMethod(T t)
{
System.out.println(t);
}
//定义一个在打印方法中去输出引用对象的简单方法
public <Z> void printMethod(Z z)
{
System.out.println(z);
}
}
class MethodGenericity7
{
public static void main(String[] args)
{
//在类中不再指定要传入的引用对象
Demo d=new Demo();
d.showMethod("wangwang");
d.showMethod(new Double(89.767));
d.printMethod("nihaoma!");
d.printMethod(new Integer(99));
/*
输出结果:
wangwang
89.767
nihaoma!
99
//这种就是应用同一个方法也能输出不同引用对象的实例
*/
}
}</span></span>
请看实例:
<span style="font-size:18px;"><span style="font-size:18px;">/*
之前是把泛型应用到类上或者方法上的例子,同样,泛型能够同时应用到类上和方法上
但是需要注意的是:static的方法应用泛型格式上有点不一样!
下面以例子具体说明:
*/
class Demo<T>
{
//方法show定义的泛型和类上定义的是同一个引用对象,所以在void前面不用再写<T>了
public void show(T t)
{
System.out.println(t);
}
//这是定义了和类不一样的泛型
public <Z> void print(Z z)
{
System.out.println(z);
}
//同理:static方法是永远都不能定义和类一样的泛型引用对象的,即是不能访问类上的泛型对象
//当静态方法上访问的对象也不能确定时,定义在static方法就行!
public static <X> void staticShow(X x)
{
System.out.println(x);
}
}
class MethodGenerTest8
{
public static void main(String[] args)
{
Demo<String> d=new Demo<String>();
d.show("the same with Demo class");
//d.show(new Integer(99));//报错,因为该方法的泛型和类一样,是String类型
//虽然类中指定了相应的Stirng类型,但是print方法却是可以和类一样,也可以不一样的
d.print("the same with Demo class"); //可以跟类泛型一样
d.print(new Integer(99)); //可以和类泛型不一样
//静态方法的泛型实例
d.staticShow("static method");
d.staticShow(new Integer(99));
}
}
/*【总结:】
1.泛型可以定义在类上,方法上(静态的方法不能访问类定义的泛型)
2.多个方法中(除static方法外)的泛型可以和类泛型一样,也可以不一样!
*/</span></span>
六、【泛型应用到接口上的具体分析】
<span style="font-size:18px;"><span style="font-size:18px;">/*
那么泛型可以定义在接口上面吗?
看实例分析:
*/
//定义一个接口,接口中有一个抽象的方法
interface Inter<T>
{
void show(T t);
}
//实现该接口的类
class InterImpl<T> implements Inter<T>
{
//覆盖接口中的方法,应该保持泛型和接口一样
public void show(T t)
{
System.out.println(t);
}
//再定义一个和接口中的泛型不一样的方法
public <Z> void print(Z z)
{
System.out.println(z);
}
}
class InterfaceGeneriDemo9
{
public static void main(String[] args)
{
//指定该接口的实现类泛型是String类型
InterImpl<String> i=new InterImpl<String>();
i.show("wangwang");
//i.show(new Integer(99));//这样就是错误的,因为show方法定义的是和接口一样的类型
i.print(new Integer(99));
}
}
//【总结】泛型是可以用在接口上的</span></span>
七、【泛型的限定】
/*泛型的限定!
【限定的种类】
1.?这是一个通配符号,可以理解是占位符
2.? extends E(上限定):可以接受E类型,或者是E的子类型
3.? super E(下限定):可以接受E类型,或者E的父类型
*/
import java.util.*;
//以下第一个程序是自定义一个泛型的方法去迭代取出元素
/*class GeneriXianding10
{
public static void main(String[] args)
{
ArrayList<String> ss=new ArrayList<String>();
ss.add("wangalkjg");
ss.add("wasdf");
ss.add("wandf");
ss.add("wang");
ArrayList<Integer> si=new ArrayList<Integer>();
si.add(new Integer(89));
si.add(new Integer(9));
si.add(new Integer(5));
si.add(new Integer(2));
showCollection(ss);
showCollection(si);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
//因为都有通过迭代取出的方法,所以定义一个迭代方法的泛型实例就可以取出元素
public static void showCollection(ArrayList<?> al)
{
Iterator<?> i=al.iterator();
while (i.hasNext())
{
sop(i.next());
}
}
}*/
class Person
{
private String name;
Person(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
}
class Student extends Person
{
Student(String name)
{
super(name);
}
}
class GeneriXianding10
{
public static void main(String[] args)
{
ArrayList<Person> al=new ArrayList<Person>();
al.add(new Person("wangke"));
al.add(new Person("wan"));
al.add(new Person("wgke"));
//showCollection(al);
ArrayList<Student> als=new ArrayList<Student>();
als.add(new Student("wangke----1"));
als.add(new Student("wan----2"));
als.add(new Student("wgke----3"));
showCollection(als);
//这句话相当于:ArrayList<Person> al=new ArrayList<Student>();
//左边是人的总类,而在右边只是人的一个子类(一个实例而已),所以两边类型不匹配
//必然报错
}
public static void sop(Object obj)
{
System.out.println(obj);
}
//Person写为?也可以实现(所有类型的打印),但是我们需要只是打印Person或者Person的子类
//这时候就需要用到
//? extends Person(上限定) 或者 ? super Student(下限定)
//只需要把showCollection方法中的Person改为? extends Person就行
public static void showCollection(ArrayList<? extends Person> a)
{
Iterator<? extends Person> i=a.iterator();
while (i.hasNext())
{
sop(i.next().getName());
}
}
}
八、【泛型高级应用总结】
/*
写一个比较器的泛型实例,最后还是用通用的迭代方法显示出来:
*/
import java.util.*;
//定义了一个父类,人类
class Person
{
private String name;
Person(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
}
//定义了一个子类学生类
class Student extends Person
{
Student(String name)
{
super(name);
}
}
//定义了一个子类工人类
class Worker extends Person
{
Worker(String name)
{
super(name);
}
}
class BiJiaoQi11
{
public static void main(String[] args)
{
//把学生类添加到TreeSet集合中
TreeSet<Student> tss=new TreeSet<Student>(new MyComp());
tss.add(new Student("abc--1"));
tss.add(new Student("abc--2"));
tss.add(new Student("abc--3"));
tss.add(new Student("abc--4"));
//把工人类添加到TreeSet集合中
TreeSet<Worker> tsw=new TreeSet<Worker>(new MyComp());
tsw.add(new Worker("abc--001"));
tsw.add(new Worker("abc--002"));
tsw.add(new Worker("abc--003"));
tsw.add(new Worker("abc--004"));
//调用通用的迭代方式显示不同的类
showIter(tss);
showIter(tsw);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
//定义了一个通用的迭代取出函数
//取出的对象是Person的所有子类型
public static void showIter(TreeSet<? extends Person> ts)
{
Iterator<? extends Person> it=ts.iterator();
while (it.hasNext())
{
sop(it.next().getName());
}
}
}
/*这里很重要:
如果Person改写成Student的话,只能比较Student类
如果Person改写成Worder的话,只能比较Worder类
但是传入的是父类,就意味着可以比较父类的所有子类了
【温馨提示】传入的是子类,那就只比较相应的子类,传入的是父类,那就可以比较父类其下的所有子类
这就是其 ? super E (下限的具体体现)
*/
class MyComp implements Comparator<Person>
{
public int compare(Person p1,Person p2)
{
return p2.getName().compareTo(p1.getName());
}
}