Jdk之中的二分查找法
闲逛看代码,看到了JDK之中实现的二分查找法。这里做一下介绍:
直接看源码
/*** not sorted, the results are undefined. If the list contains multiple* elements equal to the specified object, there is no guarantee which one* will be found.* 在指定的list中查找指定的对象,二分查找法。这个list必须根据指定的排序规则comparator排序好。* 如果这个list没排序,返回的结果是不确定的。* 如果是list实现了 RandomAccess 接口,或者长度小于5000,那么它的查找时间复杂度是 log(n),* 如果list没实现,并且它的大小大于5000,它的时间复杂度就是O(logn)*/@SuppressWarnings("unchecked")public static <T> int binarySearch(List extends T> list, T key, Comparator super T> c) {if (c==null)return binarySearch((List extends Comparable super T>>) list, key);if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)//BINARYSEARCH_THRESHOLD = 5000return Collections.indexedBinarySearch(list, key, c);elsereturn Collections.iteratorBinarySearch(list, key, c);}
上面的代码一眼看上去挺奇怪的,为什么还有两个实现,二着有什么区别:
两段代码都特别简单:
// 二分查找算法private static <T> int indexedBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {int low = 0;int high = l.size()-1;while (low <= high) {int mid = (low + high) >>> 1; //表示除以2T midVal = l.get(mid);//和 iteratorBinarySearch 区别在这里int cmp = c.compare(midVal, key); // 比较if (cmp < 0)low = mid + 1;else if (cmp > 0)high = mid - 1;elsereturn mid; // key found}return -(low + 1); // key not found}private static <T> int iteratorBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {int low = 0;int high = l.size()-1;ListIterator<? extends T> i = l.listIterator();while (low <= high) {int mid = (low + high) >>> 1;T midVal = get(i, mid); // 和 indexedBinarySearch 的 差别在这里,这里需要while循环找到指定位置的节点。所以时间复杂度高。int cmp = c.compare(midVal, key);if (cmp < 0)low = mid + 1;else if (cmp > 0)high = mid - 1;elsereturn mid; // key found}return -(low + 1); // key not found}private static <T> T get(ListIterator<? extends T> i, int index) {T obj = null;int pos = i.nextIndex();if (pos <= index) {do {obj = i.next();} while (pos++ < index);} else {do {obj = i.previous();} while (--pos > index);}return obj;}
从这里可以不难看出indexedBinarySearch 的效率为什么比 iteratorBinarySearch高,因为 iteratorBinarySearch是通过loop的方式找到对应的元素,而indexedBinarySearch可以通过下标直接访问到元素。一个复杂度是O(logn),一个是O(1)。
然后进入两个的区别是 list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD。这个表示list实现了 RandomAccess 接口,或者元素少于 5000。
我们重点看看 RandomAccess。发现它其实是一个空interface。
public interface RandomAccess {}
大概的意思就是实现了RandomAccess 就可以使用类似list.get(i) 获取到元素,不然就要使用iterator的方式loop找到这个元素。又学到一样东西。
另外Collections还有几个类似的方法:
public static <T> int binarySearch(List extends T> list, T key, Comparator super T> c) {if (c==null)return binarySearch((List extends Comparable super T>>) list, key);if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)return Collections.indexedBinarySearch(list, key, c);elsereturn Collections.iteratorBinarySearch(list, key, c);}// 二分查找算法private static <T> int indexedBinarySearch(List extends T> l, T key, Comparator super T> c) {int low = 0;int high = l.size()-1;while (low <= high) {int mid = (low + high) >>> 1; //表示除以2T midVal = l.get(mid);//和 iteratorBinarySearch 区别在这里int cmp = c.compare(midVal, key); // 比较if (cmp < 0)low = mid + 1;else if (cmp > 0)high = mid - 1;elsereturn mid; // key found}return -(low + 1); // key not found}private static <T> int iteratorBinarySearch(List extends T> l, T key, Comparator super T> c) {int low = 0;int high = l.size()-1;ListIterator extends T> i = l.listIterator();while (low <= high) {int mid = (low + high) >>> 1;T midVal = get(i, mid); //和indexedBinarySearch区别在这里int cmp = c.compare(midVal, key);if (cmp < 0)low = mid + 1;else if (cmp > 0)high = mid - 1;elsereturn mid; // key found}return -(low + 1); // key not found}
关于我的jdk+jvm包的源码学习,带注解的在下面的链接都有:
https://gitee.com/ydonghao/openjdk11.git
https://gitee.com/ydonghao/openjdk14.git
