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没排序,返回的结果是不确定的。
     *
     * <p>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  <T> 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, <code>(-(<i>insertion point</i>) - 1)</code>.  The
     *         <i>insertion point</i> 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 &gt;= 0 if
     *         and only if the key is found.
     * @throws ClassCastException if the list contains elements that are not
     *         <i>mutually comparable</i> 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()<BINARYSEARCH_THRESHOLD。这个表示list实现了 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.
 *
 * <p>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.
 *
 * <p>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:
 * <pre>
 *     for (int i=0, n=list.size(); i &lt; n; i++)
 *         list.get(i);
 * </pre>
 * runs faster than this loop:
 * <pre>
 *     for (Iterator i=list.iterator(); i.hasNext(); )
 *         i.next();
 * </pre>
 *
 * <p>This interface is a member of the
 * <a href="{@docRoot}/java.base/java/util/package-summary.html#CollectionsFramework">
 * Java Collections Framework</a>.
 *
 * @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

更多精彩内容欢迎关注我的微信公众号
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值