對啊我就是認定你不知道ArrayList為什麼要實現RandomAccess接口

2021-01-18 酷扯兒

本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫

在我們的開發中,List接口是最常見不過,而且我們幾乎每天都在用ArrayList或者LinkedList,但是細心的同學有沒有發現,ArrayList中實現了RandomAccess接口,而LinkedList卻沒有實現RandomAccess接口,這是為什麼呢?

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable

public class LinkedList<E>extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable

RandomAccess接口中是空的,RandomAccess接口又是什麼呢?

public interface RandomAccess {}

RandomAccess接口

RandomAccess是一個標記接口,官方解釋是只要List實現這個接口,就能支持快速隨機訪問。而什麼是隨機訪問呢?接下來我們來舉例說明。

Collections是集合的一個工具類,我們看一下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方法。接下來看一下這兩個方法。

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}

上述兩個方法的源碼表示,實現了RandomAccess接口的List使用索引遍歷,而未實現RandomAccess接口的List使用迭代器遍歷。那麼為什麼要這麼設計呢?既然涉及到二分搜索的遍歷操作,那麼現在我們來分析一下ArrayList和LinkedList遍曆元素的性能如何?

public class CollectionTest {public static void main(String[] args){ long arrayListIndexedTime = arrayListIndexed(); long arrayListIteratorTime = arrayListIterator(); long linkedListIndexedTime = linkedListIndexed(); long linkedListIteratorTime = linkedListIterator(); System.out.println("測試ArrayList通過for遍歷所消耗時間:" + arrayListIndexedTime); System.out.println("測試ArrayList通過iterator遍歷所消耗時間:" + arrayListIteratorTime); System.out.println("測試LinkedList通過for遍歷所消耗時間:" + linkedListIndexedTime); System.out.println("測試LinkedList通過iterator遍歷所消耗時間:" + linkedListIteratorTime); } //測試ArrayList通過for遍歷所消耗時間 public static long arrayListIndexed() { List<Integer> arrayList = new ArrayList<>(); for (int i = 0; i < 10000; i++) { arrayList.add(i); } //記錄開始時間 long startTime = System.currentTimeMillis(); for (int i = 0; i < arrayList.size(); i++) { arrayList.get(i); } //記錄結束時間 long endTime = System.currentTimeMillis(); //遍歷消耗時間 long resultTime = endTime - startTime; return resultTime; } //測試ArrayList通過iterator遍歷所消耗時間 public static long arrayListIterator() { List<Integer> arrayList = new ArrayList<>(); for (int i = 0; i < 10000; i++) { arrayList.add(i); } //記錄開始時間 long startTime = System.currentTimeMillis(); Iterator<Integer> iterator = arrayList.iterator(); while (iterator.hasNext()) { iterator.next(); } //記錄結束時間 long endTime = System.currentTimeMillis(); //遍歷消耗時間 long resultTime = endTime - startTime; return resultTime; } //測試LinkedList通過for遍歷所消耗時間 public static long linkedListIndexed() { List<Integer> linkedList = new LinkedList<>(); for (int i = 0; i < 10000; i++) { linkedList.add(i); } //記錄開始時間 long startTime = System.currentTimeMillis(); for (int i = 0; i < linkedList.size(); i++) { linkedList.get(i); } //記錄結束時間 long endTime = System.currentTimeMillis(); //遍歷消耗時間 long resultTime = endTime - startTime; return resultTime; } //測試LinkedList通過iterator遍歷所消耗時間 public static long linkedListIterator() { List<Integer> linkedList = new LinkedList<>(); for (int i = 0; i < 10000; i++) { linkedList.add(i); } //記錄開始時間 long startTime = System.currentTimeMillis(); Iterator<Integer> iterator = linkedList.iterator(); while (iterator.hasNext()) { iterator.next(); } //記錄結束時間 long endTime = System.currentTimeMillis(); //遍歷消耗時間 long resultTime = endTime - startTime; return resultTime; }}

測試結果如下

測試ArrayList通過for遍歷所消耗時間:1測試ArrayList通過iterator遍歷所消耗時間:2測試LinkedList通過for遍歷所消耗時間:47測試LinkedList通過iterator遍歷所消耗時間:1

我們來分析一下測試結果:ArrayList通過for遍歷比通過iterator遍歷要稍快,LinkedList通過iterator遍歷比通過for遍歷要快。

所以說在我們的應用中,要考慮使用List接口的哪種實現類,可以更好更高效地滿足實際場景需求。所以在這裡通過實現RandomAccess接口來區分List的哪種實現類。

總結

最後總結一句話:實現RandomAccess接口的List可以通過for循環來遍歷數據比使用iterator遍歷數據更高效,未實現RandomAccess接口的List可以通過iterator遍歷數據比使用for循環來遍歷數據更高效。

相關焦點

