Java之RandomAccess接口

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QQB67G8COM/article/details/89707961

该接口属于标志(mark)接口,不提供任何接口方法或变量,但是实现该接口的支持随机访问,一说到这里就开始奇怪了,一个空接口怎么会支持随机访问,实际上不是该接口支持随机访问,这里涉及到一个设计问题,先看官文的解析:
(English)It is recognized that the distinction between random and sequentialaccess is often fuzzy. For example, some List implementationsprovide asymptotically linear access times if they get huge, but constantaccess times in practice. Such a List implementationshould generally implement this interface. As a rule of thumb, a List implementation should implement this interface if,for typical instances of the class, this loop:
(Chinese)人们认识到,随机访问和顺序访问之间的区别通常是模糊的。 例如,一些List实现提供渐近的线性访问时间,如果它们在实践中获得巨大但是恒定的访问时间。 这样的一个List实现应该通常实现这个接口。 根据经验, 一个List的实现应实现此接口,如果对于类的典型实例,此循环:
for (int i=0, n=list.size(); i < n; i++)
list.get(i);
比这个循环运行得更快:
for (Iterator i=list.iterator(); i.hasNext(); )
i.next();
此接口是成员Java Collections Framework 。


啥意思?人们都知道Array是可以随机访问的,根据上面的典型实例的循环,也就是说ArrayList通过普通的for循环会比使用迭代器来遍历List更快,如果是LinkedList,根据链表添加删除元素容易,查找元素困难的特点,普通的for循环会更加消耗性能,使用迭代器反而会更快,也就是说,实现RandomAccess接口的都支持随机访问,也就是普通的for遍历比迭代器性能更优。
测试代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class test{
	
	ArrayList<Integer> aList = new ArrayList<Integer>(100000);
	LinkedList<Integer> lList = new LinkedList<Integer>();
	
	public void addE() {
		int i = 0;
		while(i<100000) {
			aList.add(i);
			lList.add(i);
			i++;
		}
	}
	
	public void AListUseTime() {
		Integer num = 0;
		long start1 = System.currentTimeMillis();
		for(int i =0; i < aList.size();i++) {
			num = aList.get(i);
		}
		long end1 = System.currentTimeMillis();
		System.out.println("ArrayList:普通For遍历使用时间"+(end1-start1));
		long start2 = System.currentTimeMillis();
		for(Iterator<Integer> list = aList.iterator(); list.hasNext();) {
			num = list.next();
		}
		long end2 = System.currentTimeMillis();
		
		System.out.println("ArrayList:迭代器遍历使用时间"+(end2-start2));
	}
	
	public void LListUseTime() {
		Integer num = 0;
		long start1 = System.currentTimeMillis();
		for(int i =0; i < lList.size();i++) {
			num = lList.get(i);
		}
		long end1 = System.currentTimeMillis();
		System.out.println("LinkedList:普通For遍历使用时间"+(end1-start1));
		long start2 = System.currentTimeMillis();
		for(Iterator<Integer> list = lList.iterator(); list.hasNext();) {
			num = list.next();
		}
		long end2 = System.currentTimeMillis();
		
		System.out.println("LinkedList:迭代器遍历使用时间"+(end2-start2));
	}
	 
    public static void main(String[] args) {

    	final test ts = new test();
    	ts.addE();
    	Thread t1 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				ts.AListUseTime();
			}
		}, "t1");
    	
    	Thread t2 = new Thread(new Runnable() {
			
			@Override
			public void run() {
				ts.LListUseTime();
			}
		}, "t2");
    	
    	t1.start();
    	t2.start();
    }	
}

显示结果:
ArrayList:普通For遍历使用时间4
ArrayList:迭代器遍历使用时间2
LinkedList:普通For遍历使用时间4990
LinkedList:迭代器遍历使用时间2
通过调整List的大小进行多测实验得出结论:实际上对于ArrayList无论采取哪一种遍历方式其速度都差不多,但对于LinkedList就不一样,并不支持随机访问,直接通过for的形式每次都会i++获取一个index,每次查找元素的时候都会重新遍历List,因此会遍历list.size()次list,所以特别耗时,因此对于LinkedList在实际生产中最好不要直接遍历,还是通过迭代器进行顺序遍历吧。


集合操作类Collections中的二叉搜索算法对实现RandomAccess接口的类的处理:
binarySearch API解释:
使用二叉搜索算法搜索指定对象的指定列表。 在进行此呼叫之前,列表必须根据指定的比较器(如sort(List, Comparator)方法)按升序排序。 如果没有排序,结果是未定义的。 如果列表包含与指定对象相等的多个元素,则不能保证将找到哪个元素。
该方法以log(n)时间运行“随机访问”列表(提供近常数位置访问)。 如果指定的列表没有实现RandomAccess接口并且很大,则该方法将执行基于迭代器的二进制搜索,执行O(n)链接遍历和O(log n)元素比较。

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);
    }

    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
    }

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

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

实际生成怎么用?根据集合操作类Collections来写啊,例如

if (list instanceof RandomAccess)
            //普通for遍历
else
			//迭代器遍历

猜你喜欢

转载自blog.csdn.net/QQB67G8COM/article/details/89707961