vlambda博客
学习文章列表

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 = 5000 return Collections.indexedBinarySearch(list, key, c); else return 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; //表示除以2 T midVal = l.get(mid);//和 iteratorBinarySearch 区别在这里 int cmp = c.compare(midVal, key); // 比较
if (cmp < 0) low = mid + 1; else if (cmp > 0) high = mid - 1; else return 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; else return 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); else return 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; //表示除以2 T midVal = l.get(mid);//和 iteratorBinarySearch 区别在这里 int cmp = c.compare(midVal, key); // 比较
if (cmp < 0) low = mid + 1; else if (cmp > 0) high = mid - 1; else return 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; else return mid; // key found } return -(low + 1); // key not found}

关于我的jdk+jvm包的源码学习,带注解的在下面的链接都有:

https://gitee.com/ydonghao/openjdk11.git

https://gitee.com/ydonghao/openjdk14.git