欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

Java集合框架是什么

這篇文章主要介紹了Java集合框架是什么,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

創(chuàng)新互聯(lián)是一家專業(yè)提供寧陵企業(yè)網(wǎng)站建設(shè),專注與成都網(wǎng)站設(shè)計、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、H5技術(shù)、小程序制作等業(yè)務(wù)。10年已為寧陵眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。

一、簡介

1、集合框架介紹

Java集合框架提供了一套性能優(yōu)良,使用方便的接口和類,他們位于java.util包中。容器主要包括 Collection 和 Map 兩種,Collection 存儲著對象的集合,而 Map 存儲著鍵值對(兩個對象)的映射表

Java集合框架是什么

2、相關(guān)容器介紹

2.1 Set相關(guān)

  • TreeSet
    基于紅黑樹實現(xiàn),支持有序性操作,例如根據(jù)一個范圍查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的時間復(fù)雜度為 O(1),TreeSet 則為 O(logN)

  • HashSet
    基于哈希表實現(xiàn),支持快速查找,但不支持有序性操作。并且失去了元素的插入順序信息,也就是說使用 Iterator 遍歷 HashSet 得到的結(jié)果是不確定的。

  • LinkedHashSet
    具有 HashSet 的查找效率,且內(nèi)部使用雙向鏈表維護(hù)元素的插入順序。

2.2 List相關(guān)

  • ArrayList
    基于動態(tài)數(shù)組實現(xiàn),支持隨機(jī)訪問。

  • Vector
    和 ArrayList 類似,但它是線程安全的。

  • LinkedList
    基于雙向鏈表實現(xiàn),只能順序訪問,但是可以快速地在鏈表中間插入和刪除元素。不僅如此,LinkedList 還可以用作棧、隊列和雙向隊列。

2.3 Queue相關(guān)

  • LinkedList
    可以實現(xiàn)雙向隊列。

  • PriorityQueue
    基于堆結(jié)構(gòu)實現(xiàn),可以用它來實現(xiàn)優(yōu)先隊列。

2.4 Map相關(guān)

  • TreeMap
    基于紅黑樹實現(xiàn)。

  • HashMap
    基于哈希表實現(xiàn)。

  • HashTable
    和 HashMap 類似,但它是線程安全的,這意味著同一時刻多個線程可以同時寫入 HashTable 并且不會導(dǎo)致數(shù)據(jù)不一致。它是遺留類,不應(yīng)該去使用它?,F(xiàn)在可以使用 ConcurrentHashMap 來支持線程安全,并且 ConcurrentHashMap 的效率會更高,因為 ConcurrentHashMap 引入了分段鎖。

  • LinkedHashMap
    使用雙向鏈表來維護(hù)元素的順序,順序為插入順序或者最近最少使用(LRU)順序

3、集合重點

  • Collection 接口存儲一組不唯一,無序的對象

  • List 接口存儲一組不唯一,有序的對象。

  • Set 接口存儲一組唯一,無序的對象

  • Map 接口存儲一組鍵值對象,提供key到value的映射

  • ArrayList實現(xiàn)了長度可變的數(shù)組,在內(nèi)存中分配連續(xù)的空間。遍歷元素和隨機(jī)訪問元素的效率比較高

  • LinkedList采用鏈表存儲方式。插入、刪除元素時效率比較高

  • HashSet采用哈希算法實現(xiàn)的Set

  • HashSet的底層是用HashMap實現(xiàn)的,因此查詢效率較高,由于采用hashCode算法直接確定 元素的內(nèi)存地址,增刪效率高

二、ArrayList分析

1、ArrayList使用

方法說明
boolean add(Object o)在列表的末尾順序添加元素,起始索引位置從0開始
void add(int index, Object o)在指定的索引位置添加元素,索引位置必須介于0和列表中元素個數(shù)之間
int size()返回列表中的元素個數(shù)
Object get(int index)返回指定索引位置處的元素。取出的元素是Object類型,使用前品要進(jìn)行益制類型轉(zhuǎn)換
boolean contains(Object o)判斷列表中是否存在指定元素
boolean remove(Object o)從列表中刪除元素
Object remove(int index)從列表中刪除指定位置元素,起始索引位量從0開始

2、ArrayList介紹

  • ArrayList是可以動態(tài)增長和縮減的索引序列,它是基于數(shù)組實現(xiàn)的List類

  • 該類封裝了一個動態(tài)再分配的Object[]數(shù)組,每一個類對象都有一個capacity[容量]屬性,表示它們所封裝的Object[]數(shù)組的長度,當(dāng)向ArrayList中添加元素時,該屬性值會自動增加。如果想ArrayList中添加大量元素,可使用ensureCapacity方法一次性增加capacity,可以減少增加重分配的次數(shù)提高性能

  • ArrayList的用法和Vector向類似,但是Vector是一個較老的集合,具有很多缺點,不建議使用