  • 「雷電」接口的「邪惡女僕」攻擊丨專欄
    我最近還想找時間再回顧一下《007》系列呢!大東:我也很喜歡詹姆斯·邦德這個特工形象,你最喜歡特工身上的哪個能力?小白:有好多能力我都很羨慕!但是看起來都不太現實,比如說飛簷走壁之類的。對了,東哥,好萊塢電影裡,無論電腦加密水平多麼高,黑客只需幾分鐘就能拷貝走你電腦裡所有的東西,現實中可以辦到嗎?
  • INNERSECT上 陳星如在11StreetLab玩得很random
    Q:  你的風格最初是受什麼影響,有沒有考慮過轉變風格,或者說,一路走來,有沒有已經發生的轉變?A: 從我上大學開始,那個時候我在音樂學院,我的風格受我喜歡的音樂類型影響較大,所以那個時候大家說我是復古女王,可能因為我比較多聽一些英倫的東西,喜歡英倫搖滾、獨立搖滾、朋克。
  • Math.random() 還能這樣玩?
    Math.random 除了上述的應用場景之外,還可以應用在遊戲、動畫、隨機數據、生成音樂或藝術圖片等場景。好的,廢話不多說,接下來我們馬上來一起感受一下 Math.random 的魅力。霓虹燈六角形粒子動畫生成音樂
  • 四級核心詞彙:access,crawl,maintain,你知道是什麼意思嗎?
    嗨,大家好,今天為大家介紹三個單詞:access,crawl,maintain,話不多說,讓我們開始吧!accessaccess是個名詞,它的第一個意思是「the opportunity or right to use sth or to see sb/sth」,即「接觸,接近(的機會)」。當access為這個意思時,等同於單詞「approach」。關於這個意思,有一個很常用的短語,那就是have access to。
  • Python 實現抖音上的「人像動漫化」特效,原來這麼簡單!
    前幾天,女友拉著我和她玩兒抖音,就是這個人像動漫化的操作,頓時覺得很好玩兒。我心想:python既然這麼強大,是不是也可以使用python程序來實現這樣一個操作呢?哈哈!我自己當然是沒有這個本事編寫這樣一個牛逼的程序出來,但是百度可以呀,並且還很好用。百度AI開放平臺給我們提供了完整的接口,甚至貼心的將代碼都給我們寫好了。這些接口還支持很多主流語言都呢,像Java、Python、PHP、C#等,我們做的就是直接調用它即可。效果怎麼樣呢?我們先來看看下方的對比圖吧。
  • 我要告訴你:java接口中可以定義private私有方法
    在傳統的Java編程中,被廣為人知的一個知識點是:java Interface接口中不能定義private私有方法。只允許我們定義public訪問權限的方法、抽象方法或靜態方法。但是從Java 9 開始,Interface 接口中允許定義私有方法和私有靜態方法。下面我們就來為大家介紹其語法規則,和為什麼要有這樣的設計。
  • 《面試又翻車了》這次竟然和 Random 有關?
    面試問題既然已經有了 Random 為什麼還需要 ThreadLocalRandom?正文Random 是使用最廣泛的隨機數生成工具了,即使連 Math.random() 的底層也是用 Random 實現的 源碼如下:Math.random()Math.random()Random.nextDouble
  • 如何使用測試號實現第三方授權登錄(PC)
    一、實現說明首先,經過摸索,明確的一點就是,測試號可以實現授權,這樣移動端網站登錄是比較簡單實現的。直接讓用戶發送一條連接然後回調,根據code拿到access_token和openId,根據access_token和openId拿到用戶信息,這樣你的網站就可以為所欲為了。
  • 實現階段3的DMVPN,需要匯總路由
    vlan 20 //將SW的e0/1接口劃入VLAN 20SW1#show run int e0/2interface Ethernet0/2switchport access vlan 10 //將SW的e0/1接口劃入VLAN 10
  • springboot+springsecurity實現前後端分離簡單實現!
    通過各種方式學習springsecurity,在B站、騰訊課堂、網易課堂、慕課網沒有springsecurity的前後端分離的教學視頻,那我就去csdn去尋找springsecurity博客,發現幾個問題:要麼就是前後端不分離,要麼就是通過內存方式讀取數據,而不是通過資料庫的方式讀取數據,要麼就是大佬們給的代碼不全、把代碼講的太繞,關鍵部分沒有注釋
  • M.2接口的顯卡你見過嗎?笑談電腦接口非常規用途
    這種接口我就是以前做華擎X99主板的時候接觸過,但是僅僅見過接口,沒用過產品。這個接口一端是方形兩排金手指的,辨識度很高,貌似就是伺服器市場有,家用一直沒消息,估計涼了。AMD和VIA聯合搞出來super7平臺,就是在socket7平臺基礎上面加入了AGP接口以及高速SDRAM和高速Ultra DMA並口硬碟的支持,並且AMD還研發出新架構的K7系列。正是由於Intel逼急了AMD,逼著AMD發奮圖強,要不一輩子醉生夢死在兼容Intel的CPU之中,估計現在我們還在用雙核的cpu。
  • 極速鯊課堂74:Type-C接口都有啥功能你知道嗎?
    如今越來越多的手機、筆記本、平板電腦等設備都選擇搭載Type-C接口,為什麼這種接口會受到市場青睞,它究竟有什麼好處呢?今天的極速鯊課堂就和大家聊聊Type-C接口。01 Type-C接口是什麼?飛利浦279C9從接口的外觀來看,Type-C最大的特點就是不分正反。熟悉DIY硬體的玩家都知道,無論是CPU、內存條、還是顯卡、硬碟接口,都具備「防呆口」的造型設計,來防止接口反插出現無法開機的情況。我們日常最常使用的USB-A類型接口也有防呆設計,但是由於接口體積較小,大力出奇蹟的情況時有發生,USB Type-C接口則從源頭解決了這一問題。
  • 腦機就可實現「永生」?「大佬」質疑腦機接口黑科技安全性
    馬斯克為了讓公眾更直觀地感受到這樣全新的黑科技,他在演示現場還展示了3隻實驗小豬:一隻在兩個月前被植入了腦機接口設備,一隻未植入任何設備,還有一隻曾植入過腦機接口設備、後被取出。當植入了設備的小豬的口鼻碰觸到物體時,腦機接口設備會獲取神經元發射的信號,在顯示屏上呈現點狀圖像並發出聲音,這顯示它的大腦信號可被實時採集。而植入設備後又取出的小豬表現得非常健康,與普通小豬並無差異。
  • 這是要逆天嗎?使用MHL接口實現手機連電視
    MHL 在傳輸的過程中使得電視省去了解壓縮的環節,這些信號可以原封不動的傳輸給高畫質電視並播放出來,支持多種格式的信號,質量不受到任何損耗。MHL接口優勢 第一,MHL接口夠將行動裝置中的視頻信號與音頻信號同時傳輸到外接顯示設備上,不需要經過任何的設置,對於用戶來說非常便利。
  • 從頭再來:Java中的接口(接口可以是private的嗎?)
    Java的接口Java中可以定義一個私有接口嗎?答案是肯定的。那為什麼會用到私有的接口?回答這個問題之前先看看Java中的接口Java的接口接口提供了一種接口和實現分離的方法,在Java中通過關鍵字interface定義,接口中的方法都是抽象的(沒有實現體)。接口在現實中使用的太多了,比如插座的接口,手機的充電接口,耳機接口。接口只定義了協議,實現者只要實現這個接口定義即可。
  • TypeScript(四)接口interface使用詳解
    」前言二:接口是TypeScript中一個非常重要的概念,在其他很多語言中已經有了並且被大量使用。本來這個章節我打算更新其他的內容,但是發現很多東西都依賴接口,所以我們先來學習一下接口相關的知識吧!」一. 為什麼要使用接口1.1.
  • 我說不出來為什麼愛你,但我知道,你就是我不愛別人的理由
    我說不出來為什麼愛你,但我知道,你就是我不愛別人的理由1:這個世界根本不存在「不會」這回事,當你失去所有依靠的時候,你自然就什麼都會了。苦難是豎在現實和未來之間的一扇紙糊的門,你只要敢於捅破它,前方便會一路坦途暢通無阻。
  • 為什麼現在4K超高畫質電視都要支持HDMI2.0接口?
    但是很多消費者沒有注意的是,在不同廠家的液晶電視宣傳中,都很少有會提到HDMI這個接口的事情,不過你會發現,所有的4K超高畫質電視都會提供這個接口,那對於我們用戶來說這個HDMI接口為什麼都成了標配了呢?它有什麼意義呢?首先,我們的消費者需要了解的是:HDMI是什麼意思?
  • 內涵段子:我要吐槽我女友,簡直就是泰迪精啊!
    ,我不起床! ,簡直就是泰迪精啊!也就是說不管你以前有多麼的直到最後彎了,就不可能直回來啦。誰知女友直接接口:一看就是苦逼孩子,靠著兩座山混飯吃的!哪像我媽資金充足,直接給我修了座機場。我:………酒桌上,舉起酒杯,非要拜把子,做忘年之交,閨蜜急了~~大聲喊到,「爸,你幹嘛?」閨蜜老爸說到,「閨女,這個兄弟我交定了,你不能嫁,我們經常一起去澡堂子洗澡,知根知底,他給不了你幸福的。」