成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java容器類研究4:ArrayList

xfee / 2355人閱讀

摘要:顯然,開發(fā)人員認為通過下標遍歷的數(shù)組結(jié)構(gòu)更加高效。在進行分裂時,原始保留中部至末尾的元素,新的保留原起始位置到中部的元素。同樣,也會進行重新賦值,使得在使用前與保持一致。在遍歷時,會調(diào)用方法,將動作施加于元素之上。

java.util.ArrayList

ArrayList繼承自AbstractList,AbstractList為隨機訪問數(shù)據(jù)的結(jié)構(gòu),如數(shù)組提供了基本實現(xiàn),并且提供了Iterator。首先看AbstractList實現(xiàn)了什么方法。

AbstractList AbstractList里可以存儲null嗎

null可以作為一項存儲在ArrayList中。ListIterator是遍歷訪問AbstractList中元素較好的方式,需要注意獲取元素序號的方法是previousIndex,而不是nextIndex,因為之前有next方法的調(diào)用。還有,這里判斷兩個元素是否相等采用的是equals方法,而不是==

 public int indexOf(Object o) {
        ListIterator it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }
ListIterator可以逆序訪問元素

從后向前遍歷AbstractList,過程恰恰相反。

 public int lastIndexOf(Object o) {
        ListIterator it = listIterator(size());
        if (o==null) {
            while (it.hasPrevious())
                if (it.previous()==null)
                    return it.nextIndex();
        } else {
            while (it.hasPrevious())
                if (o.equals(it.previous()))
                    return it.nextIndex();
        }
        return -1;
    }
Iterator遍歷元素時,被其它線程修改了怎么辦

如果在使用Iterator時,有其他線程嘗試去修改List的大小,會被發(fā)現(xiàn)且立即拋出異常。

可以看class Itr implements Iterator中,有屬性int expectedModCount = modCount;記錄著期望的數(shù)組大小,如果不一致,會拋出ConcurrentModificationException。

Iterator在AbstractList中如何實現(xiàn)的

有兩個游標分別記錄當(dāng)前指向的位置和上一次指向的位置。

       /**
         * Index of element to be returned by subsequent call to next.
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         */
        int lastRet = -1;
如何處理遍歷過程中,刪除list中的元素導(dǎo)致的序號偏移

Iterator可以正確的刪除AbstractList中的元素,并且保證訪問的順序的正確性。

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.remove(lastRet);
                if (lastRet < cursor)
                    cursor--;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

刪除的元素是上一個next調(diào)用后的元素,并且連續(xù)調(diào)用兩次remove會拋出異常,因為元素只能刪除一次,上次指向的元素已經(jīng)沒有了。

ListIterator可以添加元素

class ListItr extends Itr implements ListIterator中實現(xiàn)了向list添加元素的方法。添加的位置是上一次next返回的元素之后,下一個next之前。

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                AbstractList.this.add(i, e);
                lastRet = -1;
                cursor = i + 1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
ArrayList ArrayList用什么來存儲元素

就是用了一個簡單的數(shù)組。。。

/**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
如何節(jié)約ArrayList占用的空間

使用trimToSize函數(shù),將原始數(shù)組copy到適合大小的數(shù)組中。

/**
     * Trims the capacity of this ArrayList instance to be the
     * list"s current size.  An application can use this operation to minimize
     * the storage of an ArrayList instance.
     */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
用Iterator遍歷ArrayList真的高效嗎

不可否認,Iterator為遍歷一個集合提供了統(tǒng)一的接口,用戶可以忽略集合內(nèi)部的具體實現(xiàn),但是過多的封裝會導(dǎo)致效率的降低。顯然,Java開發(fā)人員認為通過下標遍歷ArrayList的數(shù)組結(jié)構(gòu)更加高效。所以重寫了indexOf和lastIndexOf。

    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
ArrayList的clone是淺copy

ArrayList只是新建了數(shù)組,copy了元素的引用,元素本身沒有進行copy。clone方法為什么不new一個ArrayList對象,而是調(diào)用了Object類的clone?因為Object的clone函數(shù)是native的,更高效。