另外,ArrayList和Vector的區(qū)別是:ArrayList是線程不安全的,當(dāng)多條線程訪問同一個ArrayList集合時,程序需要手動保證該集合的同步性,而Vector則是線程安全的。

3、源碼分析

3.1 繼承結(jié)構(gòu)與層次關(guān)系

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

Java集合框架是什么
這里簡單解釋一下幾個接口

  • RandomAccess接口
    這個是一個標(biāo)記性接口,通過查看api文檔,它的作用就是用來快速隨機(jī)存取,有關(guān)效率的問題,在實現(xiàn)了該接口的話,那么使用普通的for循環(huán)來遍歷,性能更高,例如ArrayList。而沒有實現(xiàn)該接口的話,使用Iterator來迭代,這樣性能更高,例如linkedList。所以這個標(biāo)記性只是為了 讓我們知道我們用什么樣的方式去獲取數(shù)據(jù)性能更好。

  • Cloneable接口
    實現(xiàn)了該接口,就可以使用Object.Clone()方法了。

  • Serializable接口
    實現(xiàn)該序列化接口,表明該類可以被序列化。什么是序列化?簡單的說,就是能夠從類變成字節(jié)流傳輸,然后還能從字節(jié)流變成原來的類。

這里的繼承結(jié)構(gòu)可通過IDEA中Navigate>Type Hierarchy查看

Java集合框架是什么

3.2 屬性

//版本號
private static final long serialVersionUID = 8683452581122892189L;
//缺省容量
private static final int DEFAULT_CAPACITY = 10;
//空對象數(shù)組
private static final Object[] EMPTY_ELEMENTDATA = {};
//缺省空對象數(shù)組
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存儲的數(shù)組元素
transient Object[] elementData; // non-private to simplify nested class access
//實際元素大小,默認(rèn)為0
private int size;
//最大數(shù)組容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

3.3 構(gòu)造方法

/**
 * 構(gòu)造具有指定初始容量的空列表
 * 如果指定的初始容量為負(fù),則為IllegalArgumentException
 */public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }}/**
 * 默認(rèn)空數(shù)組的大小為10
 * ArrayList中儲存數(shù)據(jù)的其實就是一個數(shù)組,這個數(shù)組就是elementData
 */public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}/**
 * 按照集合迭代器返回元素的順序構(gòu)造包含指定集合的元素的列表
 */public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 轉(zhuǎn)換為數(shù)組
        //每個集合的toarray()的實現(xiàn)方法不一樣,所以需要判斷一下,如果不是Object[].class類型,那么久需要使用ArrayList中的方法去改造一下。
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 否則就用空數(shù)組代替
        this.elementData = EMPTY_ELEMENTDATA;
    }}

3.4 自動擴(kuò)容

每當(dāng)向數(shù)組中添加元素時,都要去檢查添加后元素的個數(shù)是否會超出當(dāng)前數(shù)組的長度,如果超出,數(shù)組將會進(jìn)行擴(kuò)容,以滿足添加數(shù)據(jù)的需求。數(shù)組擴(kuò)容通過一個公開的方法ensureCapacity(int minCapacity)來實現(xiàn)。在實際添加大量元素前,我也可以使用ensureCapacity來手動增加ArrayList實例的容量,以減少遞增式再分配的數(shù)量。

