【J2SE】java編程思想之數組與集合學習總結

數組

簡述

數組是一種效率最高的存儲和隨機訪問對象引用的一個簡單的線性序列,雖然訪問快速,但爲之付出的代價是數組的大小固定,而且在其生命週期中不可改變。數組與其餘容器之間的區別在於:效率、類型和保存基本類型的能力。但隨着自動包裝機制的出現,容器已經能夠與數組幾乎同樣方便,而數組僅存的優勢就是效率。java

應該 「優先選擇容器而不是數組」,只有在已經證實性能稱爲問題(而且切換到數組對性能提升有所幫助)時,你才應該將程序重構爲使用數組

數組標識符就是一個引用,用以保存指向其餘對象的引用,數組的一個單元指向在堆中建立的一個真實對象。對像數組與基本類型數組的區別在於,對象保存的是引用,基本類型數組保存的是基本類型的值。編程

數組的 length屬性,表示數組的大小,而不是實際保存的元素個數。新生成的對象數組,會被默認的初始化爲 null,基本類型數組則會初始化爲對應基本類型的默認值,對於對象數組,能夠檢測其中的引用是否爲 null,進而肯定某個位置是否存在對象。當數組不被須要時,垃圾回收器會負責清理數組,不然將一直存在。數組

實用數組方法

  • System.arraycopy():複製一個數組比用 for 循環複製要快得多,且該方法對全部類型作了重載。當複製對象數組時,只是複製對象的引用(屬淺拷貝)。
  • Arrays.asList(T... a):將多個元素轉換成 List 列表,底層表示的仍是數組,當嘗試進行 add()delete()操做時將在運行時報錯。
  • Arrays.sort(T[] a, Comparator<? super T> c):按照給定的比較器對指定的數組進行排序。
  • Arrays.binarySearch(T[] a,T key, Comparator<? super T> c):對一個有序的數組採用二分查找獲取對象下標,若是數組存在重複元素,可能返回的不精確。<span color="red">注:若是使用了Comparator排序了一個對象數組,則調用此方法時傳入的Comparator必須是同一個。</span>

Comparator比較器

comparator用於指定元素的排列順序,在常見的數據類型中已經被默認實現,對於須要按照某種規則進行排序的時候,提供Comparator或者實現 java.lang.Comparable接口。安全

class DemoComparator implements Comparator<Demo> {

    @Override
    public int compare(Demo o1, Demo o2) {
        if (o1.value == o2.value) {
            return 0;
        }
        return o1.value < o2.value ? 1 : -1;
    }
    
}

容器

Collection

一個獨立元素的序列,這些元素都服從一條或多條規則,提供了存放一組對象的方式。List必須按照插入的順序保存元素,而Set不能有重複元素。Queue按照隊列排隊規則來肯定對象產生的順序。數據結構

PriorityQueue:優先級隊列,聲明下一個彈出最須要的元素(具備最高的優先級),經過默認排序或提供 Comparator。當調用 peek()poll()remove()方法時,獲取的元素是隊列中優先級最高的。多線程

List

描述
ArrayList 經常使用於隨機訪問元素,可是在 List的中間插入和刪除元素時較慢。
LinkedList 對中間插入和刪除元素的操做中提供了優化的順序列表,在隨機訪問方面相對比較慢,可是它的特性集較 ArrayList更大。

ListIterator:Iterator的子類,只用於對各類 List 類的訪問,能夠實現雙向移動。hasNext() / next()hasPrevious() / previous()ide

對於隨機訪問的 get/set操做,背後有數組支持的 List 比 ArrayList 稍快一點,可是對於 LinkedList,將會變得很慢,由於它不是針對隨機訪問操做而設計的。

最佳的作法是將 ArrayList 做爲默認首選,當由於常常插入和刪除操做而性能降低時,纔去選擇 LinkedList。若是使用的是固定數量的元素,既能夠選擇List,也能夠選擇數組。性能

Set

描述
Set (interface) 存入Set的每個元素都必需要惟一,由於Set不容許重複元素。加入Set的元素必須定義 equals()方法以確保對象的惟一性。Set 和 Collection 有徹底同樣的接口。 Set 接口不保證維護元素的次序。
HashSet 爲快速查找而設計的Set。存入 HashSet的元素必須定義 hashCode()
TreeSet 保持次序的 Set,底層爲數據結構。使用它能夠從 Set中提取有序的序列。元素必須實現 Comparator接口。
LinkedHashSet 具備 HashSet的查詢速度,且內部使用鏈表維護元素的順序(插入的次序)。因而在使用迭代遍歷 Set時,結果會按元素插入的次序顯示。元素必須定義 hashCode()方法。
HashSet以某種順序保存元素,LinkedHashSet按照元素插入的順序保存元素,而 TressSet按照排序維護元素。

