JAVA 容器,LinkedList、arraylist 源碼分析。容器、集合類對比,arraylist、vector、

 

List 與Set接口

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

List接口存儲一組不唯一、有序(插入順序)的對象

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

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

 

set接口中的實現類

HashSet:採用Hashtable哈希表存儲結構

優點:添加速度快,查詢速度快,刪除速度快

缺點:無序

TreeSet

採用二叉樹(紅黑樹)的存儲結構

優點:有序(排序後的升序)查詢速度比List快

缺點:查詢速度沒有HashSet快

 

java 容器 collection 和map的區別。collection中存儲了一組對象,而map存儲的是key、value,並且key不能重複。

Map:

HashMap:非線程安全、無序、key不重複。

TreeMap:非線程安全、有序、key不重複。

Hashtable:線程安全、無序、不重複。

ConcurrentHashMap:線程安全,有序、不重複。

 

Collection

set:  有序、不重複對象

TreeSet:非線程安全、有序、不重複。

HashSet:非線程安全、有序、不重複。

 

List:  按插入順序、對象可重複

TreeList:非線程安全、按插入順序、可重複。

ArrayList:非線程安全、插入順序、可重複。

LinkedList:非線程安全、插入順序、可重複。

Vector:線程安全、插入順序、可重複。

 

Arraylist : 初始大小爲10,原大小+ 原大小右移1位 = 1.5 擴容。oldCapacity + (oldCapacity >> 1)。插入刪除時都會copy一份。

Vector: 直接2倍擴容。oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)

HashMap: 初始16 ,2倍(2的n次冪)擴容.左移1位.newCap = oldCap << 1

爲什麼hashmap容量是2的n次冪?

1.方便進行 與 運算 (HashMap: 376行),與運算比取模運算效率高

2.在擴容之後涉及到元素的遷移過程,遷移的時候只需要判斷二進制的前一位是0或者1即可。

   如果是0,表示新數組和舊數組的下標位置不變,如果是1,只需要將索引位置加上舊的數組的長度值即爲新數組的下標

 


ArrayList 源碼分析

add(Object o )

/**
     * 將當前數組大小+1傳入 ensureCapacityInternal方法
     * 將e 對象添加到 elementData 數組的 當前數組+1位置。數據的最後一位,非數組的末尾!!!
     * 並且每次添加對象,數組的大小都會1.5倍擴張
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 當前數組大小 + 1
        elementData[size++] = e;
        return true;
    }

    /**
     * 判斷下 elementData 是否是空的,空的就取最小值10
     * 如果當前數組+1 的大小大於10,就取當前數組+1
     * @param minCapacity
     */
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 如果這個數組是空的
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  //初始化一個 10 的大小,或者數組+1 個
        }

        ensureExplicitCapacity(minCapacity);
    }
    public static int max(int a, int b) {//a=10 ,b=當前數組大小
        return (a >= b) ? a : b;
    }

    /**
     * 如果當前數組+1  大於  elementData的length 執行grow
     * @param minCapacity
     */
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0) //一般是true。當前數組+1 大於 當前數組大小
            grow(minCapacity);
    }
    /**
     * 先獲取 elementData 的length,然後*1.5倍。如果這個值比當前數組+1 小,就取當前數組+1 minCapacity
     * 如果 newCapacity 比 最大值大,就取 最大值或者 最大值減8
     * 將 elementData 數組擴大至 當前數組的 1.5 倍
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length; //當前數據的大小
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍。右移運算,不懂的可以for循環打出,數組新值
        if (newCapacity - minCapacity < 0) //如果 新值 小於當前數組+1
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) // 如果新值 大於最大值
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    /**
     * 當前數組+1 大於最大值 就返回 integer最大值,否則返回integer最大值-8
     */
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

通過源碼可以看到,arraylist 添加對象的時候,初始大小是10,然後會進行每次1.5倍的擴張。

而且添加的時候,是將現有的數組進行copy一份的。所以插入效率不高。

