Jdk之中的二分查找法

闲逛看代码,看到了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 接口,或者元素少于 5000。

我们重点看看 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

更多精彩内容欢迎关注我的微信公众号
Jdk之中的二分查找法_第1张图片

你可能感兴趣的:(Java基础&源码解析,数据结构与算法)