八、集合類Vector、ArrayList、LinkedList

Verctor 是 Java 早期提供的線程安全動態數組,若是不須要線程安全,並不建議選擇,畢竟同步是有額外開銷的.Vector是基於synchronized實現的線程安全的ArrayList。Vector 內部是使用對象數組來保存數據,能夠根據須要自動的增長容量,當數組已滿時,會建立新的數組,並拷貝原有數組數據。Vector,默認建立一個大小爲10的Object數組,並將capacityIncrement設置爲0;當插入元素數組大小不夠時,若是capacityIncrement>0,則將Object數組的大小擴大爲現有size+capacityIncrement;若是capacityIncrement<=0,則將Object數組的大小擴大爲現有大小的2倍java

ArrayList 是應用更加普遍的動態數組實現,它自己不是線程安全的,因此性能要好不少。與 Vector 近似,ArrayList 也是能夠根據須要調整容量,不過二者的調整邏輯有所區別,Vector 在擴容時會提升 1 倍,而 ArrayList 則是增長 50%。ArrayList在執行插入元素是超過當前數組預約義的最大值時,數組須要擴容,擴容過程須要調用底層System.arraycopy()方法進行大量的數組複製操做;在刪除元素時並不會減小數組的容量(若是須要縮小數組容量,能夠調用trimToSize()方法);在查找元素時要遍歷數組,對於非null的元素採起equals的方式尋找。面試

LinkedList 顧名思義是 Java 提供的雙向鏈表,因此它不須要像上面兩種那樣調整容量,它也不是線程安全的。它也能夠被看成堆棧、隊列或雙端隊列進行操做。算法

 

Vector 和 ArrayList 做爲動態數組,其內部元素以數組形式順序存儲的,因此很是適合隨機訪問的場合。除了尾部插入和刪除元素,每每性能會相對較差,好比咱們在中間位置插入一個元素,須要移動後續全部元素。而 LinkedList 以鏈表形式存儲的,進行節點插入、刪除卻要高效得多,可是隨機訪問性能則要比動態數組慢。編程

 

排序算法須要熟知:數組

內部排序,至少掌握基礎算法如歸併排序、交換排序(冒泡、快排)、選擇排序、插入排序等。安全

外部排序,掌握利用內存和外部存儲處理超大數據集,至少要理解過程和思路。併發

考察算法不只僅是如何簡單實現,面試官每每會刨根問底,好比哪些是排序是不穩定的呢(快排、堆排),或者思考穩定意味着什麼;對不一樣數據集,各類排序的最好或最差狀況;從某個角度如何進一步優化(好比空間佔用,假設業務場景須要最小輔助空間,這個角度堆排序就比歸併優異)等,從簡單的瞭解,到進一步的思考,面試官一般還會觀察面試者處理問題和溝通時的思路。框架

 

Java 的集合框架,Collection 接口是全部集合的根,而後擴展開提供了三大類集合,分別是:函數

  • List,也就是咱們前面介紹最多的有序集合,它提供了方便的訪問、插入、刪除等操做。工具

  • Set,Set 是不容許重複元素的,這是和 List 最明顯的區別,也就是不存在兩個對象 equals 返回 true。咱們在平常開發中有不少須要保證元素惟一性的場合。

  • Queue/Deque,則是 Java 提供的標準隊列結構的實現,除了集合的基本功能,它還支持相似先入先出(FIFO, First-in-First-Out)或者後入先出(LIFO,Last-In-First-Out)等特定行爲。這裏不包括 BlockingQueue,由於一般是併發編程場合,因此被放置在併發包裏。

 每種集合的通用邏輯,都被抽象到相應的抽象類之中,好比 AbstractList 就集中了各類 List 操做的通用部分。這些集合不是徹底孤立的,好比,LinkedList 自己,既是 List,也是 Deque 。

 TreeSet 代碼裏實際默認是利用 TreeMap 實現的,Java 類庫建立了一個 Dummy 對象「PRESENT」做爲 value,而後全部插入的元素實際上是以鍵的形式放入了 TreeMap 裏面;同理,HashSet 其實也是以 HashMap 爲基礎實現的,原來他們只是 Map 類的馬甲!

TreeSet 支持天然順序訪問,可是添加、刪除、包含等操做要相對低效(log(n) 時間)。

HashSet 則是利用哈希算法,理想狀況下,若是哈希散列正常,能夠提供常數時間的添加、刪除、包含等操做,可是它不保證有序。