數(shù)組進(jìn)行擴(kuò)容時,會將**老數(shù)組中的元素重新拷貝一份到新的數(shù)組中,每次數(shù)組容量的增長大約是其原容量的1.5倍。**這種操作的代價是很高的,因此在實際使用時,我們應(yīng)該盡量避免數(shù)組容量的擴(kuò)張。當(dāng)我們可預(yù)知要保存的元素的多少時,要在構(gòu)造ArrayList實例時,就指定其容量,以避免數(shù)組擴(kuò)容的發(fā)生?;蛘吒鶕?jù)實際需求,通過調(diào)用ensureCapacity方法來手動增加ArrayList實例的容量。

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //判斷初始化的elementData是不是空的數(shù)組,也就是沒有長度
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //因為如果是空的話,minCapacity=size+1;其實就是等于1,空的數(shù)組沒有長度就存放不了
        //所以就將minCapacity變成10,也就是默認(rèn)大小,但是在這里,還沒有真正的初始化這個elementData的大小
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //確認(rèn)實際的容量,上面只是將minCapacity=10,這個方法就是真正的判斷elementData是否夠用
    return minCapacity;}private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    //minCapacity如果大于了實際elementData的長度,那么就說明elementData數(shù)組的長度不夠用
    /*第一種情況:由于elementData初始化時是空的數(shù)組,那么第一次add的時候,
    minCapacity=size+1;也就minCapacity=1,在上一個方法(確定內(nèi)部容量ensureCapacityInternal)
    就會判斷出是空的數(shù)組,就會給將minCapacity=10,到這一步為止,還沒有改變elementData的大小。
    第二種情況:elementData不是空的數(shù)組了,那么在add的時候,minCapacity=size+1;也就是
    minCapacity代表著elementData中增加之后的實際數(shù)據(jù)個數(shù),拿著它判斷elementData的length
    是否夠用,如果length不夠用,那么肯定要擴(kuò)大容量,不然增加的這個元素就會溢出。*/ 
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);}//ArrayList核心的方法,能擴(kuò)展數(shù)組大小的真正秘密。private void grow(int minCapacity) {
    //將擴(kuò)充前的elementData大小給oldCapacity
    int oldCapacity = elementData.length;
    //newCapacity就是1.5倍的oldCapacity
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    /*這句話就是適應(yīng)于elementData就空數(shù)組的時候,length=0,那么oldCapacity=0,newCapacity=0,
    所以這個判斷成立,在這里就是真正的初始化elementData的大小了,就是為10.前面的工作都是準(zhǔn)備工作。
    */
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //如果newCapacity超過了最大的容量限制,就調(diào)用hugeCapacity,也就是將能給的最大值給newCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //新的容量大小已經(jīng)確定好就copy數(shù)組,改變?nèi)萘看笮 ?
    elementData = Arrays.copyOf(elementData, newCapacity);}//用來賦最大值private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    //如果minCapacity都大于MAX_ARRAY_SIZE,那么就Integer.MAX_VALUE返回,反之將MAX_ARRAY_SIZE返回。
    //相當(dāng)于給ArrayList上了兩層防護(hù)
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;}

3.5 add()方法

/**
 * 添加一個特定的元素到list的末尾。
 * 先size+1判斷數(shù)組容量是否夠用,最后加入元素
 */public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;}/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 *
 * @param index index at which the specified element is to be inserted
 * @param element element to be inserted
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */public void add(int index, E element) {
    //檢查index也就是插入的位置是否合理。
    rangeCheckForAdd(index);
    //檢查容量是否夠用,不夠就自動擴(kuò)容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //這個方法就是用來在插入元素之后,要將index之后的元素都往后移一位
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;}

當(dāng)調(diào)用add()方法時,實際函數(shù)調(diào)用:

add→ensureCapacityInternal→ensureExplicitCapacity(→grow→hugeCapacity)

例如剛開始初始化一個空數(shù)組后add一個值,會首先進(jìn)行自動擴(kuò)容
Java集合框架是什么

3.6 trimToSize()

將底層數(shù)組的容量調(diào)整為當(dāng)前列表保存的實際元素的大小的功能

public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA          : Arrays.copyOf(elementData, size);
    }}

3.7 remove()方法

remove()方法也有兩個版本,一個是remove(int index)刪除指定位置的元素,另一個是remove(Object o)刪除第一個滿足o.equals(elementData[index])的元素。刪除操作是add()操作的逆過程,需要將刪除點之后的元素向前移動一個位置。需要注意的是為了讓GC起作用,必須顯式的為最后一個位置賦null值。

public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; //清除該位置的引用,讓GC起作用

        return oldValue;
    }

3.8 其他方法

這里簡單介紹了核心方法,其他方法查看源碼可以很快了解

3.9 Fail-Fast機(jī)制

ArrayList采用了快速失敗的機(jī)制,通過記錄modCount參數(shù)來實現(xiàn)。在面對并發(fā)的修改時,迭代器很快就會完全失敗,并拋出ConcurrentModificationException異常,而不是冒著在將來某個不確定時間發(fā)生任意不確定行為的風(fēng)險

4、總結(jié)

  • ArrayList可以存放null

  • ArrayList本質(zhì)上就是一個elementData數(shù)組

  • ArrayList區(qū)別于數(shù)組的地方在于能夠自動擴(kuò)展大小,其中關(guān)鍵的方法就是gorw()方法

  • ArrayList中removeAll(collection c)和clear()的區(qū)別就是removeAll可以刪除批量指定的元素,而clear是全是刪除集合中的元素

  • ArrayList由于本質(zhì)是數(shù)組,所以它在數(shù)據(jù)的查詢方面會很快,而在插入刪除這些方面,性能下降很多,有移動很多數(shù)據(jù)才能達(dá)到應(yīng)有的效果

  • ArrayList實現(xiàn)了RandomAccess,所以在遍歷它的時候推薦使用for循環(huán)

