java(集合框架)(轉)

前言

集合①只能存放對象,存放基本類型會自動轉成對應的對象②能夠存放不一樣類型的對象(若是不使用泛型的話),且不限數量③集合中存放的只是對象的引用
集合詳解
html

集合-1.png

集合-2.png
 

 

 

 

 

Iterable接口(java.lang包)

Collection繼承了該接口,因此Collection的實現類均可以使用Iterator()方法來得到迭代器,從而遍歷集合java

public interface Iterable<E> {
    Iterator<E> iterator();//return a Iterator Object
}

Iterator接口(迭代器,java.util包)

迭代器可使用for-each代替。迭代器至關於一個在兩個元素之間的指針(首尾元素除外),使用remove()刪除元素以前,須要先調用next()越過該元素。若是調用next()以後,集合發生了改變,再接着調用remove()則會拋出異常。算法

public interface Iterator<E>{
  E next();//返回迭代器剛越過的元素的引用
  boolean hasNext();//判斷容器內是否還有可供訪問的元素
  void remove();//刪除迭代器剛越過的元素,因此要刪除則必須先越過
}

ListIterator接口

ListIterator接口繼承了Iterator接口,並添加了一些方法,使得能夠向前和向後遍歷。數組

add
set//修改越過的元素
previous
hasPrevious
nextIndex//返回下一次調用next方法返回的元素的索引
previousIndex

 

獲取迭代器默認其指針是在第一個元素的前面,能夠經過給定一個參數n來指定指針位置爲第n個元素的前面(ListIterator有,Iterator沒有這個),不過獲取這樣的迭代器消耗的時間比默認狀態的迭代器要高一些。另外須要注意的是remove依賴迭代器的狀態,add只依賴迭代器的位置安全

Collection

Collection接口定義了不少方法,開發者若是要開發本身的集合類,能夠繼承AbstractCollection抽象類,該類實現了Collection的不少方法,可是還有size()Iterator()沒實現數據結構

方法 描述
iterator() 獲取迭代器
size() 元素個數
isEmpty() 判斷有無元素
clear() 清空集合
contains() 是否包含給定元素
containsAll() 是否包含給定集合全部元素
add() 添加元素
addAll() 添加給定集合全部元素
remove() 刪除元素
removeAll() 刪除給定集合中全部元素
retainAll() 刪除給定集合中不存在的元素,即取交集
toArray() 返回集合的對象數組
toArray(array) 同上,不過指定一個數組參數

List

List能夠得到ListIterator迭代器,List能夠有重複的元素,元素能夠按照使用者的意願排序,元素的位置跟元素的值沒有關係,所以稱爲有序ide

  • ArrayList
    底層數據結構是數組,查詢快,增刪慢,線程不安全,效率高。ArrayList維護封裝了一個動態分配的對象數組,能夠經過索引快速訪問/修改元素,也能夠經過迭代器
  • LinkedList
    底層數據結構是鏈表,查詢慢,增刪快,線程不安全,效率高;鏈表不能像數組那樣經過索引來隨機訪問/修改元素,必須從頭開始,越過一個個元素。鏈表增刪元素是經過ListIterator迭代器來操做。LinkedList仍是雙端隊列
addFirst
addLast
getFirst
getLast
removeFirst
removeLast
  • Vector
    底層數據結構是數組,查詢快,增刪慢,線程安全,效率低,幾乎已經淘汰

Set

Set不能夠有重複的元素,只能用Iterator迭代器,不能使用ListIterator迭代器性能

  • HashSet(散列集)
    不容許重複,判斷重複的標準是equals爲true,且hashCode相等,容許null(最多一個),無序。元素的位置是由元素自己的hashCode來決定的,也就是說元素的位置是跟元素的值相關的,使用者沒法決定(改變),更直白地說,一個元素的值肯定了,它的位置就基本肯定了,元素的值改變了,其在散列集中的位置也會改變,所以咱們稱其爲無序的。HashSet中的元素須要重寫equals和hashCode方法,並確保equals返回true,則它們的hashCode必須相等。散列集底層數據結構是鏈表數組,每一個鏈表稱爲桶(bucket),標準庫中桶的數量是2的冪次方,默認爲16,也能夠本身指定。當添加一個元素的時候,用元素的散列碼對桶數求餘,餘數即爲該元素的桶的索引,也就是說求餘結果相同的元素都位於同一個桶中,再將元素與該桶中的元素進行equals比較,若是爲true,說明已經存在相同的元素,則不添加,若是爲false,則添加;散列表中的元素佔散列表空間的比例稱爲裝載因子(load factor,size/桶數),當比例超過裝載因子時,散列表就會用雙倍的桶數自動進行再散列。通常裝載因子爲0.75就能夠了,跟桶數同樣,裝載因子能夠在構造散列表的時候設置
  • TreeSet
    不容許重複,判斷重複的標準是CompareTo或compare返回0,不容許null,有序。底層數據結構是紅黑樹,擅長於範圍查詢。要求元素實現了Comparable接口(compareTo方法天然排序),也能夠在構造樹集的時候提供一個實現了Comparator接口(compare方法)的比較器,則樹集將會按照提供的比較器的規則進行排序,將一個元素添加到TreeSet的速度要比HashSet的速度慢一點,查找新元素的正確位置的平均時間爲log2(n),比元素添加到數組和鏈表中的正確位置(指按同一種規定的順序)要快不少
  • LinkedHashSet
    不能夠重複,判斷重複的標準是equals爲true,且hashCode相等,有序(記錄了插入順序)。由於底層採用鏈表和哈希表的算法。鏈表保證元素的添加順序,哈希表保證元素的惟一性this


    LinkedHashSet.png

隊列和雙端隊列

