首頁 Java java教程 java初學者必備小抄

java初學者必備小抄

Dec 10, 2016 am 09:23 AM
java java初學者

在盡可能短的篇幅裡,將所有集合與並發集合的特徵,實現方式,性能捋一遍。適合所有”精通Java”其實還不那麼自信的人閱讀。

List

ArrayList

以數組實作。節約空間,但數組有容量限制。超出限制時會增加50%容量,用System.arraycopy()複製到新的數組,因此最好能給出數組大小的預估值。預設第一次插入元素時建立大小為10的陣列。

按數組下標存取元素–get(i)/set(i,e) 的效能很高,這是數組的基本優勢。

直接在數組末尾加入元素–add(e)的效能也高,但如果按標插入、刪除元素–add(i,e), remove(i), remove(e),則要用System. arraycopy()來移動部分受影響的元素,性能就變差了,這是基本劣勢。

LinkedList

以雙向鍊錶實作。鍊錶無容量限制,但雙向鍊錶本身使用了更多空間,也需要額外的鍊錶指標操作。

按下標訪問元素–get(i)/set(i,e) 要悲劇的遍歷鍊錶將指針移動到位(如果i>數組大小的一半,會從末尾移起)。

插入、刪除元素時修改前後節點的指標即可,但還是要遍歷部分鍊錶的指標才能移動到下標所指的位置,只有在鍊錶兩頭的操作–add(), addFirst(),removeLast( )或用iterator()上的remove()能省掉指標的移動。

CopyOnWriteArrayList

並發優化的ArrayList。用CopyOnWrite策略,在修改時先複製一個快照來修改,改完再讓內部指標指向新陣列。

因為對快照的修改對讀取操作來說不可見,所以只有寫鎖沒有讀鎖,加上複製的昂貴成本,典型的適合讀多寫少的場景。如果更新頻率較高,或數組較大時,還是Collections.synchronizedList(list),對所有操作用同一把鎖來確保線程安全更好。

增加了addIfAbsent(e)方法,會遍歷數組來檢查元素是否已存在,而效能可想像的不會太好。

補充

無論哪種實現,按值返回下標–contains(e), indexOf(e), remove(e) 都需遍歷所有元素進行比較,性能可想像的不會太好。

沒有依元素值排序的SortedList,在執行緒安全類別中也沒有無鎖定演算法的ConcurrentLinkedList,湊合著用Set與Queue中的等價類別時,會缺少一些List特有的方法。

Map

HashMap

以Entry[]陣列實作的雜湊桶數組,用Key的雜湊值取模桶數組的大小可得到數組下標。

插入元素時,如果兩條Key落在同一個桶(比如哈希值1和17取模16後都屬於第一個哈希桶),Entry用一個next屬性實作多個Entry以單向鍊錶存放,後入桶的Entry將next指向桶目前的Entry。

尋找雜湊值為17的key時,先定位到第一個雜湊桶,然後以鍊錶遍歷桶裡所有元素,逐一比較其key值。

當Entry數量達到桶數量的75%時(很多文章說使用的桶數量達到了75%,但看代碼不是),會成倍擴容桶數組,並重新分配所有原來的Entry,所以這裡也最好有個預估值。

取模用位元運算(hash & (arrayLength-1))會比較快,所以陣列的大小永遠是2的N次方, 你隨便給一個初始值例如17會轉為32。預設第一次放入元素時的初始值是16。

iterator()時順著哈希桶數組來遍歷,看起來是個亂序。

在JDK8裡,新增預設8的閥值,當一個桶裡的Entry超過閥值,就不以單向鍊錶而以紅黑樹來存放以加快Key的查找速度。

LinkedHashMap

擴充HashMap增加雙向鍊錶的實現,號稱是最佔記憶體的資料結構。支援iterator()時會依照Entry的插入順序來排序(但是更新不算, 如果設定accessOrder屬性為true,則所有讀寫存取都算)。

實作上是在Entry上再增加屬性before/after指針,插入時把自己加到Header Entry的前面去。如果所有讀寫存取都要排序,還要把前後Entry的before/after拼接起來以在鍊錶中刪除掉自己。

TreeMap

以紅黑樹實現,支援iterator()時按Key值排序,可依實現了Comparable介面的Key的升序排序,或由傳入的Comparator控制。可想的,在樹上插入/刪除元素的代價一定比HashMap的大。

支援SortedMap接口,如firstKey(),lastKey()取得最大最小的key,或sub(fromKey, toKey), tailMap(fromKey)剪取Map的某一段。

ConcurrentHashMap

並發優化的HashMap,預設16把寫鎖(可以設定更多),有效分散了阻塞的機率,而且沒有讀鎖。 
資料結構為Segment[],Segment裡面才是哈希桶數組,每個Segment一把鎖。 Key先算出它在哪個Segment裡,再算出它在哪個哈希桶裡。

支援ConcurrentMap接口,如putIfAbsent(key,value)與相反的replace(key,value)與以及實現CAS的replace(key, oldValue, newValue)。