三、LinkedList分析

1、LinkedList使用

方法名說明
void addFirst(Object o)在列表的首部添加元素
void addLast(Object o)在列表的未尾添加元素
Object getFirst()返回列表中的第一個元素
Object getLast()返回列表中的最后一個元素
Object removeFirst()刪除并返回列表中的第一個元素
Object removeLast()刪除并返回列表中的最后一個元素

2、LinkedList介紹

LinkedList同時實現(xiàn)了List接口和Deque接口,也就是說它既可以看作一個順序容器,又可以看作一個隊列(Queue),同時又可以看作一個棧(Stack)。這樣看來,LinkedList簡直就是個全能冠軍。當(dāng)你需要使用?;蛘哧犃袝r,可以考慮使用LinkedList,一方面是因為Java官方已經(jīng)聲明不建議使用Stack類,更遺憾的是,Java里根本沒有一個叫做Queue_的類(它是個接口名字)。關(guān)于棧或隊列,現(xiàn)在的首選是ArrayDeque,它有著比LinkedList(當(dāng)作?;蜿犃惺褂脮r)有著更好的性能。

LinkedList的實現(xiàn)方式?jīng)Q定了所有跟下標(biāo)相關(guān)的操作都是線性時間,而在首段或者末尾刪除元素只需要常數(shù)時間。為追求效率LinkedList沒有實現(xiàn)同步(synchronized),如果需要多個線程并發(fā)訪問,可以先采用Collections.synchronizedList()方法對其進(jìn)行包裝

3、源碼分析

3.1 繼承結(jié)構(gòu)與層次

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

Java集合框架是什么
Java集合框架是什么

這里可以發(fā)現(xiàn)LinkedList多了一層AbstractSequentialList的抽象類,這是為了減少實現(xiàn)順序存?。ɡ鏛inkedList)這種類的工作。如果自己想實現(xiàn)順序存取這種特性的類(就是鏈表形式),那么就繼承 這個AbstractSequentialList抽象類,如果想像數(shù)組那樣的隨機(jī)存取的類,那么就去實現(xiàn)AbstracList抽象類。

  • List接口
    列表add、set等一些對列表進(jìn)行操作的方法

  • Deque接口
    有隊列的各種特性

  • Cloneable接口
    能夠復(fù)制,使用那個copy方法

  • Serializable接口
    能夠序列化。

  • 沒有RandomAccess
    推薦使用iterator,在其中就有一個foreach,增強(qiáng)的for循環(huán),其中原理也就是iterator,我們在使用的時候,使用foreach或者iterator

3.2 屬性與構(gòu)造方法

transient關(guān)鍵字修飾,這也意味著在序列化時該域是不會序列化的

//實際元素個數(shù)transient int size = 0;
//頭結(jié)點transient Node<E> first;
//尾結(jié)點transient Node<E> last;
public LinkedList() {}public LinkedList(Collection<? extends E> c) {
    this();
    //將集合c中的各個元素構(gòu)建成LinkedList鏈表
    addAll(c);}

3.3 內(nèi)部類Node

//根據(jù)前面介紹雙向鏈表就知道這個代表什么了,linkedList的奧秘就在這里private static class Node<E> {
    // 數(shù)據(jù)域(當(dāng)前節(jié)點的值)
    E item;
    //后繼
    Node<E> next;
    //前驅(qū)
    Node<E> prev;
    // 構(gòu)造函數(shù),賦值前驅(qū)后繼
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }}

3.4 核心方法add()和addAll()

public boolean add(E e) {
    linkLast(e);
    return true;}void linkLast(E e) {
    //臨時節(jié)點l(L的小寫)保存last,也就是l指向了最后一個節(jié)點
    final Node<E> l = last;
    //將e封裝為節(jié)點,并且e.prev指向了最后一個節(jié)點
    final Node<E> newNode = new Node<>(l, e, null);
    //newNode成為了最后一個節(jié)點,所以last指向了它
    last = newNode;
    if (l == null)
        //判斷是不是一開始鏈表中就什么都沒有,如果沒有,則new Node就成為了第一個結(jié)點,first和last都指向它
        first = newNode;
    else
        //正常的在最后一個節(jié)點后追加,那么原先的最后一個節(jié)點的next就要指向現(xiàn)在真正的 最后一個節(jié)點,原先的最后一個節(jié)點就變成了倒數(shù)第二個節(jié)點
        l.next = newNode;
    //添加一個節(jié)點,size自增
    size++;
    modCount++;}