remove(object o)

/**
     * 先進行遍歷,獲取到要刪除的位置的索引。
     * 然後調用系統的刪除方法
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /**
     * 進行copy刪除
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }

可以發現,remove效率也不高,先遍歷再copy一份刪除。

 

LinkedList

/**
     * 先要取出next,然後再取出next的item進行比較
     */
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

    /**
     * 直接鏈到最後面
     */
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

插入效率高、刪除速度快。遍歷和隨機訪問效率低。

爲什麼說linkedlist是雙向鏈表?

因爲在linkedlist中存儲的Node,會記錄上一個下一個node

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

 

Vector

add: 添加時進行當前大小的*2擴增,並且是線程安全的。

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity)
     * 兩倍擴增
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

 

Queue 和 list的區別

 

Queue 和list 的區別。queue 添加了很多對線程友好的api  : offer、peek、poll。 put、take 會阻塞

Queue :當隊列滿了的時候調用 add方法會拋出異常,同理remove也會拋出異常。

queue提供了offer、peek、poll的支持。

offer 添加,返回值boolean,不會報錯

peek 返回隊列的第一個(頭)元素,不會刪除

poll 返回隊列頭元素並刪除

 

 

ArrayBlockingQueue  實現了 BlockingQueue, BlockingQueue 繼承自Queue

ArrayBlockingQueue  有take方法,得到頭元素並刪除,如果queue是空的就進行等待(阻塞),需要捕獲異常

ArrayBlockingQueue  有put方法,添加時如果滿了就進行等待(阻塞),需要捕獲異常。

ArrayBlockingQueue  是線程安全的,方法裏面加入了 ReentrantLock 

 

Arrays

用於對數組操作的工具類

 

java.util.Collections  類!!  而不是接口 Collection

這個類是專門對實現了List的類提供了一些靜態方法支持,以及提供了將非同步Set、List、Map轉換成同步的方法

sort()排序:  Collections.sort(list);

public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }

如果是自定義的對象,需要實現 Comparable 接口

 

binarySearch : 對半查找

//很明顯會調用 iteratorBinarySearch
    public static <T>
    int binarySearch(List<? extends Comparable<? super T>> list, T key) {
        if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
            return Collections.indexedBinarySearch(list, key);
        else
            return Collections.iteratorBinarySearch(list, key);
    }

    /**
     * 這裏使用了一個無符號右移(邏輯右移) int mid = (low + high) >>> 1;
     * 上面一行的 運算結果 爲((low + high)) 的一半
     * 所以這個查找也叫對半查找
     */
    private static <T>
    int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
    {
        int low = 0;
        int high = list.size()-1;
        ListIterator<? extends Comparable<? super T>> i = list.listIterator();

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = get(i, mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

    //遍歷查找
    private static <T> T get(ListIterator<? extends T> i, int index) {
        T obj = null;
        int pos = i.nextIndex();
        if (pos <= index) {
            do {
                obj = i.next();
            } while (pos++ < index);
        } else {
            do {
                obj = i.previous();
            } while (--pos > index);
        }
        return obj;
    }

 

shuffle:隨機排序

public static void shuffle(List<?> list) {
        Random rnd = r;
        if (rnd == null)
            r = rnd = new Random(); // harmless race.
        shuffle(list, rnd);
    }

reverse:倒序

public static void reverse(List<?> list) {
        int size = list.size();
        if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
            for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
                swap(list, i, j);
        } else {
            // instead of using a raw type here, it's possible to capture
            // the wildcard but it will require a call to a supplementary
            // private method
            ListIterator fwd = list.listIterator();
            ListIterator rev = list.listIterator(size);
            for (int i=0, mid=list.size()>>1; i<mid; i++) {
                Object tmp = fwd.next();
                fwd.set(rev.previous());
                rev.set(tmp);
            }
        }
    }

copy:從一個集合複製到另一個集合

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章