为什么ArrayList要实现RandomAccess接口?
RandomAccess接口是一个标记接口(marker interface),没有任何方法定义。它的存在主要是为了标识那些支持快速(常数时间复杂度)随机访问的列表实现。例如,ArrayList和Vector都实现了这个接口,而LinkedList则没有实现这个接口。
RandomAccess接口的作用
优化算法的选择
一些标准库中的算法会根据列表是否实现了RandomAccess接口来选择不同的优化策略。例如,在Java的java.util.Collections类中,有些方法会检查传入的列表是否实现了RandomAccess接口,以决定采用哪种访问方式:
public static <T> void sort(List<T> list, Comparator<? super T> c) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
indexedSort(list, c);
else
iteratorSort(list, c);
}
private static <T> void indexedSort(List<T> list, Comparator<? super T> c) {
for (int i=0; i<list.size(); i++)
// 使用索引访问进行排序
}
private static <T> void iteratorSort(List<T> list, Comparator<? super T> c) {
ListIterator<T> i = list.listIterator();
while (i.hasNext())
// 使用迭代器进行排序
}
Collections中关于RandomAccess的应用,在Collections的二分搜索方法:
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
可以看出,该方法对List是否实现了RandomAccess接口进行了判断,若实现,则执行indexedBinarySearch方法,否则,则执行iteratorBinarySearch方法。即实现了RandomAccess接口的List使用索引遍历,没实现的则采用迭代器进行遍历,原因:不同的集合底层设计不一样,例如ArrayList底层是连续,适合索引访问;LinkedList则不是,使用索引访问就很消耗时间。
提供给开发者的提示
实现RandomAccess接口也为开发者提供了一种提示,即这个列表实现(例如ArrayList)适合那些需要频繁随机访问的场景。开发者可以通过检查列表是否实现了RandomAccess接口来编写更高效的代码:
if (list instanceof RandomAccess) {
// 使用索引访问进行操作
for (int i = 0; i < list.size(); i++) {
// 访问元素 list.get(i)
}
} else {
// 使用迭代器进行操作
for (E element : list) {
// 访问元素 element
}
}
总结
ArrayList实现RandomAccess接口的主要原因是为了表明它支持快速随机访问,这样可以帮助标准库中的算法和开发者在使用ArrayList时选择最优的访问方式。通过这一标识,代码可以根据具体的列表实现选择不同的操作策略,从而提高性能和效率。