/**
     * Returns a shallow copy of this ArrayList instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this ArrayList instance
     */
    public Object clone() {
        try {
            //使用Object的clone獲得一個對象
            ArrayList v = (ArrayList) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn"t happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
ArrayList存儲空間的擴展策略

如果list初始沒有元素,使用一個靜態(tài)的空數(shù)組。元素增加時,空間擴展為默認的10。在后續(xù)的擴展過程中,容量會每次增加原始大小的1/2。

ArrayList實現(xiàn)了自己的iterator

雖然AbstractList實現(xiàn)了iterator,但ArrayList似乎不太滿意,又重新實現(xiàn)了一遍。主要區(qū)別就是在獲取元素時,利用了數(shù)組結(jié)構(gòu)的優(yōu)勢,可以直接通過下標獲取元素,而不必通過調(diào)用方法。

用Spliterator分割遍歷ArrayList

ArrayList理所當(dāng)然的實現(xiàn)了自己的Spliterator,也就是ArrayListSpliterator。分割的策略簡而言之為:二分+延遲初始化。

ArrayListSpliterator有如下屬性:

this.list = list; // OK if null unless traversed 保存目標list
this.index = origin; //起始位置
this.fence = fence;  //終止位置
this.expectedModCount = expectedModCount;  //期望修改次數(shù),用來判斷運行時是否有其它線程修改

每次從中間開始分裂。在進行分裂時,原始spliterator保留中部至末尾的元素,新的spliterator保留原起始位置到中部的元素。

        public ArrayListSpliterator trySplit() {
            int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
            return (lo >= mid) ? null : // divide range in half unless too small
                new ArrayListSpliterator(list, lo, index = mid,
                                            expectedModCount);
        }

在第一次創(chuàng)建spliterator時,fence被初始為-1,所以在實際使用spliterator時,getFence才會被調(diào)用,從而fence才會被賦值為數(shù)組大小。同樣,expectedModCount也會進行重新賦值,使得spliterator在使用前與list保持一致。

         private int getFence() { // initialize fence to size on first use
            int hi; // (a specialized variant appears in method forEach)
            ArrayList lst;
            if ((hi = fence) < 0) {
                if ((lst = list) == null)
                    hi = fence = 0;
                else {
                    expectedModCount = lst.modCount;
                    hi = fence = lst.size;
                }
            }
            return hi;
        }

在遍歷list時,會調(diào)用方法tryAdvance,將動作施加于元素之上。該方法會檢查是否有其它線程的修改,在ArrayList中,有大量方法都會使用modCount來記錄修改,或者判斷是否在方法執(zhí)行時有其它線程的修改,目前看來,用這個方法來檢測是否有并行修改使得list結(jié)構(gòu)變化是有效的,可以避免并行修改帶來的一些問題。

        public boolean tryAdvance(Consumer action) {
            if (action == null)
                throw new NullPointerException();
            int hi = getFence(), i = index;
            if (i < hi) {
                index = i + 1;
                @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
                action.accept(e);
                if (list.modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                return true;
            }
            return false;
        }

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/67747.html

相關(guān)文章

  • Java容器研究6:Vector

    摘要:不同點是線程安全的,方法有關(guān)鍵字修飾。容量增長策略默認的增長策略是每次在原容量的基礎(chǔ)上。的怎么做到線程安全的實現(xiàn)了自己的,為了保證并發(fā)線程安全的共享一個,開發(fā)者在等方法中也加入了。與類繼承自,的實現(xiàn)不止一種方式,比如。 java.util.Vector Vector與ArrayList的異同 相同點:隨機存取,可通過位置序號直接獲取數(shù)據(jù)。都是通過一個數(shù)組來存放元素。 不同點:Vecto...

    Charles 評論0 收藏0
  • Java容器研究2:List

    摘要:在中引入該方法,用來替換中的每個元素。的方法,只能在或之后使用,也就是確保當(dāng)前指向了一個存在的項。這里使用了,該方法可以將非對象先映射為型,然后進行比較。存儲的是有序的數(shù)組,所以劃分時會按照順序進行劃分。 java.util.List replaceAll 在Java8中引入該方法,用來替換list中的每個元素。 default void replaceAll(UnaryOpe...

    zzzmh 評論0 收藏0
  • Java 基礎(chǔ) | Collection 集合概覽

    摘要:說到復(fù)盤基礎(chǔ),并不是所有的都會復(fù)盤,沒那個時間更沒那個必要。比如,一些基礎(chǔ)的語法以及條件語句,極度簡單。思前想后,我覺得整個計劃應(yīng)該從集合開始,而復(fù)盤的方式就是讀源碼。通常,隊列不允許隨機訪問隊列中的元素。 ?showImg(https://segmentfault.com/img/remote/1460000020029737?w=1080&h=711); 老讀者都知道,我是自學(xué)轉(zhuǎn)行...

    codergarden 評論0 收藏0
  • Java容器研究1:Collection

    摘要:集合類關(guān)系是和的父接口。相等必須是對稱的,約定只能和其它相等,亦然。和接口在中引入,這個單詞是和的合成,用來分割集合以給并行處理提供方便。這些并不立即執(zhí)行,而是等到最后一個函數(shù),統(tǒng)一執(zhí)行。 集合類關(guān)系: Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ...

    AprilJ 評論0 收藏0
  • 集合Collection總覽

    前言 聲明,本文使用的是JDK1.8 從今天開始正式去學(xué)習(xí)Java基礎(chǔ)中最重要的東西--->集合 無論在開發(fā)中,在面試中這個知識點都是非常非常重要的,因此,我在此花費的時間也是很多,得參閱挺多的資料,下面未必就做到日更了... 當(dāng)然了,如果講得有錯的地方還請大家多多包涵并不吝在評論去指正~ 一、集合(Collection)介紹 1.1為什么需要Collection Java是一門面向?qū)ο蟮恼Z言,...

    FullStackDeveloper 評論0 收藏0

發(fā)表評論

0條評論

xfee

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<