开场白:
很长时间以来一直想把以前所学习的一些基本算法整理一遍,最近准备利用周末的时间在博客园写一个算法系列,留作记录以备将来查看。首先从“元素项类” 谈起。
所有源代码在google code上http://code.google.com/p/algolib-java/下载或者直接猛击:/Files/luweiseu/Source_codes.rar
在线性表以及其它的数据结构中,每个元素都包含数据区,即我们这里讲的“元素项”(Element Item)。元素项的元素值可以是数值类型,如整数、浮点数等;也可以是字符型,如String,char。一般情况下,元素项的类型在同一个线性表中是相同的,但是不能排除类型不同的情况发生。在本书接下来的很多章节将会涉及到元素项的使用及操作,本节内容将重点介绍元素项类的设计和实现。
本系列中采用泛型类编程方法,即元素项建立泛型类(generic type)。元素项类的对象的具体类型可以是任意的,如Integer(整数类型)、Double(双精度类型)、String(字符串类型)等,甚至也可以是用户自定义的类。不管是什么具体类型的对象,这些对象常常需要进行比较,打印等操作。整数类型的对象可以依据整数数值的大小进行比较,打印的内容也是该整数的数值。但是对一个用户自定义的类怎样才能具有可比较性?如何定义自定义类的打印内容?
在C++语言中对象的比较可以通过运算符重载实现,但是JAVA语言中没有这样的机制。在JAVA语言中有一个替代的方法是继承Comparable定义的接口。用户自定义的类通过实现Comparable接口的函数compareTo使得类的对象具有“可比较性”。该函数的返回值为整型,1表示大于,0表示相等,-1表示小于。对类的对象进行打印时,用户可以选择性地打印类中的一些信息,这可以通过重载函数toString来实现,该函数返回类型是String。在执行System.out.println()函数打印对象时,事实上打印的是该对象的toString函数的返回值。
这里以一个常见的类来解释上面讨论的“可比较性”和选择打印过程。设计学生类,Student。包含学生的学号、年龄和成绩信息。学生和学生之间有可能需要比较的对象为年龄或成绩,这里考虑比较成绩,即在事项compareTo函数时,将本学生的成绩与另一个学生进行比价,若大于则返回1,小于则返回-1,等于则返回0。对一个学生对象进行打印时,可以选择将学生的部分或者所有信息显示出来,在toString函数中将需要显示的信息返回即可。
下面是学生类Student的实现:
/**
* 学生类,用于测试ElemItem类的可比较性 Studnet.java
*/
public class Student implements Comparable{
private int ID; //学号
private int age; //年龄
private int ave_score; //平均分
public Student(){//默认构造函数
ID = -1;
age = -1;
ave_score = -1;
}
//有参数构造函数
public Student(int _id, int _age, int _ave_age){
ID = _id;
age = _age;
ave_score = _ave_age;
}
//实现Comparable接口函数
//当前学生的成绩比带比较的学生的成绩高时返回1,
//低时返回-1,相等时返回0
public int compareTo(Object _student) {
Student _s = (Student)(_student);
return (ave_score > _s.ave_score)?1:
((ave_score < _s.ave_score)? -1:0);
}
//获取学生的信息
public int getID(){ return ID;}
public int getAge() {return age;}
public int getAve_score() {return ave_score;}
//打印的内容,打印学生的学号,年龄和成绩
public String toString(){
return "Studnet number " + ID + ", age " +
age + ", aveage score " + ave_score;
}
}
上面我们讨论到,元素项类中的元素值必须具有可比较性,那么我们在定义泛型类型时可以加上这一限定,即泛型必须extends Comparable<T>。此外,我们可以将元素项的元素值之间的比较再做一层封装,封装成元素项之间的比较,即在元素项类中设计一个成员函数来实现两个元素项中的元素元素值的比较。
元素项类的JAVA语言设计:
* 建立元素项类,这里采用Java泛型编程,并且泛型限制为
* 继承了Comparable类的类型
* 泛型T可以是任意类型,包括整数Integer,Double等
*/
package Element;
public class ElemItem<T extends Comparable<T>> {
public T elem; //泛型元素
public ElemItem(){ //默认构造函数
elem = null;
}
public ElemItem(T t){ //含参数的构造函数
elem = t;
}
public T getElem(){ //获取元素
return elem;
}
// 元素项之间具有可比较性
public int compareTo(ElemItem<T> oo){
return elem.compareTo(oo.elem);
}
}
下面的例子中分别创建String、整数、双精度类型以及上面我们自定义的Student类的元素项对象,用标准输出流打印元素项的数据,并测试了学生类对象之间的比较。
/**
* ElemItem类的测试示例代码,ExampleElemItem.java
* 测试类Integer,Double,String和Student类型的
* ElemItem
*/
public class ExampleElemItem {
public static void main(String args[]){
//字符串类型的元素项
ElemItem<String> elemString =
new ElemItem<String>("String type elem item");
//整数类型的元素项
ElemItem<Integer> elemInt
= new ElemItem<Integer>(123);
//Double型的元素项
ElemItem<Double> elemDouble
= new ElemItem<Double>(12.345);
//打印各类元素项
System.out.println(elemString.getElem().toString());
System.out.println(elemInt.getElem());
System.out.println(elemDouble.getElem());
// Student 类的测试示例
//学生1,学号:1,年龄15,成绩97
ElemItem S1
= new ElemItem<Student>(new Student(1, 15, 97));
//学生2,学号:2,年龄16,成绩95
ElemItem S2
= new ElemItem<Student>(new Student(2, 16, 95));
System.out.println(S1.getElem());
System.out.println(S2.getElem());
int cmp = S1.compareTo(S2);
System.out.println("S1 比 S2成绩" +
(cmp>0?"高":(cmp<0?"低":"相同")));
}
}
本示例程序首先创建三个ElemItem的对象,分别为String字符串类型,Integer整数类型和双精度Double类型,调用系统函数println将这三个对象打印出来,事实上打印的是这三个对象的toString成员函数的返回值。然后创建两个Student类型的ElemItem对象S1和S2,调用系统函数println打印这两个对象,实际上也是打印的这两个对象的toString成员函数的返回值,具体见Student类中的定义。最后比较这两个对象,实际上在函数内部是调用Student类的compareTo函数进行学生成绩的比较。
输出结果:
String type elem item
123
12.345
Studnet number 1, age 15, aveage score 97
Studnet number 2, age 16, aveage score 95
S1 比 S2成绩高
在本系列接下来的几章内容中,元素项将作为各种链表、树、图等数据结构中的基本元素,在讨论具体内容时读者将可以进一步了解到本节中设计的元素项的优势。