RandomAccess接口

序言:许多人看完,ArrayList源码后,自我感受良好,一问 RandomAccess 这玩意干吗的,一脸懵,java

因此今天来盘盘这个接口算法

RandomAccess接口的做用

咱先看看官方怎么介绍这个接口的,摘自注释数组

译:这个接口是被用来List实现的标记接口,支持快速随机访问,且只要你实现了它,你使用for循环遍历,效率会高于迭代器的遍历(说明一下,这里说的 for 循环是普通循环,而 加强 for-each 本质就等同于 迭代器遍历)数据结构

//避免自动装箱拆箱影响,不声明泛型
        List list = new ArrayList<>();
        int total = 40000000;
        for (int i = 0; i<total; i++){
            list.add(i);
        }
        //1.使用普通for循环的效率
        long start1 = System.currentTimeMillis();
        for(int i = 0; i < total; i++){
            Object temp  = list.get(i);
        }
        long end1 = System.currentTimeMillis();
        System.out.println("普通循环的时间效率:" + (end1 - start1));
        //2.使用迭代器的循环效率
        long start2 = System.currentTimeMillis();
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            Object temp = iterator.next();
        }
        long end2 = System.currentTimeMillis();
        System.out.println("迭代器循环的时间效率:" + (end2 - start2));
        //3.使用加强for循环(其实底层也是用迭代器玩的)
        long start3 = System.currentTimeMillis();
        for(Object num: list){
            Object temp = num;
        }
        long end3 = System.currentTimeMillis();
        System.out.println("加强for循环的时间效率:" + (end3 - start3));

这里的随机访问,就是可以随机的访问 List 中的任何一个元素,不要想多dom

虽然全部的 List 实现 都支持随机访问,只是因为数据结构不一样,致使访问效率不一样。可是这里的快速随机访问,不是全部 List 集合能干的。测试

  • ArrayList,底层为数组,有下标,指哪打哪,随机访问效率 O(1)
  • LinkedList,底层为链表,访问一个元素,须要遍历,随机访问效率 O(n)

因此 ArrayList 实现了 RandomAccess,LinkedList 则没有code

实现了 RandomAccess 的接口有:对象

  • ArrayList
  • Vector
  • CopyOnWriteArrayList
  • RandomAccessSubList
  • UnmodifiableArrayList

可能你看到这,又有疑问了,我知道这个接口是标志接口了,实现了它就能快速随机访问,因此它有什么用 ?blog

在上文中,咱们经过测试发现只要实现了这个接口,普通 for 循环的 效率要高于迭代器,因此你可能会说在追求效率的时候我全用 普通 for循环 就好了,这个接口的做用仍是没有凸显出来。接口

那么下面咱们看这样一个测试, 此次测试对象为 LinkedList。

//注意此次咱们使用双线链表LinkedList
        List list = new LinkedList();
        int total = 100000;
        for (int i = 0; i<total; i++){
            list.add(i);
        }
        //1.使用普通for循环的效率
        long start1 = System.currentTimeMillis();
        for(int i = 0; i < total; i++){
            Object temp  = list.get(i);
        }
        long end1 = System.currentTimeMillis();
        System.out.println("普通循环的时间效率:" + (end1 - start1));
        //2.使用迭代器的循环效率
        long start2 = System.currentTimeMillis();
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            Object temp = iterator.next();
        }
        long end2 = System.currentTimeMillis();
        System.out.println("迭代器循环的时间效率:" + (end2 - start2));

 

明白了不,不一样 List 集合 使用不一样的遍历方式,效率完彻底全不同,不是使用谁效率就必定高,得看对象是谁

因此若是你有这么个诉求,你有个List 对象 A,可是它多是 LinkedList,多是ArrayList,也多是 CopyOnWriteArrayList,可是你就想它是以最高效率去遍历的,这个时候你能够根据这个RandomAccess 去决定以哪一种方式去遍历

if(A instanceof RandomAccess){
    //使用普通for循环遍历
} else {
    //使用迭代器遍历
}

 

算法的差别

上文咱们提到有没有实现 RandomAccess接口,会致使不一样的集合采起不一样的遍历方式,会有不同的访问效率。可是为何会这样呢,底层是怎么作的

咱们看一下 java.util.Collections#binarySearch

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

咱们能够看到,在进行二分查找时,会进行判断是否实现了 RandomAccess接口 从而采起不同的遍历方式

因此看到这你应该明白了,数据结构决定了算法的根本,RandomAccess接口 仅仅是个标志接口

不只是二分查找,底层的普通遍历也会根据其数据结构选择相应的执行策略,选对了和数据结构相性匹配的策略固然就快了

总结:数据结构决定算法

相关文章
相关标签/搜索