HashSet的性能基本上老是比 TreeSet好,特別在添加和查詢元素時。 TreeSet存在的惟一緣由是它維持元素的排序順序,所以迭代的時候,TreeSet一般比用HashSet要快。優化

Map

Map的各個實現類的行爲特性的不一樣在於,效率、鍵值對的保存及呈現次序、對象的保存週期、映射表如何在多線程程序中工做和斷定 」鍵「等價的策略等方面。spa

描述
HashMap Map基於散列表的實現(取代HashTable)。插入和查詢「鍵值對」的開銷是固定的。能夠經過構造器設置容量和負載因子,以調整容器的性能。
LinkedHashMap 相似於HashMap,可是迭代遍歷它時,取得 「鍵值對」 的順序就是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一點;而在迭代訪問時反而快,由於它使用鏈表維護內部次序。
TreeMap 基於紅黑樹的實現。查看 「鍵」 或「鍵值對」時,它們會被排序。TreeMap的特色在於,所獲得的的結果是通過排序的。TreeSet是惟一一個帶有 subMap()方法的Map,它能夠返回一個子樹。
WeekHashMap 弱鍵映射,容許釋放映射所指向的對象,這是爲解決某類特殊問題而設計的。若是映射 以外沒有引用指向某個「鍵」,則此「鍵」能夠被垃圾收集器回收。
ConcurrentHashMap 一種線程安全的Map,它不涉及同步加鎖。
IdentityHashMap 使用 ==代替equals()對「鍵」進行比較的散列映射。專爲解決特殊問題而設計的
使用Map時,首選HashMap,TreeMap維護着散列數據結構的同時還要維護鏈表,在迭代的時候速度快,其他方面比HashMap要慢。

插入操做隨着Map尺寸的變大,速度會明顯變慢(除了 IdentityHashMap),可是查找所耗費的代價比插入小不少。對於插入操做,因爲維護鏈表所帶來的的開銷,致使LinkedHashSet 比 HashSet的代價高不少。

HashMap的性能因子:

  • 容量:表中的桶位數。
  • 初始容量:表在建立時所擁有的桶位數。
  • 尺寸:表中當前存儲的項數。
  • 負載因子:尺寸/容量。 空表的負載因子爲 0,半滿表爲 0.5。負載越輕的表產生的衝突性越小,HashMap默認負載因子爲 0.75,達到默認值時,將進行散列。

散列與散列碼

散列碼是一個無符號的十六進制數,散列的目的在於使用一個對象來查找另外一個對象;散列的價值在於可以快速進行查詢。

在Map上的實現:經過鍵對象生成一個數字,這個數字就是散列碼。將散列碼做爲數組的下標,該數組的一個單元指向一個鏈表,鏈表上的一個節點就是一個 Entry 對象。數組的容量是固定的,不一樣的鍵能夠產生相同的下標,這將致使 「衝突」。

Example:參考Java編程思想 P493

static final int SIZE = 1024;
LinkedList<Entry<K, V>>[] buckets = new LinkedList[SIZE]; 

public V put(K key, V value) {
    V oldValue = null;
    // 對散列值取餘獲取數組下標
    int index = Math.abs(key.hashCode()) % SIZE;
    if (buckets[index] == null) {
        buckets[index] = new LinkedList<Entry<K,V>>();
    }

    // 獲取下標所指向的鏈表
    LinkedList<Entry<K, V>> bucket = buckets[index];
    Entry<K, V> pair = new Entry<>();
    boolean found = false;

    // 獲取迭代器進行迭代,判斷是否存在,存在則覆蓋
    ListIterator<Entry<K, V>> it = bucket.listIterator();
    while (it.hasNext()) {
        Entry<K, V> iPair = it.next();
        if (iPair.getKey().equals(key)) {
            oldValue = iPair.getValue();
            it.set(pair);
            found = true;
            break;
        }
    }
    // 沒有找到就添加
    if (!found) {
        buckets[index].add(pair);
    }
    return oldValue;
}

equals()

  1. 自反性。
  2. 對稱性。
  3. 傳遞性。
  4. 一致性。
  5. 對任何不是 null的 x, x.equals(null) 必定返回 false。

hashcode()

  1. 對同一個對象調用hashcode() 都應該生成一樣的值。
  2. 基於對象的內容生成散列碼,讓其富有意義。
  3. 散列碼沒必要獨一無二,應關注生成速度。
  4. 經過 hashCode()equals(),必須可以徹底肯定對象的身份。
  5. 好的 hashCode()應該產生分佈均勻的散列碼。賦予一個非零常量值。
public int hashCode() {
    int result = 19;
    return result * field.hashCode();
}
相關文章
相關標籤/搜索