addAll()有兩個重載函數(shù),addAll(Collection<? extends E>)型和addAll(int,Collection<? extends E>)型,我們平時習(xí)慣調(diào)用的addAll(Collection<?extends E>)型會轉(zhuǎn)化為addAll(int,Collection<? extends<E>)

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);}public boolean addAll(int index, Collection<? extends E> c) {
    //檢查index這個是否為合理
    checkPositionIndex(index);
    //將集合c轉(zhuǎn)換為Object數(shù)組
    Object[] a = c.toArray();
    //數(shù)組a的長度numNew,也就是由多少個元素
    int numNew = a.length;
    if (numNew == 0)
        //如果空的就什么也不做
        return false;

    Node<E> pred, succ;
    //構(gòu)造方法中傳過來的就是index==size
    //情況一:構(gòu)造方法創(chuàng)建的一個空的鏈表,那么size=0,last、和first都為null。linkedList中是空的。
    //什么節(jié)點都沒有。succ=null、pred=last=null
    //情況二:鏈表中有節(jié)點,size就不是為0,first和last都分別指向第一個節(jié)點,和最后一個節(jié)點,
    //在最后一個節(jié)點之后追加元素,就得記錄一下最后一個節(jié)點是什么,所以把last保存到pred臨時節(jié)點中。
    //情況三index!=size,說明不是前面兩種情況,而是在鏈表中間插入元素,那么就得知道index上的節(jié)點是誰,
    //保存到succ臨時節(jié)點中,然后將succ的前一個節(jié)點保存到pred中,這樣保存了這兩個節(jié)點,就能夠準(zhǔn)確的插入節(jié)點了
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        succ = node(index);
        pred = succ.prev;
    }

    for (Object o : a) {
        @SuppressWarnings("unchecked") E e = (E) o;
        Node<E> newNode = new Node<>(pred, e, null);
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        pred = newNode;
    }

    if (succ == null) {
        /*如果succ==null,說明是情況一或者情況二,
        情況一、構(gòu)造方法,也就是剛創(chuàng)建的一個空鏈表,pred已經(jīng)是newNode了,
        last=newNode,所以linkedList的first、last都指向第一個節(jié)點。
        情況二、在最后節(jié)后之后追加節(jié)點,那么原先的last就應(yīng)該指向現(xiàn)在的最后一個節(jié)點了,
        就是newNode。*/
        last = pred;
    } else {
        pred.next = succ;
        succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;}//根據(jù)引下標(biāo)找到該結(jié)點并返回Node<E> node(int index) {
    //判斷插入的位置在鏈表前半段或者是后半段
    if (index < (size >> 1)) {
        Node<E> x = first;
        //從頭結(jié)點開始正向遍歷
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        //從尾結(jié)點開始反向遍歷
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }}

3.5 remove()

/*如果我們要移除的值在鏈表中存在多個一樣的值,那么我們
會移除index最小的那個,也就是最先找到的那個值,如果不存在這個值,那么什么也不做
*/public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;}不能傳一個null值E unlink(Node<E> x) {
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }
    //x的前后指向都為null了,也把item為null,讓gc回收它
    x.item = null;
    size--;
    modCount++;
    return element;}

3.6 其他方法

**get(index)、indexOf(Object o)**等查看源碼即可

3.7 LinkedList的迭代器

在LinkedList中除了有一個Node的內(nèi)部類外,應(yīng)該還能看到另外兩個內(nèi)部類,那就是ListItr,還有一個是DescendingIterator內(nèi)部類

Java集合框架是什么

/*這個類,還是調(diào)用的ListItr,作用是封裝一下Itr中幾個方法,讓使用者以正常的思維去寫代碼,
例如,在從后往前遍歷的時候,也是跟從前往后遍歷一樣,使用next等操作,而不用使用特殊的previous。
*/private class DescendingIterator implements Iterator<E> {
    private final ListItr itr = new ListItr(size());
    public boolean hasNext() {
        return itr.hasPrevious();
    }
    public E next() {
        return itr.previous();
    }
    public void remove() {
        itr.remove();
    }}

4、總結(jié)

  • linkedList本質(zhì)上是一個雙向鏈表,通過一個Node內(nèi)部類實現(xiàn)的這種鏈表結(jié)構(gòu)。linkedList能存儲null值

  • 跟ArrayList相比較,就真正的知道了,LinkedList在刪除和增加等操作上性能好,而ArrayList在查詢的性能上好,從源碼中看,它不存在容量不足的情況

  • linkedList不光能夠向前迭代,還能像后迭代,并且在迭代的過程中,可以修改值、添加值、還能移除值

  • linkedList不光能當(dāng)鏈表,還能當(dāng)隊列使用,這個就是因為實現(xiàn)了Deque接口

四、List總結(jié)

