Change Dir

          先知cd——熱愛生活是一切藝術的開始

          統計

          留言簿(18)

          積分與排名

          “牛”們的博客

          各個公司技術

          我的鏈接

          淘寶技術

          閱讀排行榜

          評論排行榜

          分享代碼系列——vlist

          vlist是一種列表的實現。結構如下圖:

           (圖來源wikipedia

          類似鏈接表的結構,但是,不是線性的。它的結構基于一種2的冪次擴展,第一個鏈接節點包含了列表的前一半數據,第二個包含了剩下一半的一半,依次遞歸。節點的基本結構不像LinkedList的節點是雙端隊列,每個VListCell包含了下個節點的指針mNext和前一個節點的指針mPrev,同時內置一個數組mElems用來存放當前節點的數據集合,包含一個數字指針指向當前數組元素的位置。舉個例子,如果有個Vlist包含10個元素,分別是1-10的整數,而且是按升序順序插入的,那么vlist的結構數據時這樣的:

          VList基于數組實現,在add操作時,每次會把元素插入到list的最前面一個節點內的mElems的最后一個位置,首先判斷head,如果head的元素數組已經滿了,那么就增加一個頭節點并擴容其elems數組為2倍,然后插入到位置指針所指向的地方去,時間是O(1)的。而在get操作時,要首先定位第n個元素的位置,會進行一次locate定位操作,接著直接返回數組中的該locate位置即可。定位操作實質是二分的,但是因為VList本身就是一個單向的二分表,因此順序判斷即可,時間復雜度是平均O(1)和最壞情況O(log n)。對應get的set操作,復雜度和步驟完全一樣。當然最最惡心的還是remove操作了,因為基于數組,且本身結構有意義(特定的),所以刪除會復雜一些,首先一個O(log n)的locate定位,找到元素后,刪掉之后,是把它之前的所有元素后移一位,當然這個移位操作并不是特別復雜,只要把當前節點的全部后移,然后如果當前節點有前驅節點,那么前驅的最后一個元素覆蓋當前節點第一個元素,如此反復直到當前節點指針為空結束,時間復雜度是O(n)的。

          我做了一個perf test來測試性能,發現這個不倫不類的list在arralist和linkedlist面前顯得是脆弱的。那它的作用體現在哪里呢?簡單的設計和良好的結構,滿足add和get的平衡,對于list后半部分的數據的操作具有很好的性能,像個數組,但是又和其前半部分有快速的鏈接關系,對于其數組的不可變性也是最好的用于函數式編程的典范(來源于wikipedia的翻譯)

          源代碼如下,繼承了jdk中的AbstractList<T>:

             1: public final class VList<T> extends AbstractList<T> {
             2:  
             3:     /**
             4:      * A single cell in the VList implementation.
             5:      */
             6:     private static final class VListCell<T> {
             7:         public final T[] mElems;
             8:         public final VListCell<T> mNext;
             9:  
            10:         /*
            11:          * This field is not mutable because when new elements are added/deleted
            12:          * from the main list, the previous pointer needs to be updated.
            13:          * However, next links never change because the list only grows in one
            14:          * direction.
            15:          */
            16:         public VListCell<T> mPrev;
            17:  
            18:         /*
            19:          * The number of unused elements in this cell. Alternatively, you can
            20:          * think of this as the index in the array in which the first used
            21:          * element appears. Both interpretations are used in this
            22:          * implementation.
            23:          */
            24:         public int mFreeSpace;
            25:  
            26:         /**
            27:          * Constructs a new VListCell with the specified number of elements and
            28:          * specified next element.
            29:          * 
            30:          * @param numElems
            31:          *            The number of elements this cell should have space for.
            32:          * @param next
            33:          *            The cell in the list of cells that follows this one.
            34:          */
            35:         public VListCell(int numElems, VListCell<T> next) {
            36:             mElems = (T[]) new Object[numElems];
            37:             mNext = next;
            38:             mPrev = null;
            39:  
            40:             /* Update the next cell to point back to us. */
            41:             if (next != null)
            42:                 next.mPrev = this;
            43:  
            44:             /* We have free space equal to the number of elements. */
            45:             mFreeSpace = numElems;
            46:         }
            47:     }
            48:  
            49:     /**
            50:      * A utility struct containing information about where an element is in the
            51:      * VList. Methods that need to manipulate individual elements of the list
            52:      * use this struct to communicate where in the list to look for that
            53:      * element.
            54:      */
            55:     private static final class VListLocation<T> {
            56:         public final VListCell<T> mCell;
            57:         public final int mOffset;
            58:  
            59:         public VListLocation(VListCell<T> cell, int offset) {
            60:             mCell = cell;
            61:             mOffset = offset;
            62:         }
            63:     }
            64:  
            65:     /*
            66:      * Pointer to the head of the VList, which contains the final elements of
            67:      * the list.
            68:      */
            69:     private VListCell<T> mHead;
            70:  
            71:     /* Cached total number of elements in the array. */
            72:     private int mSize;
            73:  
            74:     /**
            75:      * Adds a new element to the end of the array.
            76:      * 
            77:      * @param elem
            78:      *            The element to add.
            79:      * @return true
            80:      */
            81:     @Override
            82:     public boolean add(T elem) {
            83:         /* If no free space exists, add a new element to the list. */
            84:         if (mHead == null || mHead.mFreeSpace == 0)
            85:             mHead = new VListCell<T>(mHead == null ? 1
            86:                     : mHead.mElems.length * 2, mHead);
            87:  
            88:         /* Prepend this element to the current cell. */
            89:         mHead.mElems[(mHead.mFreeSpace--) - 1] = elem;
            90:         ++mSize;
            91:  
            92:         /* Success! */
            93:         return true;
            94:     }
            95:  
            96:     /**
            97:      * Given an absolute offset into the VList, returns an object describing
            98:      * where that object is in the VList.
            99:      * 
           100:      * @param index
           101:      *            The index into the VList.
           102:      * @return A VListLocation object holding information about where that
           103:      *         element can be found.
           104:      */
           105:     private VListLocation<T> locateElement(int index) {
           106:         /* Bounds-check. */
           107:         if (index >= size() || index < 0)
           108:             throw new IndexOutOfBoundsException("Position " + index + "; size "
           109:                     + size());
           110:  
           111:         /*
           112:          * Because the list is stored with new elements in front and old
           113:          * elements in back, we'll invert the index so that 0 refers to the
           114:          * final element of the array and size() - 1 refers to the first
           115:          * element.
           116:          */
           117:         index = size() - 1 - index;
           118:  
           119:         /*
           120:          * Scan across the cells, looking for the first one that can hold our
           121:          * entry. We do this by continuously skipping cells until we find one
           122:          * that can be sure to hold this element.
           123:          * 
           124:          * Note that each cell has mElems.length elements, of which mFreeSpace
           125:          * is used. This means that the total number of used elements in each
           126:          * cell is mElems.length - mFreeSpace.
           127:          */
           128:         VListCell<T> curr = mHead;
           129:         while (index >= curr.mElems.length - curr.mFreeSpace) {
           130:             /* Skip past all these elements. */
           131:             index -= curr.mElems.length - curr.mFreeSpace;
           132:             curr = curr.mNext;
           133:         }
           134:  
           135:         /*
           136:          * We're now in the correct location for what we need to do. The element
           137:          * we want can be found by indexing the proper amount beyond the free
           138:          * space.
           139:          */
           140:         return new VListLocation<T>(curr, index + curr.mFreeSpace);
           141:     }
           142:  
           143:     /**
           144:      * Scans for the proper location in the cell list for the element, then
           145:      * returns the element at that position.
           146:      * 
           147:      * @param index
           148:      *            The index at which to look up the element.
           149:      * @return The element at that position.
           150:      */
           151:     @Override
           152:     public T get(int index) {
           153:         VListLocation<T> where = locateElement(index);
           154:  
           155:         /* Return the element in the current position of this array. */
           156:         return where.mCell.mElems[where.mOffset];
           157:     }
           158:  
           159:     /**
           160:      * Returns the cached size.
           161:      * 
           162:      * @return The size of the VList.
           163:      */
           164:     @Override
           165:     public int size() {
           166:         return mSize;
           167:     }
           168:  
           169:     /**
           170:      * Sets an element at a particular position to have a particular value.
           171:      * 
           172:      * @param index
           173:      *            The index at which to write a new value.
           174:      * @param value
           175:      *            The value to write at that position.
           176:      * @return The value originally held at that position.
           177:      */
           178:     @Override
           179:     public T set(int index, T value) {
           180:         VListLocation<T> where = locateElement(index);
           181:  
           182:         /* Cache the element in the current position of this array. */
           183:         T result = where.mCell.mElems[where.mOffset];
           184:         where.mCell.mElems[where.mOffset] = value;
           185:         return result;
           186:     }
           187:  
           188:     /**
           189:      * Removes the element at the specified position from the VList, returning
           190:      * its value.
           191:      * 
           192:      * @param index
           193:      *            The index at which the element should be removed.
           194:      * @return The value held at that position.
           195:      */
           196:     @Override
           197:     public T remove(int index) {
           198:         VListLocation<T> where = locateElement(index);
           199:  
           200:         /* Cache the value that will be removed. */
           201:         T result = where.mCell.mElems[where.mOffset];
           202:  
           203:         /* Invoke the helper to do most of the work. */
           204:         removeAtPosition(where);
           205:  
           206:         return result;
           207:     }
           208:  
           209:     /**
           210:      * Removes the element at the indicated VListLocation.
           211:      * 
           212:      * @param where
           213:      *            The location at which the element should be removed.
           214:      */
           215:     private void removeAtPosition(VListLocation<T> where) {
           216:         /*
           217:          * Scan backward across the blocks after this element, shuffling array
           218:          * elements down a position and copying the last element of the next
           219:          * block over to fill in the top.
           220:          * 
           221:          * The variable shuffleTargetPosition indicates the first element of the
           222:          * block that should be overwritten during the shuffle-down. In the
           223:          * first block, this is the position of the element that was
           224:          * overwritten. In all other blocks, it's the last element.
           225:          */
           226:         VListCell<T> curr = where.mCell;
           227:         for (int shuffleTargetPosition = where.mOffset; curr != null; curr = curr.mPrev, shuffleTargetPosition = (curr == null ? 0
           228:                 : curr.mElems.length - 1)) {
           229:             /*
           230:              * Shuffle down each element in the current array on top of the
           231:              * target position. Note that in the final block, this may end up
           232:              * copying a whole bunch of null values down. This is more work than
           233:              * necessary, but is harmless and doesn't change the asymptotic
           234:              * runtime (since the last block has size O(n)).
           235:              */
           236:             for (int i = shuffleTargetPosition - 1; i >= 0; --i)
           237:                 curr.mElems[i + 1] = curr.mElems[i];
           238:  
           239:             /*
           240:              * Copy the last element of the next array to the top of this array,
           241:              * unless this is the first block (in which case there is no next
           242:              * array).
           243:              */
           244:             if (curr.mPrev != null)
           245:                 curr.mElems[0] = curr.mPrev.mElems[curr.mPrev.mElems.length - 1];
           246:         }
           247:  
           248:         /*
           249:          * The head just lost an element, so it has some more free space. Null
           250:          * out the lost element and increase the free space.
           251:          */
           252:         ++mHead.mFreeSpace;
           253:         mHead.mElems[mHead.mFreeSpace - 1] = null;
           254:  
           255:         /* The whole list just lost an element. */
           256:         --mSize;
           257:  
           258:         /* If the head is entirely free, remove it from the list. */
           259:         if (mHead.mFreeSpace == mHead.mElems.length) {
           260:             mHead = mHead.mNext;
           261:  
           262:             /*
           263:              * If there is at least one block left, remove the previous block
           264:              * from the linked list.
           265:              */
           266:             if (mHead != null)
           267:                 mHead.mPrev = null;
           268:         }
           269:     }
           270:  
           271:     /**
           272:      * A custom iterator class that traverses the elements of this container in
           273:      * an intelligent way. The normal iterator will call get repeatedly, which
           274:      * is slow because it has to continuously scan for the proper location of
           275:      * the next element. This iterator works by traversing the cells as a proper
           276:      * linked list.
           277:      */
           278:     private final class VListIterator implements Iterator<T> {
           279:         /*
           280:          * The cell and position in that cell that we are about to visit. We
           281:          * maintain the invariant that if there is a next element, mCurrCell is
           282:          * non-null and conversely that if mCurrCell is null, there is no next
           283:          * element.
           284:          */
           285:         private VListCell<T> mCurrCell;
           286:         private int mCurrIndex;
           287:  
           288:         /*
           289:          * Stores whether we have something to remove (i.e. whether we've called
           290:          * next() without an invervening remove()).
           291:          */
           292:         private boolean mCanRemove;
           293:  
           294:         /**
           295:          * Constructs a new VListIterator that will traverse the elements of the
           296:          * containing VList.
           297:          */
           298:         public VListIterator() {
           299:             /*
           300:              * Scan to the tail using the "pointer chase" algorithm. When this
           301:              * terminates, prev will hold a pointer to the last element of the
           302:              * list.
           303:              */
           304:             VListCell<T> curr, prev;
           305:             for (curr = mHead, prev = null; curr != null; prev = curr, curr = curr.mNext)
           306:                 ;
           307:  
           308:             /* Set the current cell to the tail. */
           309:             mCurrCell = prev;
           310:  
           311:             /*
           312:              * If the tail isn't null, it must be a full list of size 1. Set the
           313:              * current index appropriately.
           314:              */
           315:             if (mCurrCell != null)
           316:                 mCurrIndex = 0;
           317:         }
           318:  
           319:         /**
           320:          * As per our invariant, returns whether mCurrCell is non-null.
           321:          */
           322:         public boolean hasNext() {
           323:             return mCurrCell != null;
           324:         }
           325:  
           326:         /**
           327:          * Advances the iterator and returns the element it used to be over.
           328:          */
           329:         public T next() {
           330:             /* Bounds-check. */
           331:             if (!hasNext())
           332:                 throw new NoSuchElementException();
           333:  
           334:             /* Cache the return value; we'll be moving off of it soon. */
           335:             T result = mCurrCell.mElems[mCurrIndex];
           336:  
           337:             /* Back up one step. */
           338:             --mCurrIndex;
           339:  
           340:             /*
           341:              * If we walked off the end of the buffer, advance to the next
           342:              * element of the list.
           343:              */
           344:             if (mCurrIndex < mCurrCell.mFreeSpace) {
           345:                 mCurrCell = mCurrCell.mPrev;
           346:  
           347:                 /*
           348:                  * Update the next get location, provided of course that we
           349:                  * didn't just walk off the end of the list.
           350:                  */
           351:                 if (mCurrCell != null)
           352:                     mCurrIndex = mCurrCell.mElems.length - 1;
           353:             }
           354:  
           355:             /* Since there was indeed an element, we can remove it. */
           356:             mCanRemove = true;
           357:  
           358:             return result;
           359:         }
           360:  
           361:         /**
           362:          * Removes the last element we visited.
           363:          */
           364:         public void remove() {
           365:             /* Check whether there's something to remove. */
           366:             if (!mCanRemove)
           367:                 throw new IllegalStateException(
           368:                         "remove() without next(), or double remove().");
           369:  
           370:             /* Clear the flag saying we can do this. */
           371:             mCanRemove = false;
           372:  
           373:             /*
           374:              * There are several cases to consider. If the current cell is null,
           375:              * we've walked off the end of the array, so we want to remove the
           376:              * very last element. If the current cell isn't null and the cursor
           377:              * is in the middle, remove the previous element and back up a step.
           378:              * If the current cell isn't null and the cursor is at the front,
           379:              * remove the element one step before us and back up a step.
           380:              */
           381:  
           382:             /* Case 1. */
           383:             if (mCurrCell == null)
           384:                 VList.this.remove(size() - 1);
           385:             /* Case 2. */
           386:             else if (mCurrIndex != mCurrCell.mElems.length - 1) {
           387:                 /*
           388:                  * Back up a step, and remove the element at this position.
           389:                  * After the remove completes, the element here should be the
           390:                  * next element to visit.
           391:                  */
           392:                 ++mCurrIndex;
           393:                 removeAtPosition(new VListLocation<T>(mCurrCell, mCurrIndex));
           394:             }
           395:             /* Case 3. */
           396:             else {
           397:                 /*
           398:                  * Back up a step to the top of the previous list. We know that
           399:                  * the top will be at position 0, since all internal blocks are
           400:                  * completely full. We also know that we aren't at the very
           401:                  * front of the list, since if we were, then the call to next()
           402:                  * that enabled this call would have pushed us to the next
           403:                  * location.
           404:                  */
           405:                 mCurrCell = mCurrCell.mNext;
           406:                 mCurrIndex = 0;
           407:                 removeAtPosition(new VListLocation<T>(mCurrCell, mCurrIndex));
           408:             }
           409:         }
           410:     }
           411:  
           412:     /**
           413:      * Returns a custom iterator rather than the default.
           414:      */
           415:     @Override
           416:     public Iterator<T> iterator() {
           417:         return new VListIterator();
           418:     }
           419:  
           420: }

          參考資料:

          http://www.keithschwarz.com/interesting/code/?dir=vlist

          http://en.wikipedia.org/wiki/VList

          posted on 2012-04-15 12:29 changedi 閱讀(2589) 評論(1)  編輯  收藏 所屬分類: Java技術算法好代碼分享

          評論

          # re: 分享代碼系列——vlist 2012-04-17 08:39 tb

          資料不錯   回復  更多評論   

          主站蜘蛛池模板: 唐河县| 鸡东县| 广宁县| 乡宁县| 裕民县| 彰化市| 黔西| 新竹市| 外汇| 灵寿县| 贡嘎县| 阳东县| 石屏县| 金华市| 安国市| 温泉县| 广水市| 东乡| 赣州市| 黄骅市| 东丰县| 弥勒县| 高碑店市| 成武县| 长武县| 新晃| 农安县| 天门市| 蕲春县| 梁河县| 洛南县| 龙里县| 孟连| 甘谷县| 徐汇区| 洮南市| 承德市| 岑溪市| 湖南省| 鞍山市| 新闻|