22.JAVA编程思想——排序和搜索
Java 1.2 添加了自己的一套实用工具,可用来对数组或列表进行排列和搜索。这些工具都属于两个新类的“静态”方法。这两个类分别是用于排序和搜索数组的Arrays,以及用于排序和搜索列表的Collections。
Arrays 类为所有基本数据类型的数组提供了一个过载的sort()和binarySearch(),它们亦可用于String 和Object。下面这个例子显示出如何排序和搜索一个字节数组(其他所有基本数据类型都是类似的)以及一个String 数组:
import java.util.*;
public class Array1 {
static Random r= newRandom();
static String ssource = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz";
static char[]src= ssource.toCharArray();
// Create arandom String
public staticString randString(int length){
char[] buf = newchar[length];
int rnd;
for (int i = 0; i < length; i++) {
rnd = Math.abs(r.nextInt()) % src.length;
buf[i] = src[rnd];
}
return new String(buf);
}
// Create arandom array of Strings:
public staticString[] randStrings(int length,intsize){
String[] s = new String[size];
for (int i = 0; i < size; i++)
s[i] = randString(length);
return s;
}
public staticvoidprint(byte[]b) {
for (int i = 0; i < b.length; i++)
System.out.print(b[i] + " ");
System.out.println();
}
public staticvoidprint(String[] s){
for (int i = 0; i < s.length; i++)
System.out.print(s[i] + " ");
System.out.println();
}
public staticvoidmain(String[] args){
byte[] b = newbyte[15];
r.nextBytes(b); // Fill withrandom bytes
print(b);
Arrays.sort(b);
print(b);
int loc = Arrays.binarySearch(b, b[10]);
System.out.println("Location of " + b[10] + " = " + loc);
// Test String sort & search:
String[] s = randStrings(4, 10);
print(s);
Arrays.sort(s);
print(s);
loc = Arrays.binarySearch(s, s[4]);
System.out.println("Location of " + s[4] + " = " + loc);
}
} /// :~
98-34 -68 -95 31 54 120 46 50 15 1 49 25 80 120
-95-68 -34 1 15 25 31 46 49 50 54 80 98 120 120
Locationof 54 = 10
vmdoGxLR IunY EXKM yzdY Veng HKbB zZRw RnVS SmoD
EXKMGxLR HKbB IunY RnVS SmoD Veng vmdo yzdY zZRw
Locationof RnVS = 4
类的第一部分包含了用于产生随机字串对象的实用工具,可供选择的随机字母保存在一个字符数组中。
randString()返回一个任意长度的字串;而readStrings()创建随机字串的一个数组,同时给定每个字串的长度以及希望的数组大小。两个print()方法简化了对示范数组的显示。在main()中,Random.nextBytes()用随机选择的字节填充数组自变量(没有对应的Random方法用于创建其他基本数据类型的数组)。获得一个数组后,便可发现为了执行sort()或者binarySearch(),只需发出一次方法调用即可。与binarySearch()有关的还有一个重要的警告:若在执行一次binarySearch()之前不调用sort(),便会发生不可预测的行为,其中甚至包括无限循环。
对String 的排序以及搜索是相似的,但在运行程序的时候,我们会注意到一个有趣的现象:排序遵守的是字典顺序,亦即大写字母在字符集中位于小写字母的前面。因此,所有大写字母都位于列表的最前面,后面再跟上小写字母——Z 居然位于a 的前面。似乎连电话簿也是这样排序的。
但假若我们不满足这一排序方式,又该如何处理呢?如果必须对以A 或a 开头的词条分别到两处地方查看,那么肯定会使读者颇不耐烦。
若想对一个Object 数组进行排序,那么必须解决一个问题。根据什么来判定两个Object 的顺序呢?不幸的是,最初的Java 设计者并不认为这是一个重要的问题,否则就已经在根类Object 里定义它了。这样造成的一个后果便是:必须从外部进行Object 的排序,而且新的集合库提供了实现这一操作的标准方式(最理想的是在Object 里定义它)。
针对Object 数组(以及String,它当然属于Object 的一种),可使用一个sort(),并令其接纳另一个参数:实现了Comparator 接口(即“比较器”接口,新集合库的一部分)的一个对象,并用它的单个compare()方法进行比较。这个方法将两个准备比较的对象作为自己的参数使用——若第一个参数小于第二个,返回一个负整数;若相等,返回零;若第一个参数大于第二个,则返回正整数。基于这一规则,上述例子的String 部分便可重新写过,令其进行真正按字母顺序的排序:
import java.util.*;
public class AlphaComp implements Comparator{
public intcompare(Object o1,Object o2){
// Assume it's used only for Strings...
String s1 = ((String) o1).toLowerCase();
String s2 = ((String) o2).toLowerCase();
return s1.compareTo(s2);
}
public staticvoidmain(String[] args){
String[] s = Array1.randStrings(4, 10);
Array1.print(s);
AlphaComp ac = new AlphaComp();
Arrays.sort(s, ac);
Array1.print(s);
// Must use the Comparator to search, also:
int loc = Arrays.binarySearch(s, s[3], ac);
System.out.println("Location of " + s[3] + " = " + loc);
}
} /// :~
CIurMMxl UKSJ HVkY YZaF MHTg kuhV FRAk Vhgj AyBq
AyBqCIur FRAk HVkY kuhV MHTg MMxl UKSJ Vhgj YZaF
Locationof HVkY = 3
通过造型为String,compare()方法会进行“暗示”性的测试,保证自己操作的只能是String 对象——运行期系统会捕获任何差错。将两个字串都强迫换成小写形式后,String.compareTo()方法会产生预期的结果。若用自己的Comparator 来进行一次sort(),那么在使用binarySearch()时必须使用那个相同的Comparator。
Arrays 类提供了另一个sort()方法,它会采用单个自变量:一个Object 数组,但没有Comparator。这个sort()方法也必须用同样的方式来比较两个Object。通过实现Comparable 接口,它采用了赋予一个类的“自然比较方法”。这个接口含有单独一个方法——compareTo(),能分别根据它小于、等于或者大于自变量而返回负数、零或者正数,从而实现对象的比较。
import java.util.*;
public class CompClass implements Comparable{
private inti;
public CompClass(intii){
i = ii;
}
public int compareTo(Object o) {
// Implicitly tests for correct type:
int argi = ((CompClass)o).i;
if(i == argi)return0;
if(i < argi)return-1;
return 1;
}
public staticvoidprint(Object[] a){
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}
public String toString() {
return i + "";
}
public staticvoidmain(String[] args){
CompClass[] a = new CompClass[20];
for (int i = 0; i < a.length; i++)
a[i] = newCompClass((int)(Math.random() * 100));
print(a);
Arrays.sort(a);
print(a);
int loc = Arrays.binarySearch(a, a[3]);
System.out.println("Location of " + a[3] + " = " + loc);
}
} /// :~
88 34 78 64 29 70 1 25 10 39 95 9 36 74 97 82 69 20 66
18 8 9 10 20 25 29 34 36 39 64 66 69 70 74 78 82 95 97
Locationof 9 = 3
可用与数组相同的形式排序和搜索一个列表(List)。用于排序和搜索列表的静态方法包含在类
Collections 中,但它们拥有与Arrays 中差不多的签名:sort(List)用于对一个实现了Comparable 的对象列表进行排序;binarySearch(List,Object)用于查找列表中的某个对象;sort(List,Comparator)利用一个“比较器”对一个列表进行排序;而binarySearch(List,Object,Comparator)则用于查找那个列表中的一个对象。下面这个例子利用了预先定义好的CompClass 和AlphaComp 来示范Collections 中的各种排序工具:
import java.util.*;
public class ListSort {
public staticvoidmain(String[] args){
final int SZ = 20;
// Using "natural comparison method":
List a = new ArrayList();
for (int i = 0; i < SZ; i++)
a.add(new CompClass((int) (Math.random() *100)));
Collection1.print(a);
Collections.sort(a);
Collection1.print(a);
Object find = a.get(SZ/ 2);
int loc = Collections.binarySearch(a, find);
System.out.println("Location of " + find + " = " + loc);
// Using a Comparator:
List b = new ArrayList();
for (int i = 0; i < SZ; i++)
b.add(Array1.randString(4));
Collection1.print(b);
AlphaComp ac = new AlphaComp();
Collections.sort(b, ac);
Collection1.print(b);
find = b.get(SZ/ 2);
// Must use the Comparator to search, also:
loc = Collections.binarySearch(b, find, ac);
System.out.println("Location of " + find + " = " + loc);
}
} /// :~
3861 43 10 90 92 32 14 22 81 38 13 95 25 62 52 76 76 35 65
1013 14 22 25 32 35 38 38 43 52 61 62 65 76 76 81 90 92 95
Locationof 52 = 10
lSgYMpSR skmc CoEw IPzS YQQU CTiU TRYc FaHh uAsm oehY zpTa dktG SiMp KpZh lBro oJFPXnbe eHlZ UqsH
CoEwCTiU dktG eHlZ FaHh IPzS KpZh lBro lSgY MpSR oehY oJFP SiMp skmc TRYc uAsm UqsHXnbe YQQU zpTa
Locationof oehY = 10
这些方法的用法与在Arrays 中的用法是完全一致的,只是用一个列表代替了数组。
TreeMap 也必须根据Comparable或者Comparator 对自己的对象进行排序。