LinkedHashSet,內部構建了一個記錄插入順序的雙向鏈表,所以提供了按照插入順序遍歷的能力,與此同時,也保證了常數時間的添加、刪除、包含等操做,這些操做性能略低於 HashSet,由於須要維護鏈表的開銷。

在遍歷元素時,HashSet 性能受自身容量影響,因此初始化時,除非有必要,否則不要將其背後的 HashMap 容量設置過大。而對於 LinkedHashSet,因爲其內部鏈表提供的方便,遍歷性能只和元素多少有關係。

 

 

除了 java.util.concurrent 裏面的線程安全容器,在 Collections 工具類中,提供了一系列的 synchronized 方法返回線程安全的同步列表對象,如:

List list = Collections.synchronizedList(new ArrayList());

它的實現,基本就是將每一個基本方法,好比 get、set、add 之類,都經過 synchronizd 添加基本的同步支持,很是簡單粗暴,但也很是實用。注意這些方法建立的線程安全集合,都符合迭代時 fail-fast 行爲,當發生意外的併發修改時,儘早拋出 ConcurrentModificationException 異常,以免不可預計的行爲。

 

 Java 提供的默認排序算法:

 須要區分是 Arrays.sort() 仍是 Collections.sort() (底層是調用 Arrays.sort());什麼數據類型;多大的數據集(過小的數據集,複雜排序是不必的,Java 會直接進行二分插入排序)等。

對於基本數據類型,目前使用的是所謂雙軸快速排序(Dual-Pivot QuickSort),是一種改進的快速排序算法,早期版本是相對傳統的快速排序。

對於引用數據類型,目前則是使用TimSort,思想上也是一種歸併和二分插入排序(binarySort)結合的優化排序算法。TimSort 並非 Java 的首創,簡單說它的思路是查找數據集中已經排好序的分區(這裏叫 run),而後合併這些分區來達到排序的目的。

Java 8 引入了並行排序算法(直接使用 parallelSort 方法),這是爲了充分利用現代多核處理器的計算能力,底層實現基於 fork-join 框架,當處理的數據集比較小的時候,差距不明顯,甚至還表現差一點;可是,當數據集增加到數萬或百萬以上時,提升就很是大了,具體仍是取決於處理器和系統環境。

排序算法仍然在不斷改進,最近雙軸快速排序實現的做者提交了一個更進一步的改進,歷時多年的研究,目前正在審覈和驗證階段。根據做者的性能測試對比,相比於基於歸併排序的實現,新改進能夠提升隨機數據排序速度提升 10%~20%。

在 Java 8 之中,Java 平臺支持了 Lambda 和 Stream,相應的 Java 集合框架也進行了大範圍的加強,以支持相似爲集合建立相應 stream 或者 parallelStream 的方法實現,咱們能夠很是方便的實現函數式代碼。閱讀 Java 源代碼,你會發現,這些 API 的設計和實現比較獨特,它們並非實如今抽象類裏面,而是以默認方法的形式實如今 Collection 這樣的接口裏!這是 Java 8 在語言層面的新特性,容許接口實現默認方法,理論上來講,咱們原來實如今相似 Collections 這種工具類中的方法,大多能夠轉換到相應的接口上。

在 Java 9 中,Java 標準類庫提供了一系列的靜態工廠方法,好比,List.of()、Set.of(),大大簡化了構建小的容器實例的代碼量。而且集合實例都是容量很是有限的,並且在生命週期中並不會進行修改。

之前這麼寫:

ArrayList<String>  list = new ArrayList<>();
list.add("Hello");
list.add("World");

利用新的容器靜態工廠方法,一句代碼就夠了,而且保證了不可變性。如今能夠這麼寫:

List<String> simpleList = List.of("Hello","world");

更進一步,經過各類 of 靜態工廠方法建立的實例,還應用了一些咱們所謂的最佳實踐,好比,它是不可變的,符合咱們對線程安全的需求;它由於不須要考慮擴容,因此空間上更加緊湊等。

 

 

java堆結構PriorityQueue徹底解析:

PriorityQueue隊列,是基於最小堆原理實現。http://www.javashuo.com/article/p-bbbigpdd-ne.html

PriorityQueue隊列不適合進場出隊入隊的頻繁操做,可是他的優先級特性很是適合一些對順序有要求的數據處理場合。且非線程安全的。

相關文章
相關標籤/搜索