Arrays的二分查找
二分查找也称为折半查找,是对有序元素查找的一种算法,在查找的过程中,不断的将搜索长度减半,因此效率不错。Java的JDK提供了二分法查找的算法,使用的方法是Arrays.binarySearch()。binarySearch()方法提供了多种数据类型的二分查找,比如实现了int、float、double、char、byte和Object类型,还提供了对泛型的支持。在JavaAPI手册中提供了接口说明,比如如下方法:
1 static int binarySearch(long[] a, int fromIndex, int toIndex, long key) 2 static int binarySearch(long[] a, long key) 3 static int binarySearch(Object[] a, int fromIndex, int toIndex, Object key) 4 static int binarySearch(Object[] a, Object key) 5 static int binarySearch(short[] a, int fromIndex, int toIndex, short key) 6 static int binarySearch(short[] a, short key) 7 static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) 8 static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
以上是Arrays类中提供的部分关于binanrySearch()方法的定义,对于不同类型来说,基本提供了两种方法,第一种方法需要在调用时提供数组、开始下标、结束下标和查找的值,比如:
static int binarySearch(long[] a, int fromIndex, int toIndex, long key)
另外一种查找的方法是提供数组和查找的值即可,比如:
static int binarySearch(long[] a, long key)
对于这两种搜索方法,在Java中提供了统一的调用方法,可以查看其代码,在Java的安装目录下找到src.zip文件,该文件是Java的部分源码。将src.zip文件解压缩,在java/util/Arrays.java中可以找到以上两个方法的实现,代码如下:
1 public static int binarySearch(long[] a, long key) { 2 return binarySearch0(a, 0, a.length, key); 3 } 4 5 public static int binarySearch(long[] a, int fromIndex, int toIndex, 6 long key) { 7 rangeCheck(a.length, fromIndex, toIndex); 8 return binarySearch0(a, fromIndex, toIndex, key); 9 }
从代码中可以看到,两个方法最后都调用了binarySearch0()方法,但是在第二个binarySearch()方法中调用了rangeCheck()方法,该方法用于检查数组长度、开始下标和结束下标的正确性,rangeCheck()方法的实现如下:
1 /** 2 * Checks that {@code fromIndex} and {@code toIndex} are in 3 * the range and throws an exception if they aren't. 4 */ 5 private static void rangeCheck(int arrayLength, int fromIndex, int toIndex) { 6 if (fromIndex > toIndex) { 7 throw new IllegalArgumentException( 8 "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); 9 } 10 if (fromIndex < 0) { 11 throw new ArrayIndexOutOfBoundsException(fromIndex); 12 } 13 if (toIndex > arrayLength) { 14 throw new ArrayIndexOutOfBoundsException(toIndex); 15 } 16 }
如果调用第一个binarySearch()方法,数组长度、开始下标和结束下标是方法中自行获取的,因此不需要进行rangeCheck(),而调用第二个binarySearch()方法时,数组长度、开始下标和结束下标是调用时外部提供的,因此为了保证正确性进行了rangeCheck()。
二分法真正的实现是binarySearch0()方法,根据不同的数据类型,binarySearch0()方法也提供了多种重载,这里只看long类型的实现,代码如下:
1 private static int binarySearch0(long[] a, int fromIndex, int toIndex, 2 long key) { 3 int low = fromIndex; 4 int high = toIndex - 1; 5 6 while (low <= high) { 7 int mid = (low + high) >>> 1; 8 long midVal = a[mid]; 9 10 if (midVal < key) 11 low = mid + 1; 12 else if (midVal > key) 13 high = mid - 1; 14 else 15 return mid; // key found 16 } 17 return -(low + 1); // key not found. 18 }
二分查找的思路是从有序(从小到大)数组的中间位置开始查找,如果中间位置的数小于查找的目标值,则查找数组中间值右侧的部分,如果中间位置的数大于查找的目标值,则查找数组中间值左侧的部分,如果相等,则返回当前的下标,如果没有找到则返回一个负数。
除了上面的实现外,还有一种针对泛型的binarySeach()的方法,如下:
1 static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) 2 static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
在上面两个方法的定义中,最后一个参数是一个比较器,比较器的作用是比较两个元素的大小用的,查看以上两个方法的实现,代码如下:
1 public static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) { 2 return binarySearch0(a, 0, a.length, key, c); 3 } 4 5 public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, 6 T key, Comparator<? super T> c) { 7 rangeCheck(a.length, fromIndex, toIndex); 8 return binarySearch0(a, fromIndex, toIndex, key, c); 9 }
以上两个方法同样调用了binarySearch0()方法,该binarySearch0()方法的实现代码如下:
1 private static <T> int binarySearch0(T[] a, int fromIndex, int toIndex, 2 T key, Comparator<? super T> c) { 3 if (c == null) { 4 return binarySearch0(a, fromIndex, toIndex, key); 5 } 6 int low = fromIndex; 7 int high = toIndex - 1; 8 9 while (low <= high) { 10 int mid = (low + high) >>> 1; 11 T midVal = a[mid]; 12 int cmp = c.compare(midVal, key); 13 if (cmp < 0) 14 low = mid + 1; 15 else if (cmp > 0) 16 high = mid - 1; 17 else 18 return mid; // key found 19 } 20 return -(low + 1); // key not found. 21 }
观察代码的第12行,c是比较器,该比较器中提供了一个compare()方法用来比较两个元素的大小,如果midVal比key小,compare返回负数,如果midVal比key大,compare返回整数,如果midVal和key相等,compare则返回0。
以上就是在学习Arrays工具类的使用时,顺便阅读了它的实现,而刚好又能看懂,所以记录在此!