沒有讀鎖定是因為put/remove動作是個原子動作(比如put是一個對數組元素/Entry 指標的賦值操作),讀取操作不會看到一個更新動作的中間狀態。

ConcurrentSkipListMap

JDK6新增的並發優化的SortedMap,以SkipList實作。 SkipList是紅黑樹的簡化替代方案,是個流行的有序集合演算法,篇幅所限見入門教學。 Concurrent套件選用它是因為它支援基於CAS的無鎖演算法,而紅黑樹則沒有好的無鎖演算法。

很特殊的,它的size()不能隨便調,會遍歷來統計。

補充

關於null,HashMap和LinkedHashMap是隨意的,TreeMap沒有設定Comparator時key不能為null;ConcurrentHashMap在JDK7裡value不能為null(這是為什麼呢?),JDK8裡key與value都不能為null ;ConcurrentSkipListMap是所有JDK裡key與value都不能為null。

Set

Set幾乎都是內部用一個Map來實現, 因為Map裡的KeySet就是一個Set,而value是假值,全部使用同一個Object。 Set的特徵也繼承了那些內部Map實作的特徵。

HashSet:內部是HashMap。 
LinkedHashSet:內部是LinkedHashMap。 
TreeSet:內部是TreeMap的SortedSet。 
ConcurrentSkipListSet:內部是ConcurrentSkipListMap的同時最佳化的SortedSet。 
CopyOnWriteArraySet:內部是CopyOnWriteArrayList的並發優化的Set,利用其addIfAbsent()方法實現元素去重,如前所述該方法的效能很一般。 
補充:好像少了個ConcurrentHashSet,本來也該有一個內部用ConcurrentHashMap的簡單實現,但JDK偏偏沒提供。 Jetty就自己封了一個,Guava則直接用java.util.Collections.newSetFromMap(new ConcurrentHashMap()) 實作。

Queue

Queue是在兩端出入的List,所以也可以用陣列或鍊錶來實現。

–普通隊列–

LinkedList

是的,以雙向鍊錶實現的LinkedList既是List,也是Queue。它是唯一允許放入null的Queue。

ArrayDeque

以循環數組實現的雙向Queue。大小是2的倍數,預設是16。

普通數組只能快速在末尾添加元素,為了支援FIFO,從數組頭快速取出元素,就需要使用循環數組:有隊頭隊尾兩個下標:彈出元素時,隊頭下標遞增;加入元素時,如果已到數組空間的末尾,則將元素循環賦值到數組0,同時隊尾下標指向0,再插入下一個元素則賦值到數組[1],隊尾下標指向1。如果隊尾的下標追上隊頭,表示數組所有空間都已用完,進行雙倍的數組擴容。

PriorityQueue

用二元堆實現的優先權隊列,不再是FIFO而是按元素實現的Comparable接口或傳入Comparator的比較結果來出隊,數值越小,優先權越高,越先出隊。但是注意其iterator()的回傳不會排序。

–線程安全的隊列–

ConcurrentLinkedQueue/ConcurrentLinkedDeque

無界的並發優化的Queue,基於鍊錶,實現了依賴CAS的無鎖定演算法。

ConcurrentLinkedQueue的結構是單向鍊錶和head/tail兩個指針,因為入隊時需要修改隊尾元素的next指針,以及修改tail指向新入隊的元素兩個CAS動作無法原子,所以需要的特殊的演算法,篇幅所限見入門教學。

PriorityBlockingQueue

無界的並發優化的PriorityQueue,也是基於二元堆。使用一把公共的讀寫鎖。雖然實作了BlockingQueue接口,其實沒有任何阻塞佇列的特徵,空間不夠時會自動擴容。

DelayQueue

內部包含一個PriorityQueue,同樣是無界的。元素需實現Delayed接口,每次調用時需返回當前離觸發時間還有多久,小於0表示該觸發了。 
pull()時會用peek()查看隊頭的元素,檢查是否到達觸發時間。 ScheduledThreadPoolExecutor用了類似的結構。

–線程安全的阻塞隊列–

BlockingQueue的隊列長度受限,用以確保生產者與消費者的速度不會相差太遠,避免記憶體耗盡。隊列長度設定後不可改變。當入隊時隊列已滿,或出隊時隊列已空,不同函數的效果見下表: 

java初學者必備小抄

ArrayBlockingQueue

定長的並發優化的BlockingQueue,基於循環數組實現。有一把公共的讀寫鎖與notFull、notEmpty兩個Condition管理佇列滿或空時的阻塞狀態。

LinkedBlockingQueue/LinkedBlockingDeque

可選定長的並發優化的BlockingQueue,基於鍊錶實現,所以可以把長度設為Integer.MAX_VALUE。利用鍊錶的特徵,分離了takeLock與putLock兩把鎖,繼續用notEmpty、notFull管理佇列滿或空時的阻塞狀態。

補充

JDK7有個LinkedTransferQueue,transfer(e)方法保證Producer放入的元素,被Consumer取走了再返回,比SynchronousQueue更好,有空要學習下。


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

創造未來:零基礎的 Java 編程 創造未來:零基礎的 Java 編程 Oct 13, 2024 pm 01:32 PM

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

See all articles