Java容器類研究3:AbstractCollection

ArrayList繼承自AbstractList,AbstractList爲「random access」的數組提供了基本的實現。隨機訪問數據是相對於順序訪問數據而言,例如鏈表的形式。AbstractSequentialList爲鏈表形式的順序訪問提供了基本實現。AbstractList提供了默認的隨機訪問數據的iterator。AbstractList繼承自AbstractCollection,AbstractCollection爲Collection提供了基本實現。java

java.util.AbstractCollection數組

<!--more-->安全

contains

AbstractCollection實現了查詢是否包含某一個元素的方法。最好使用Iterator遍歷集合中的元素,由於能夠屏蔽集合內部元素存儲的具體實現,而且根據不一樣的數據存儲特色,優化訪問策略。這裏還能夠正確查找null元素,須要注意的是對null元素的查詢須要特別的處理,有時候本身實現方法時,每每會忽略傳入參數爲null時的處理,致使方法沒法處理特殊狀況。併發

public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }

toArray

toArray方法將容器中的元素轉化爲數組的形式。這裏元素的複製,採用的是直接複製引用。這裏還考慮到了併發運算時,元素數量在複製時產生變化的狀況,當數量減小時,就用Arrays.copy()截取結果。當數量增長時,會調用finishToArray函數擴容。app

public Object[] toArray() {
        // Estimate size of array; be prepared to see more or fewer elements
        Object[] r = new Object[size()];
        Iterator<E> it = iterator();
        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) // fewer elements than expected
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }

在finishToArray函數中,對容量每次擴展1/2+1的大小,而且會檢查是否會超過設定的最大數組長度MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8。若是長度超過了限定值,則以原容量+1爲底線,返回一個最大容量。最後返回的數組也會修剪掉多餘的位置。dom

private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
        int i = r.length;
        while (it.hasNext()) {
            int cap = r.length;
            if (i == cap) {
                int newCap = cap + (cap >> 1) + 1;
                // overflow-conscious code
                if (newCap - MAX_ARRAY_SIZE > 0)
                    newCap = hugeCapacity(cap + 1);
                r = Arrays.copyOf(r, newCap);
            }
            r[i++] = (T)it.next();
        }
        // trim if overallocated
        return (i == r.length) ? r : Arrays.copyOf(r, i);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError
                ("Required array size too large");
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

這裏有一個toArray的重載,須要傳入一個數組做爲參數,據我觀察,目的是將集合中的元素存入指定的數組,重利用已有數組的存儲。若是元素過多,而目標數組容納不下,只能從新申請數組來進行存儲。函數

public <T> T[] toArray(T[] a) {
        // Estimate size of array; be prepared to see more or fewer elements
        int size = size();
        T[] r = a.length >= size ? a :
                  (T[])java.lang.reflect.Array
                  .newInstance(a.getClass().getComponentType(), size);
        Iterator<E> it = iterator();

        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) { // fewer elements than expected
                if (a == r) {
                    //利用的原數組,且元素不足,則補null表示結束
                    r[i] = null; // null-terminate
                } else if (a.length < i) {
                    //元素較少,但數量超過原始數組,只能用新數組
                    return Arrays.copyOf(r, i);
                } else {
                    //元素減小,致使原始數組能夠容納下,則copy回原始數組,折騰唄
                    System.arraycopy(r, 0, a, 0, i);
                    if (a.length > i) {
                        a[i] = null;
                    }
                }
                return a;
            }
            r[i] = (T)it.next();
        }
        // more elements than expected
        return it.hasNext() ? finishToArray(r, it) : r;
    }

removeAll

removeAll方法刪除目標集合中的全部重疊元素。Iterator能夠作到在遍歷時,安全的刪除元素,而經過index循環刪除則可能致使index偏移。優化

public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }

toString

AbstractCollection的toString實現,比較令我震驚的是e == this的做用。這裏爲何要判斷打印的內容是否爲本身自己?由於sb.append(e == this ? "(this Collection)" : e);會調用e的toString函數來生成字符串,若不加判斷,則會造成無限的toString遞歸調用。我猜測一下,估計當時toString的實現者沒有注意到該問題的存在,直到該bug出現,腦洞不大還真難想起來這個問題。ui

public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }
相關文章
相關標籤/搜索