• vlist java实现-转


    转自:http://www.blogjava.net/changedi/archive/2012/04/15/374226.html

    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

  • 相关阅读:
    JUnit之持续集成(CI,Continuous Integration)
    Junit初级编码(二)探索JUnit核心
    《opencv学习》 之 特征检测与匹配
    opencv小问题大智慧
    opencv3.1+contrib的配置大总结(配置了两天,遇到问题无数)
    《图像处理实例》 之 透视变换
    《图像处理实例》 之 物体计数
    《图像处理实例》 之 操作规则的圆
    《电路学习第三天》 之 彩扩机项目设计
    《图像处理实例》 之 提取特殊背景的直线
  • 原文地址:https://www.cnblogs.com/davidwang456/p/3761477.html
Copyright © 2020-2023  润新知