隊列能夠有效的在隊列尾部添加元素,頭部刪除元素。雙端隊列能夠同時在尾部和頭部添加和刪除元素,不過不能夠在中間。ArrayDeque和LinkedList實現了雙端隊列spa

優先級隊列PriorityQueue

底層數據結構是堆(heap)。典型示例爲任務調度。優先級隊列跟TreeSet同樣,須要元素實現Comparable接口或在構造時提供Comparator比較器。不管以什麼順序插入,remove()的老是優先級隊列裏最小的元素,然而優先級隊列並無對全部元素進行排序,只是確保了remove出來的是最小的元素

阻塞隊列BlockingQueue

映射表Map

映射表存儲的是鍵值對,Map不能使用for-each循環進行遍歷,由於它既不是Collection系列,也沒繼承Iterable接口

//Map的方法
put
get
putAll
containsKey
containsValue
Set<Map.Entry<K,V>> entrySet()//後面3個方法返回的是Map的視圖,能夠經過這三個集修改和刪除元素,則Map中也會相應改變,可是不能添加元素
Set<K> keySet()
Collection<V> values()
//Entry的方法
getKey
getValue
setValue
  • HashMap
    對鍵進行散列,鍵不容許重複,容許爲null(最多一個),判斷鍵重複的標準是鍵的equals爲true,且鍵的hashCode相等

  • TreeMap
    對鍵進行排序,用鍵的順序對元素進行排序,鍵不容許重複,判斷鍵重複的標準是鍵compareTo或compare爲0

  • LinkedHashMap
    與HashMap相似,只是多了鏈表保證鍵有序(記錄了訪問順序,每次因調用了get/set方法受到影響的元素都會從當前鏈表位置刪除,放到鏈表末尾,但注意在桶中的位置是不會受影響的)

  • WeakHashMap
    弱散列映射表

  • Hashtable
    與HashMap同樣,不過線程安全,性能低

  • Properties
    Hashtable的子類,要求鍵值都是字符串,通常用於配置文件

Set和Map

都有幾個類型的集合。HashMap 和 HashSet ,都採哈希表算法;TreeMap 和 TreeSet 都採用 紅-黑樹算法;LinkedHashMap 和 LinkedHashSet 都採用 哈希表算法和紅-黑樹算法。分析 Set 的底層源碼,咱們能夠看到,Set 集合 就是 由 Map 集合的 Key 組成,只不過Value是同一個Object對象


HashSet.png

經常使用的hashCode方法

hashCode.png

線程安全解決

  • Set
    Set set = Collections.synchronizedSet(Set 對象)
  • Map
    Map map= Collections.synchronizedSet(Map對象)

集合和數組轉換

  • 數組轉集合
    Arrays.asList()
String[] strs = new String[10];
List<String> list = Arrays.asList(strs);//list是原數組的視圖

集合轉數組
toArray(數組)

HashSet<String> staff = new HashSet<>(...);
String[] strs = staff.toArray();//error
String[] strs = staff.toArray(new String[0]);//ok
String[] strs = staff.toArray(new String[staff.size()]);//ok

數組複製方法

  • clone
    clone方法是從Object類繼承來的,對於基本類型的數組能夠直接使用,String類數組也可使用,由於String是不可變類,對於可變類,clone方法是淺拷貝
int[] a1 = {1, 3};
int[] a2 = a1.clone();
a1[0] = 666;
System.out.println(Arrays.toString(a1));   //[666, 3]
System.out.println(Arrays.toString(a2));   //[1, 3]
String[] s1 = {"a1", "a2"};
String[] s2 = s1.clone();
a1[0] = "b1"; //更改a1數組中元素的值
System.out.println(Arrays.toString(s1));   //[b1, a2]
System.out.println(Arrays.toString(s2));   //[a1, a2]

System.arraycopy
System.arraycopy是一個本地方法,源碼定義以下

public static native void arraycopy(Object src, int srcPos, Object dest, int desPos, int length)
//參數含義(原數組, 原數組的開始位置, 目標數組, 目標數組的開始位置, 拷貝長度)

 Arrays.copyOf
Arrays.copyOf底層其實也是用的System.arraycopy ,參數含義(原數組,拷貝長度)源碼以下:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

 Arrays.copyOfRange
Arrays.copyOfRange底層其實也是用的System.arraycopy,只不過封裝了一個方法,參數含義(原數組,開始位置,拷貝長度)

public static <T,U> T[] copyOfRange(U[] original, int from, int to, Class<? extends T[]> newType) {
    int newLength = to - from;
    if (newLength < 0)
        throw new IllegalArgumentException(from + " > " + to);
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, from, copy, 0,
                     Math.min(original.length - from, newLength));
    return copy;
}

 

深拷貝

    • 簡單類
      數據域爲基本類型或不可變類,實現Cloneable接口,並重寫clone方法,注意一個類不實現這個接口,直接使用clone方法是編譯通不過的
@Override
    public Cat clone() throws CloneNotSupportedException {
        Cat cat = (Cat) super.clone();
        return dog;
    }
組合類深拷貝
若是一個類裏面,又引用其餘的類,其餘的類又有引用別的類,那麼想要深度拷貝必須全部的類及其引用的類都得實現Cloneable接口,重寫clone方法,這樣以來很是麻煩,簡單的方法是讓全部的對象實現序列化接口( Serializable,該接口僅是一個標記,沒有方法),而後經過序列化-反序列化的方法來深度拷貝對象。

public Cat myClone() {
    Cat cat = null;

    try {
        //將對象序列化成爲流,由於如今流是對象的一個拷貝
        //而原始對象仍然存在於JVM中,因此利用這個特性能夠實現深拷貝
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);

        //將流序列化爲對象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        cat = (Cat) objectInputStream.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }

    return cat;
}
相關文章
相關標籤/搜索