1、ArrayList和LinkedList區(qū)別

  • ArrayList底層是用數(shù)組實現(xiàn)的順序表,是隨機(jī)存取類型,可自動擴(kuò)增,并且在初始化時,數(shù)組的長度是0,只有在增加元素時,長度才會增加。默認(rèn)是10,不能無限擴(kuò)增,有上限,在查詢操作的時候性能更好

  • LinkedList底層是用鏈表來實現(xiàn)的,是一個雙向鏈表,注意這里不是雙向循環(huán)鏈表,順序存取類型。在源碼中,似乎沒有元素個數(shù)的限制。應(yīng)該能無限增加下去,直到內(nèi)存滿了在進(jìn)行刪除,增加操作時性能更好。

兩個都是線程不安全的,在iterator時,會發(fā)生fail-fast:快速失效

2、ArrayList和Vector區(qū)別

  • ArrayList線程不安全,在用iterator,會發(fā)生fail-fast

  • Vector線程安全,因為在方法前加了Synchronized關(guān)鍵字,也會發(fā)生fail-fast

3、fail-fast和fail-safe區(qū)別與情況說明

在java.util下的集合都是發(fā)生fail-fast,而在java.util.concurrent下的發(fā)生的都是fail-safe

  • fail-fast
    快速失敗,例如在arrayList中使用迭代器遍歷時,有另外的線程對arrayList的存儲數(shù)組進(jìn)行了改變,比 如add、delete等使之發(fā)生了結(jié)構(gòu)上的改變,所以Iterator就會快速報一個java.util.ConcurrentModi?cationException異常(并發(fā)修改異常),這就是快速失敗

  • fail-safe
    安全失敗,在java.util.concurrent下的類,都是線程安全的類,他們在迭代的過程中,如果有線程進(jìn)行結(jié)構(gòu)的改變,不會報異常,而是正常遍歷,這就是安全失敗

  • 為什么在java.util.concurrent包下對集合有結(jié)構(gòu)的改變卻不會報異常?
    在concurrent下的集合類增加元素的時候使用Arrays.copyOf()來拷貝副本,在副本上增加元素,如果有其他線程在此改變了集合的結(jié)構(gòu),那也是在副本上的改變,而不是影響到原集合,迭代器還是照常遍歷,遍歷完之后,改變原引用指向副本,所以總的一句話就是如果在此包下的類進(jìn)行增加刪除,就會出現(xiàn)一個副本。所以能防止fail-fast,這種機(jī)制并不會出錯,所以我們叫這種現(xiàn)象為fail-safe

  • vector也是線程安全的,為什么是fail-fast呢?
    出現(xiàn)fail-safe是因為他們在實現(xiàn)增刪的底層機(jī)制不一樣,就像上面說的,會有一個副本,而像arrayList、linekdList、verctor等他們底層就是對著真正的引用進(jìn)行操作,所以才會發(fā)生異常

4、為什么現(xiàn)在都不提倡使用Vector

  • vector實現(xiàn)線程安全的方法是在每個操作方法上加鎖,這些鎖并不是必須要的,在實際開發(fā)中,一般都是通過鎖一系列的操作來實現(xiàn)線程安全,也就是說將需要同步的資源放一起加鎖來保證線程安全

  • 如果多個Thread并發(fā)執(zhí)行一個已經(jīng)加鎖的方法,但是在該方法中,又有Vector的存在,Vector
    本身實現(xiàn)中已經(jīng)加鎖了,那么相當(dāng)于鎖上又加鎖,會造成額外的開銷

  • Vector還有fail-fast的問題,也就是說它也無法保證遍歷安全,在 遍歷時又得額外加鎖,又是額外的開銷,還不如直接用arrayList,然后再加鎖

總結(jié):Vector在你不需要進(jìn)行線程安全的時候,也會給你加鎖,也就導(dǎo)致了額外開銷,所以在jdk1.5之后就被棄用了,現(xiàn)在如果要用到線程安全的集合,都是從java.util.concurrent包下去拿相應(yīng)的類。

五、HashMap分析

1、HashMap介紹

1.1 Java8以前的HashMap

通過key、value封裝成一個entry對象,然后通過key的值來計算該entry的hash值,通過entry的hash 值和數(shù)組的長度length來計算出entry放在數(shù)組中的哪個位置上面,每次存放都是將entry放在第一個位置。

HashMap實現(xiàn)了Map接口,即允許放入keynull的元素,也允許插入valuenull的元素;除該類未實現(xiàn)同步外,其余跟Hashtable大致相同;跟TreeMap不同,該容器不保證元素順序,根據(jù)需要該容器可能會對元素重新哈希,元素的順序也會被重新打散,因此不同時間迭代同一個HashMap的順序可能會不同。 根據(jù)對沖突的處理方式不同,哈希表有兩種實現(xiàn)方式,一種開放地址方式(Open addressing),另一種是沖突鏈表方式(Separate chaining with linked lists)。Java7 HashMap采用的是沖突鏈表方式。

