RandomAccess 這個空架子有何用?

在學習 Java 集合時, 最早學習的即是 List 中的 ArrayListLinkedList, 學習集合很關鍵的是學習其源碼, 瞭解底層實現方式, 那麼今天就講講 ArrayList 實現的一個接口 RandomAccessjava

好奇心的產生

查看 ArrayList 的源碼, 發現它實現了 RandomAccess 這個接口, 出於好奇點進去看看, 結果發現這接口是空的, 這固然引起了更大的好奇心:這空架子到底有何用?算法

深刻探究

JDK 官方文檔是不可少的工具, 先看看它是怎麼說的:RandomAccessList 實現所使用的標記接口,用來代表其支持快速(一般是固定時間)隨機訪問。此接口的主要目的是容許通常的算法更改其行爲,從而在將其應用到隨機或連續訪問列表時能提供良好的性能。數組

標記接口(Marker):這就說明了 RandomAccess 爲空的緣由,這個接口的功能僅僅起到標記的做用。dom

這不是與序列化接口 Serializable 差很少嗎? 只要你認真觀察, 其實不僅這一個標記接口, 實際上 ArrayList 還實現了另外兩個這樣的空接口:工具

Cloneable 接口 :實現了 Cloneable 接口,以指示 Object.clone() 方法能夠合法地對該類實例進行按字段複製。 若是在沒有實現 Cloneable 接口的實例上調用 Objectclone 方法,則會致使拋出 CloneNotSupportedException 異常。oop

Serializable 接口: 類經過實現 java.io.Serializable 接口以啓用其序列化功能。未實現此接口的類將沒法使其任何狀態序列化或反序列化。性能

繼續探討

標記接口都有什麼做用呢? 繼續討論 RandomAccess 的做用,其餘兩個在此不做討論。學習

若是 List 子類實現了 RandomAccess 接口,那表示它能快速隨機訪問存儲的元素, 這時候你想到的多是數組, 經過下標 index 訪問, 實現了該接口的 ArrayList 底層實現就是數組, 一樣是經過下標訪問, 只是咱們須要用 get() 方法的形式 , ArrayList 底層仍然是數組的訪問形式。測試

同時你應該想到鏈表, LinkedList 底層實現是鏈表, LinkedList 沒有實現 RandomAccess 接口,發現這一點就是突破問題的關鍵點。spa

數組支持隨機訪問, 查詢速度快, 增刪元素慢; 鏈表支持順序訪問, 查詢速度慢, 增刪元素快。因此對應的 ArrayList 查詢速度快,LinkedList 查詢速度慢, RandomAccess 這個標記接口就是標記可以隨機訪問元素的集合, 簡單來講就是底層是數組實現的集合。

爲了提高性能,在遍歷集合前,咱們即可以經過 instanceof 作判斷, 選擇合適的集合遍歷方式,當數據量很大時, 就能大大提高性能。

隨機訪問列表使用循環遍歷,順序訪問列表使用迭代器遍歷。

先看看 RandomAccess 的使用方式

import java.util.*;
public class RandomAccessTest {
    public static void traverse(List list){

        if (list instanceof RandomAccess){
            System.out.println("實現了RandomAccess接口,不使用迭代器");

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

        }else{
            System.out.println("沒實現RandomAccess接口,使用迭代器");

            Iterator it = list.iterator();
            while(it.hasNext()){
                System.out.println(it.next());
            }

        }
    }
    public static void main(String[] args) {
        List<String> arrayList = new ArrayList<>();
        arrayList.add("a");
        arrayList.add("b");
        traverse(arrayList);

        List<String> linkedList = new LinkedList<>();
        linkedList.add("c");
        linkedList.add("d");
        traverse(linkedList);
    }
}
複製代碼

下面咱們加入大量數據進行性能測試:

import java.util.*;
public class RandomAccessTimeTest {

    //使用for循環遍歷
    public static long traverseByLoop(List list){
        long startTime = System.currentTimeMillis();
        for (int i = 0;i < list.size();i++){
            list.get(i);
        }
        long endTime = System.currentTimeMillis();
        return endTime-startTime;
    }

    //使用迭代器遍歷
    public static long traverseByIterator(List list){
        Iterator iterator = list.iterator();
        long startTime = System.currentTimeMillis();
        while (iterator.hasNext()){
            iterator.next();
        }
        long endTime = System.currentTimeMillis();
        return endTime-startTime;
    }

    public static void main(String[] args) {
        //加入數據
        List<String> arrayList = new ArrayList<>();
        for (int i = 0;i < 30000;i++){
            arrayList.add("" + i);
        }
        long loopTime = RandomAccessTimeTest.traverseByLoop(arrayList);
        long iteratorTime = RandomAccessTimeTest.traverseByIterator(arrayList);
        System.out.println("ArrayList:");
        System.out.println("for循環遍歷時間:" + loopTime);
        System.out.println("迭代器遍歷時間:" + iteratorTime);

        List<String> linkedList = new LinkedList<>();
        //加入數據
        for (int i = 0;i < 30000;i++){
            linkedList.add("" + i);
        }
        loopTime = RandomAccessTimeTest.traverseByLoop(linkedList);
        iteratorTime = RandomAccessTimeTest.traverseByIterator(linkedList);
        System.out.println("LinkedList:");
        System.out.println("for循環遍歷時間:" + loopTime);
        System.out.println("迭代器遍歷時間:" + iteratorTime);
    }
}
複製代碼

結果:

ArrayList: for 循環遍歷時間: 3 迭代器遍歷時間: 7

LinkedList: for 循環遍歷時間: 2435 迭代器遍歷時間: 3

結論

根據結果咱們能夠得出結論: ArrayList 使用 for 循環遍歷優於迭代器遍歷 LinkedList 使用 迭代器遍歷優於 for 循環遍歷

根據以上結論即可利用 RandomAccess 在遍歷前進行判斷,根據 List 的不一樣子類選擇不一樣的遍歷方式, 提高算法性能。

學習閱讀源碼, 發現底層實現的精妙之處, 改變本身的思惟, 從每個小細節提高代碼的性能。

相關文章
相關標籤/搜索