Java 和库都缺少的一样东西是算术运算甚至没有最简单的排序运算方法因此我们最好创建一个Vector利用经典的Quicksort(快速排序)方法对其自身进行排序
编写通用的排序代码时面临的一个问题是必须根据对象的实际类型来执行比较运算从而实现正确的排序当然一个办法是为每种不同的类型都写一个不同的排序方法然而应认识到假若这样做以后增加新类型时便不易实现代码的重复利用
程序设计一个主要的目标就是将发生变化的东西同保持不变的东西分隔开在这里保持不变的代码是通用的排序算法而每次使用时都要变化的是对象的实际比较方法因此我们不可将比较代码硬编码到多个不同的排序例程内而是采用回调技术利用回调经常发生变化的那部分代码会封装到它自己的类内而总是保持相同的代码则回调发生变化的代码这样一来不同的对象就可以表达不同的比较方式同时向它们传递相同的排序代码
下面这个接口(Interface)展示了如何比较两个对象它将那些要发生变化的东西封装在内
//: Comparejava
// Interface for sorting callback:
package c;
interface Compare {
boolean lessThan(Object lhs Object rhs);
boolean lessThanOrEqual(Object lhs Object rhs);
} ///:~
对这两种方法来说lhs代表本次比较中的左手对象而rhs代表右手对象
可创建Vector的一个子类通过Compare实现快速排序对于这种算法包括它的速度以及原理等等在此不具体说明欲知详情可参考Binstock和Rex编着的《Practical Algorithms for Programmers》由AddisonWesley于年出版
//: SortVectorjava
// A generic sorting vector
package c;
import javautil*;
public class SortVector extends Vector {
private Compare compare; // To hold the callback
public SortVector(Compare comp) {
compare = comp;
}
public void sort() {
quickSort( size() );
}
private void quickSort(int left int right) {
if(right > left) {
Object o = elementAt(right);
int i = left ;
int j = right;
while(true) {
while(comparelessThan(
elementAt(++i) o))
;
while(j > )
if(comparelessThanOrEqual(
elementAt(j) o))
break; // out of while
if(i >= j) break;
swap(i j);
}
swap(i right);
quickSort(left i);
quickSort(i+ right);
}
}
private void swap(int loc int loc) {
Object tmp = elementAt(loc);
setElementAt(elementAt(loc) loc);
setElementAt(tmp loc);
}
} ///:~
现在大家可以明白回调一词的来历这是由于quickSort()方法往回调用了Compare中的方法从中亦可理解这种技术如何生成通用的可重复利用(再生)的代码
为使用SortVector必须创建一个类令其为我们准备排序的对象实现Compare此时内部类并不显得特别重要但对于代码的组织却是有益的下面是针对String对象的一个例子
//: StringSortTestjava
// Testing the generic sorting Vector
package c;
import javautil*;
public class StringSortTest {
static class StringCompare implements Compare {
public boolean lessThan(Object l Object r) {
return ((String)l)toLowerCase(pareTo(
((String)r)toLowerCase()) < ;
}
public boolean
lessThanOrEqual(Object l Object r) {
return ((String)l)toLowerCase(pareTo(
((String)r)toLowerCase()) <= ;
}
}
public static void main(String[] args) {
SortVector sv =
new SortVector(new StringCompare());
svaddElement(d);
svaddElement(A);
svaddElement(C);
svaddElement(c);
svaddElement(b);
svaddElement(B);
svaddElement(D);
svaddElement(a);
svsort();
Enumeration e = svelements();
while(ehasMoreElements())
Systemoutprintln(enextElement());
}
} ///:~
内部类是静态(Static)的因为它毋需连接一个外部类即可工作
大家可以看到一旦设置好框架就可以非常方便地重复使用象这样的一个设计——只需简单地写一个类将需要发生变化的东西封装进去然后将一个对象传给SortVector即可
比较时将字串强制为小写形式所以大写A会排列于小写a的旁边而不会移动一个完全不同的地方然而该例也显示了这种方法的一个不足因为上述测试代码按照出现顺序排列同一个字母的大写和小写形式A a b B c C d D但这通常不是一个大问题因为经常处理的都是更长的字串所以上述效果不会显露出来(Java 的集合提供了排序功能已解决了这个问题)
继承(extends)在这儿用于创建一种新类型的Vector——也就是说SortVector属于一种Vector并带有一些附加的功能继承在这里可发挥很大的作用但了带来了问题它使一些方法具有了final属性(已在第章讲述)所以不能覆盖它们如果想创建一个排好序的Vector令其只接收和生成String对象就会遇到麻烦因为addElement()和elementAt()都具有final属性而且它们都是我们必须覆盖的方法否则便无法实现只能接收和产生String对象
但在另一方面请考虑采用合成方法将一个对象置入一个新类的内部此时不是改写上述代码来达到这个目的而是在新类里简单地使用一个SortVector在这种情况下用于实现Compare接口的内部类就可以匿名地创建如下所示
//: StrSortVectorjava
// Automatically sorted Vector that
// accepts and produces only Strings
package c;
import javautil*;
public class StrSortVector {
private SortVector v = new SortVector(
// Anonymous inner class:
new Compare() {
public boolean
lessThan(Object l Object r) {
return
((String)l)toLowerCase(pareTo(
((String)r)toLowerCase()) < ;
}
public boolean
lessThanOrEqual(Object l Object r) {
return
((String)l)toLowerCase(pareTo(
((String)r)toLowerCase()) <= 0;
}
}
);
private boolean sorted = false;
public void addElement(String s) {
v.addElement(s);
sorted = false;
}
public String elementAt(int index) {
if(!sorted) {
v.sort();
sorted = true;
}
return (String)v.elementAt(index);
}
public Enumeration elements() {
if(!sorted) {
v.sort();
sorted = true;
}
return v.elements();
}
// Test it:
public static void main(String[] args) {
StrSortVector sv = new StrSortVector();
sv.addElement("d");
sv.addElement("A");
sv.addElement("C");
sv.addElement("c");
sv.addElement("b");
sv.addElement("B");
sv.addElement("D");
sv.addElement("a");
Enumeration e = sv.elements();
while(e.hasMoreElements())
System.out.println(e.nextElement());
}
} ///:~
这样便可快速再生来自SortVector的代码,从而获得希望的功能。tW.WIngwIT.COM然而,并不是来自SortVector和Vector的所有public方法都能在StrSortVector中出现。若按这种形式再生代码,可在新类里为包含类内的每一个方法都生成一个定义。当然,也可以在刚开始时只添加少数几个,以后根据需要再添加更多的。新类的设计最终会稳定下来。
这种方法的好处在于它仍然只接纳String对象,也只产生String对象。而且相应的检查是在编译期间进行的,而非在运行期。当然,只有addElement()和elementAt()才具备这一特性;elements()仍然会产生一个Enumeration(枚举),它在编译期的类型是未定的。当然,对Enumeration以及在StrSortVector中的类型检查会照旧进行;如果真的有什么错误,运行期间会简单地产生一个违例。事实上,我们在编译或运行期间能保