Java集合框架是什么

1.2 Java8后的HashMap

Java8 對 HashMap 進(jìn)行了一些修改,最大的不同就是利用了紅黑樹,所以其由 數(shù)組+鏈表+紅黑樹組成。根據(jù) Java7 HashMap的介紹,我們知道,查找的時候,根據(jù) hash 值我們能夠快速定位到數(shù)組的具體下標(biāo),但是之后的話,需要順著鏈表一個個比較下去才能找到我們需要的,時間復(fù)雜度取決于鏈表的長度為 O(n)。為了降低這部分的開銷,在 Java8中,當(dāng)鏈表中的元素達(dá)到了 8 個時,會將鏈表轉(zhuǎn)換為紅黑樹,在這些位置進(jìn)行查找的時候可以降低時間復(fù)雜度為 O(logN)。
Java集合框架是什么

Java7 中使用 Entry 來代表每個 HashMap 中的數(shù)據(jù)節(jié)點,Java8 中使用 Node,基本沒有區(qū)別,都是 key,value,hash 和 next 這四個屬性,不過,Node 只能用于鏈表的情況,紅黑樹的情況需要使用 TreeNode

2、Java8 HashMap源碼分析

2.1 繼承結(jié)構(gòu)與層次

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

Java集合框架是什么

2.2 屬性

//序列號private static final long serialVersionUID = 362498820763181265L;
//默認(rèn)的初始容量static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
// aka 16
//最大容量static final int MAXIMUM_CAPACITY = 1 << 30;
//默認(rèn)加載因子static final float DEFAULT_LOAD_FACTOR = 0.75f;
//當(dāng)桶(bucket)上的結(jié)點數(shù)大于這個值時會轉(zhuǎn)成紅黑樹static final int TREEIFY_THRESHOLD = 8;
//當(dāng)桶(bucket)上的結(jié)點數(shù)小于這個值時樹轉(zhuǎn)鏈表static final int UNTREEIFY_THRESHOLD = 6;
//桶中結(jié)構(gòu)轉(zhuǎn)化為紅黑樹對應(yīng)的table的最小大小static final int MIN_TREEIFY_CAPACITY = 64;
//存儲元素的數(shù)組,總是2的冪次倍transient Node<K,V>[] table;
//存放具體元素的集transient Set<Map.Entry<K,V>> entrySet;
//存放元素的個數(shù),注意這個不等于數(shù)組的長度transient int size;
//每次擴(kuò)容和更改map結(jié)構(gòu)的計數(shù)器transient int modCount;
//臨界值,當(dāng)實際大小(容量*填充因子)超過臨界值時,會進(jìn)行擴(kuò)容int threshold;
//填充因子,計算HashMap的實時裝載因子的方法為:size/capacityfinal float loadFactor;

2.3 構(gòu)造方法

public HashMap(int initialCapacity, float loadFactor) {
    // 初始容量不能小于0,否則報錯
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    // 初始容量不能大于最大值,否則為最大值
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    //填充因子不能小于或等于0,不能為非數(shù)字
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
         loadFactor);
    //初始化填充因子                                       
    this.loadFactor = loadFactor;
    //初始化threshold大小
    this.threshold = tableSizeFor(initialCapacity);}//這個方法將傳進(jìn)來的參數(shù)轉(zhuǎn)變?yōu)?的n次方的數(shù)值static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;}/**
 * 自定義初始容量,加載因子為默認(rèn)
 */public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);}/**
 * 使用默認(rèn)的加載因子等字段
 */public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}public HashMap(Map<? extends K, ? extends V> m) {
    //初始化填充因子
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    //將m中的所有元素添加至HashMap中
    putMapEntries(m, false);}//將m的所有元素存入該實例final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    int s = m.size();
    if (s > 0) {
        //判斷table是否已經(jīng)初始化
        if (table == null) { // pre-size
            //未初始化,s為m的實際元素個數(shù)
            float ft = ((float)s / loadFactor) + 1.0F;
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            //計算得到的t大于閾值,則初始化閾值
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        else if (s > threshold)
            resize();
        //將m中的所有元素添加至HashMap中
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }}

2.4 核心方法

put()方法

