RandomAccess接口的使用
RandomAccess在类Collections的shuffle()方法中的使用:(jdk源码如下)
1 /** 2 * Randomly permute the specified list using the specified source of 3 * randomness. All permutations occur with equal likelihood 4 * assuming that the source of randomness is fair.<p> 5 * 6 * This implementation traverses the list backwards, from the last element 7 * up to the second, repeatedly swapping a randomly selected element into 8 * the "current position". Elements are randomly selected from the 9 * portion of the list that runs from the first element to the current 10 * position, inclusive.<p> 11 * 12 * This method runs in linear time. If the specified list does not 13 * implement the {@link RandomAccess} interface and is large, this 14 * implementation dumps the specified list into an array before shuffling 15 * it, and dumps the shuffled array back into the list. This avoids the 16 * quadratic behavior that would result from shuffling a "sequential 17 * access" list in place. 18 * 19 * @param list the list to be shuffled. 20 * @param rnd the source of randomness to use to shuffle the list. 21 * @throws UnsupportedOperationException if the specified list or its 22 * list-iterator does not support the <tt>set</tt> operation. 23 */ 24 public static void shuffle(List<?> list, Random rnd) { 25 int size = list.size(); 26 if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) { 27 for (int i=size; i>1; i--) 28 swap(list, i-1, rnd.nextInt(i)); 29 } else { 30 Object arr[] = list.toArray(); 31 32 // Shuffle array 33 for (int i=size; i>1; i--) 34 swap(arr, i-1, rnd.nextInt(i)); 35 36 // Dump array back into list 37 ListIterator it = list.listIterator(); 38 for (int i=0; i<arr.length; i++) { 39 it.next(); 40 it.set(arr[i]); 41 } 42 } 43 }
由以上的jdk源码可见,在对实现list接口的对象进行洗牌,打乱时,区分了该类是否是RandomAccess的实例,这样做有什么意义呢?请继续向下看:
在jdk文档中对RandomAccess接口的定义如下:
/** * Marker interface used by <tt>List</tt> 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 * <tt>ArrayList</tt>) can produce quadratic behavior when applied to * sequential access lists (such as <tt>LinkedList</tt>). Generic list * algorithms are encouraged to check whether the given list is an * <tt>instanceof</tt> 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 <tt>List</tt> implementations * provide asymptotically linear access times if they get huge, but constant * access times in practice. Such a <tt>List</tt> implementation * should generally implement this interface. As a rule of thumb, a * <tt>List</tt> implementation should implement this interface if, * for typical instances of the class, this loop: * <pre> * for (int i=0, n=list.size(); i < 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}/../guide/collections/index.html"> * Java Collections Framework</a>. * */ public interface RandomAccess { }
List 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。
将操作随机访问列表的最佳算法(如 ArrayList )应用到连续访问列表(如 LinkedList )时,可产生二次项的行为。如果将某个算法应用到连续访问列表,那么在应用可能提供较差性能的算法前,鼓励使用一般的列表算法检查给定列表是否为此接口的一个 instanceof ,如果需要保证可接受的性能,还可以更改其行为。
现在已经认识到,随机和连续访问之间的区别通常是模糊的。例如,如果列表很大时,某些 List 实现提供渐进的线性访问时间,但实际上是固定的访问时间。这样的 List 实现通常应该实现此接口。
JDK中推荐的是对List集合尽量要实现RandomAccess接口
如果集合类是RandomAccess的实现,则尽量用for(int i = 0; i < size; i++) 来遍历而不要用Iterator迭代器来遍历,在效率上要差一些。反过来,如果List是Sequence List,则最好用迭代器来进行迭代。
JDK中说的很清楚,在对List特别是Huge size的List的遍历算法中,要尽量来判断是属于RandomAccess(如ArrayList)还是Sequence List (如LinkedList),因为适合RandomAccess List的遍历算法,用在Sequence List上就差别很大,常用的作法就是:
要作一个判断:
if (list instance of RandomAccess) { for(int m = 0; m < list.size(); m++){} }else{ Iterator iter = list.iterator(); while(iter.hasNext()){} }
1 package testrandomaccess; 2 3 import java.util.ArrayList; 4 import java.util.Iterator; 5 import java.util.LinkedList; 6 import java.util.List; 7 import java.util.RandomAccess; 8 9 /** 10 * 11 * @author bolong 12 */ 13 public class TestRandomAccess { 14 // 初始化列表 15 16 public static void initList(List list, int n) { 17 for (int i = 0; i < n; i++) { 18 list.add(i); 19 } 20 } 21 //使用循环进行对列表的迭代 22 23 public static void traverseWithLoop(List list) { 24 long starttime = 0; 25 long endtime = 0; 26 starttime = System.currentTimeMillis(); 27 for (int count = 0; count <= 1000; count++) { 28 for (int i = 0; i < list.size(); i++) { 29 list.get(i); 30 } 31 } 32 endtime = System.currentTimeMillis(); 33 System.out.println("使用loop迭代一共花了" + (endtime - starttime) + "ms时间"); 34 35 } 36 //使用迭代器对列表进行迭代 37 38 public static void traverseWithIterator(List list) { 39 long starttime = 0; 40 long endtime = 0; 41 starttime = System.currentTimeMillis(); 42 for (int count = 0; count <= 1000; count++) { 43 for (Iterator itr = list.iterator(); itr.hasNext();) { 44 itr.next(); 45 } 46 } 47 endtime = System.currentTimeMillis(); 48 System.out.println("使用Iterator迭代一共花了" + (endtime - starttime) + "ms时间"); 49 } 50 51 public static void traverse(List list) { 52 53 long starttime = 0; 54 long endtime = 0; 55 if (list instanceof RandomAccess) { 56 System.out.println("该list实现了RandomAccess接口"); 57 starttime = System.currentTimeMillis(); 58 for (int count = 0; count <= 1000; count++) { 59 for (int i = 0; i < list.size(); i++) { 60 list.get(i); 61 } 62 } 63 endtime = System.currentTimeMillis(); 64 System.out.println("迭代一共花了" + (endtime - starttime) + "ms时间"); 65 } else { 66 System.out.println("该list未实现RandomAccess接口"); 67 starttime = System.currentTimeMillis(); 68 for (int count = 0; count <= 1000; count++) { 69 for (Iterator itr = list.iterator(); itr.hasNext();) { 70 itr.next(); 71 } 72 } 73 endtime = System.currentTimeMillis(); 74 System.out.println("迭代一共花了" + (endtime - starttime) + "ms时间"); 75 } 76 } 77 78 public static void main(String[] args) { 79 ArrayList arraylist = new ArrayList(); 80 LinkedList linkedlist = new LinkedList(); 81 initList(arraylist, 1000); 82 initList(linkedlist, 1000); 83 traverse(arraylist); 84 traverse(linkedlist); 85 traverseWithIterator(arraylist); 86 traverseWithLoop(arraylist); 87 traverseWithIterator(linkedlist); 88 traverseWithLoop(linkedlist); 89 } 90 }
根据程序输出的结果的确证明了,arraylist等实现了RandomAccess接口的类在进行迭代时使用loop效率更高,而linkedList那些未实现该接口的类在进行迭代时使用Iterator进行迭代效率更高.