【Java】JDK借助RandomAccess接口 ,定制ArrayList与LinkedList的二分查找策略

本文详细探讨了Java集合框架中ArrayList和LinkedList的实现差异,特别是它们对于二分查找算法的性能影响。通过对比两种集合类型在执行二分查找时的不同表现,揭示了RandomAccess接口在优化ArrayList性能方面的作用,以及LinkedList如何利用ListIterator来提高查找效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Collections工具类二分查找

  • ArrayList 类签名
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • LinkedList 类签名
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  • Collections 对 RandomAccess 进行继续下标的二分查找
    if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
        return Collections.indexedBinarySearch(list, key);
    else
        return Collections.iteratorBinarySearch(list, key);

RandomAccess 接口

空接口
在这里插入图片描述

ArrayList 二分查找

内在原因:
ArrayList.get(index) 时间复杂度:O(1)
LinkedList.get(index) 时间复杂度:O(n) 。后文将二分查找时如何优化这个O(n)的复杂度

演示:
ArrayList寻找value:16的元素

  1. 根据数组长度 mid 确认为 4,get(4) == 11 < 16
  2. 根据二分算法mid 确认为 6,get(6) == 16
value15781115162839
index012345678

相关代码:

private static <T> int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
        int low = 0;
        int high = list.size()-1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = list.get(mid);
            int cmp = midVal.compareTo(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
    }

LinkedList 二分查找

value15781115162839
迭代器缓存的下标012345678

LinkedList get操作的效率问题

链表获取指定下标的元素,需要从从头遍历

ListIterator 解决问题

LinkedList 的get方法的效率较低,所以Collections 在二分查找的场景使用了 ListIterator
ListIterator 可以向前遍历,也可以向后遍历

    private static <T> T get(ListIterator<? extends T> i, int index) {
        T obj = null;  // 待返回的value
        int pos = i.nextIndex(); // 向后移动游标获取当前位置
        if (pos <= index) { // 当前位置小于index
            do {
                obj = i.next(); 
            } while (pos++ < index); // 一直向后找,直到到达index的位置,返回index对应的value
        } else {
            do {
                obj = i.previous(); // 反之向前找
            } while (--pos > index);
        }
        return obj;
    }

结论

RandomAccess 接口只参与标记作用,目的是让ArrayList发挥其底层数据结构数组的O(1)查找能力
根据上文,说明了以下代码是个很低效的代码

		LinkedList linkedList = new LinkedList();
		for(int i = 0; i < linkedList.size(); i++) {
			System.out.println(linkedList.get(i);
		}

写成高效的,就是foreach循环的本质。

        LinkedList linkedList = new LinkedList();
        ListIterator listIterator = linkedList.listIterator(); // 只往后遍历可以写成iterator() 
        while (listIterator.hasNext()) { 
            listIterator.next();
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值