闲逛看代码,看到了JDK之中实现的二分查找法。这里做一下介绍:
直接看源码
/**
* Searches the specified list for the specified object using the binary
* search algorithm. The list must be sorted into ascending order
* according to the specified comparator (as by the
* {@link #sort(List, Comparator) sort(List, Comparator)}
* method), prior to making this call. If it is
* 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没排序,返回的结果是不确定的。
*
* This method runs in log(n) time for a "random access" list (which
* provides near-constant-time positional access). If the specified list
* does not implement the {@link RandomAccess} interface and is large,
* this method will do an iterator-based binary search that performs
* O(n) link traversals and O(log n) element comparisons.
* 如果是list实现了 RandomAccess 接口,或者长度小于5000,那么它的查找时间复杂度是 log(n),
* 如果list没实现,并且它的大小大于5000,它的时间复杂度就是O(logn)
*
* @param the class of the objects in the list
* @param list the list to be searched.
* @param key the key to be searched for.
* @param c the comparator by which the list is ordered.
* A {@code null} value indicates that the elements'
* {@linkplain Comparable natural ordering} should be used.
* @return the index of the search key, if it is contained in the list;
* otherwise, (-(insertion point) - 1)
. The
* insertion point is defined as the point at which the
* key would be inserted into the list: the index of the first
* element greater than the key, or {@code list.size()} if all
* elements in the list are less than the specified key. Note
* that this guarantees that the return value will be >= 0 if
* and only if the key is found.
* @throws ClassCastException if the list contains elements that are not
* mutually comparable using the specified comparator,
* or the search key is not mutually comparable with the
* elements of the list using this comparator.
*/
@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()
我们重点看看 RandomAccess
。发现它其实是一个空interface。
/**
* Marker interface used by {@code List} implementations to indicate that
* they support fast (generally constant time) random access. The primary
* purpose of this interface is to allow generic algorithms to alter their
* behavior to provide good performance when applied to either random or
* sequential access lists.
*
* The best algorithms for manipulating random access lists (such as
* {@code ArrayList}) can produce quadratic behavior when applied to
* sequential access lists (such as {@code LinkedList}). Generic list
* algorithms are encouraged to check whether the given list is an
* {@code instanceof} this interface before applying an algorithm that would
* provide poor performance if it were applied to a sequential access list,
* and to alter their behavior if necessary to guarantee acceptable
* performance.
*
*
It is recognized that the distinction between random and sequential
* access is often fuzzy. For example, some {@code List} implementations
* provide asymptotically linear access times if they get huge, but constant
* access times in practice. Such a {@code List} implementation
* should generally implement this interface. As a rule of thumb, a
* {@code List} implementation should implement this interface if,
* for typical instances of the class, this loop:
*
* for (int i=0, n=list.size(); i < n; i++)
* list.get(i);
*
* runs faster than this loop:
*
* for (Iterator i=list.iterator(); i.hasNext(); )
* i.next();
*
*
* This interface is a member of the
*
* Java Collections Framework.
*
* @since 1.4
*/
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