先計算key的hash值,然后根據(jù)hash值搜索在table數(shù)組中的索引位置,如果table數(shù)組在該位置處有元素,則查找是否存在相同的key,若存在則覆蓋原來key的value,否則將該元素保存在鏈表尾部,注意JDK1.7中采用的是頭插法,即每次都將沖突的鍵值對放置在鏈表頭,這樣最初的那個鍵值對最終就會成為鏈尾,而JDK1.8中使用的是尾插法。此外,若table在該處沒有元素,則直接保存。

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);}final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //第一次put元素時,table數(shù)組為空,先調(diào)用resize生成一個指定容量的數(shù)組
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //hash值和n-1的與運(yùn)算結(jié)果為桶的位置,如果該位置空就直接放置一個Node
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    //如果計算出的bucket不空,即發(fā)生哈希沖突,就要進(jìn)一步判斷
    else {
        Node<K,V> e; K k;
        //判斷當(dāng)前Node的key與要put的key是否相等
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //判斷當(dāng)前Node是否是紅黑樹的節(jié)點
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        //以上都不是,說明要new一個Node,加入到鏈表中
        else {
            for (int binCount = 0; ; ++binCount) {
              //在鏈表尾部插入新節(jié)點,注意jdk1.8是在鏈表尾部插入新節(jié)點
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    // 如果當(dāng)前鏈表中的元素大于樹化的閾值,進(jìn)行鏈表轉(zhuǎn)樹的操作
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                //在鏈表中繼續(xù)判斷是否已經(jīng)存在完全相同的key
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        //走到這里,說明本次put是更新一個已存在的鍵值對的value
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            //在hashMap中,afterNodeAccess方法體為空,交給子類去實現(xiàn)
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    //如果當(dāng)前size超過臨界值,就擴(kuò)容。注意是先插入節(jié)點再擴(kuò)容
    if (++size > threshold)
        resize();
    //在hashMap中,afterNodeInsertion方法體為空,交給子類去實現(xiàn)
    afterNodeInsertion(evict);
    return null;}

resize() 數(shù)組擴(kuò)容

用于初始化數(shù)組或數(shù)組擴(kuò)容,每次擴(kuò)容后,容量為原來的 2 倍,并進(jìn)行數(shù)據(jù)遷移

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) { // 對應(yīng)數(shù)組擴(kuò)容
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // 將數(shù)組大小擴(kuò)大一倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            // 將閾值擴(kuò)大一倍
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // 對應(yīng)使用 new HashMap(int initialCapacity) 初始化后,第一次 put 的時候
        newCap = oldThr;
    else {// 對應(yīng)使用 new HashMap() 初始化后,第一次 put 的時候
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }

    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;

    // 用新的數(shù)組大小初始化新的數(shù)組
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab; // 如果是初始化數(shù)組,到這里就結(jié)束了,返回 newTab 即可

    if (oldTab != null) {
        // 開始遍歷原數(shù)組,進(jìn)行數(shù)據(jù)遷移。
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                // 如果該數(shù)組位置上只有單個元素,那就簡單了,簡單遷移這個元素就可以了
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                // 如果是紅黑樹,具體我們就不展開了
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { 
                    // 這塊是處理鏈表的情況            

本文名稱:Java集合框架是什么
文章地址:http://aaarwkj.com/article40/gjddho.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計公司、網(wǎng)站設(shè)計、品牌網(wǎng)站設(shè)計、標(biāo)簽優(yōu)化品牌網(wǎng)站制作、網(wǎng)站營銷

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化
日韩成人三级一区二区| 国产免费一区二区福利| 国产在线一区二区三区蜜桃| 一卡二卡三卡四卡日韩| 亚洲午夜av久久乱码| 日本女优中文字幕久久| 亚洲少妇午夜福利视频| 五月婷婷丁香婷婷丁香| 亚洲成人久久久久久久| 国产又粗又硬又长又爽在线观看| 白嫩少妇情久久密月久久| 国产一区二区三区精品久久| 国产亚洲精品视频在线网| 国产精品午夜福利91| 日韩精品人妻一区二区三区蜜桃臀| 色哟哟网站一区二区精品久久| 日韩 高清 一区二区| 国产男女猛烈无遮挡av| 日本在线视频精品一区| 国产精品欧美久久久久久| 四虎精品国产一区二区三区| 日本高清加勒比免费在线| 欧美三级美国三级亚洲三级| 白虎亚洲福利精品一区| 韩国av网址在线观看| 久久精品性少妇一区=区三区| 欧美日韩高清一区二区三区| 国产内射一级一片高清视频观看| 高清大片免费看一区二区| 亚洲欧美日韩不卡一区二区| 久久99精品人妻一区二区三区| 成年人免费久久毛片| 日本欧美中文字幕一区| 中文字幕人妻熟人妻熟丝| 日本免费91午夜视频| 欧美日韩在线不卡一区| 国产美女被狂操到高潮| 黑丝美女大战白丝美女| 男女做爰高清无遮挡免费| 国内久久婷婷综合五月趴| 成人在线观看一区二区三区|