30万件のデータから「値が 20 〜 25 の値の間のものをとってこい」とかが0ミリ秒とかでとってこれちゃう【ちょっぱやキーバーリューストア】作ったよ!

今回つくったActionScript3用「KeyValueStore」は
30万件のデータからのサーチでも、結果を約0ミリ秒でとってくることができます。

  • オブジェクト(DisplayObjectとかなんでも)をキーにして値を管理
  • 「ある値からある値の間のオブジェクト」とか「ある値のオブジェクト」などの参照系処理が高速にできる
  • 更新系処理もそこそこ高速
  • 一度に1万件くらいの挿入であれば結構一瞬
  • データ件数が多くなってもパフォーマンスがほとんど落ちない

のような特徴があります。

テスト結果

テストしたのは、

データ件数   300000件
   0〜30000のランダムな値

を登録した状態でのテストです。
挿入に関しては30万件挿入だとさすがに、かなり時間がかかってしまうので、
実際に大量にデータ挿入を行う時は、5000〜10000件程度づつ分割して挿入していくと良いと思います。



値が20〜30の間のデータを探すのに
リストをforループで探した場合と今回のクラスを使用した場合で比較しました



結果は・・・・



for loop   230msec
KeyValueStore   0msec

ActionScriptなのでmsec以下の精度で測れていません。マイクロ秒とかとれたっけ?

相当早いです!

ソース

配布ページとかめんどかったのでとりあえず。ソースをそのまま・・・・
そのうちどっかにおきます

/**
 * copyright kaw( http://d.hatena.ne.jp/toytools/ )
 */
package net.toytools.core.data 
{
    import flash.utils.Dictionary;
    
    /**
     * Objectなど何でもキーにして、値を管理します。
     * ある値からある値の間に入っているオブジェクトのリストなどを
     * ちょっぱやで取得できます。
     * @author kaw( toytools ).
     */
    public class KeyValueStore 
    {
        
        //------- CONST ------------------------------------------------------------------------
        //------- MEMBER ------------------------------------------------------------------------
        private var _list:Array;
        private var _objectDict:Dictionary;
        //------- PUBLIC ---------------------------------------------------------------------
        /**
         * 
         */
        public function KeyValueStore() 
        {
            clear();
        }
        
        
        /**
         * 内部データを全て削除.
         */
        public function clear():void {
            _list = new Array();
            _objectDict = new Dictionary();
        }
        
        
        /**
         * データの追加.
         * @param	obj
         * @param	numericValue
         */
        public function add( obj:* , numericValue:Number ):void {
            if ( _objectDict[obj] != undefined ) {
                throw new Error("KeyValueStore.add()  Error::既に存在するキーです.");
            }
            var keyValueStoreItem:KeyValueStoreItem = new KeyValueStoreItem( obj , numericValue );
            _objectDict[obj] = keyValueStoreItem;
            var bsResult:BinarySearchResult = _doCustomBinarySearch( numericValue );
            var insertTarget:int = ( bsResult.isFinded() ) ? bsResult.getMinIndex() : bsResult.getMaxIndex();
            _list.splice( insertTarget , 0 , keyValueStoreItem );
        }
        
        
        /**
         * 値の更新.
         * @param	obj
         * @param	numericValue
         */
        public function update( obj:* , numericValue:Number ):void {
            var keyValueStoreItem:KeyValueStoreItem = _objectDict[obj] as KeyValueStoreItem;
            if ( keyValueStoreItem == null ) {
                throw new Error("KeyValueStore.update()  更新対象のオブジェクトが見つかりません.");
            }
            remove( obj );
            add( obj , numericValue );
        }
        
        
        /**
         * 削除.
         * @param	obj
         */
        public function remove( obj:* ):void {
            var keyValueStoreItem:KeyValueStoreItem = _objectDict[obj] as KeyValueStoreItem;
            delete _objectDict[obj];
            if ( keyValueStoreItem == null ) {
                return;
            }
            var bsResult:BinarySearchResult = _doCustomBinarySearch( keyValueStoreItem.getNumericValue() );
            if ( !bsResult.isFinded() ) {
                return;
            }
            for ( var i:int = bsResult.getMinIndex() ; i <= bsResult.getMaxIndex() ; i++ ) {
                if ( KeyValueStoreItem( _list[i] ).getObj() == obj ) {
                    _list.splice( i , 1 );
                    return;
                }
            }
        }
        
        
        
        
        /**
         * NumericValueが特定の値の間のObjectのリストを取得する.
         * @param	minNumericValue
         * @param	maxNumericValue
         * @return
         */
        public function between( minNumericValue:Number , maxNumericValue:Number ):Array {
            var minBSResult:BinarySearchResult = _doCustomBinarySearch( minNumericValue );
            var maxBSResult:BinarySearchResult = _doCustomBinarySearch( maxNumericValue );
            
            var startIndex:int = minBSResult.isFinded() ? minBSResult.getMinIndex() : minBSResult.getMaxIndex();
            var endIndex:int   = maxBSResult.isFinded() ? maxBSResult.getMaxIndex() : maxBSResult.getMinIndex();
            
            var result:Array = new Array();
            for ( var i:int = startIndex ; i <= endIndex ; i++ ) {
                result.push( KeyValueStoreItem( _list[i] ).getObj() );
            }
            return result;
        }
        
        
        /**
         * ある特定のNumericValueのオブジェクトのリストを取得する.
         * @param	numericValue
         * @return
         */
        public function getObjectList( numericValue:Number ):Array {
            var result:Array = new Array();
            var bsResult:BinarySearchResult = _doCustomBinarySearch( numericValue );
            if ( !bsResult.isFinded() ) {
                return result;
            }
            for ( var i:int = bsResult.getMinIndex() ; i <= bsResult.getMaxIndex() ; i++ ) {
                result.push( KeyValueStoreItem( _list[i] ).getObj() );
            }
            return result;
        }
        
        
        /**
         * オブジェクトのNumericValueを取得する.
         * @param	obj
         */
        public function getNumericValue( obj:* ):Number {
            var keyValueStoreItem:KeyValueStoreItem = _objectDict[obj] as KeyValueStoreItem;
            if ( keyValueStoreItem == null ) {
                throw new Error("KeyValueStore.getNumericValue()  取得対象のオブジェクトが見つかりません.");
            }
            return keyValueStoreItem.getNumericValue();
        }
        
        
        //------- PRIVATE ---------------------------------------------------------------------
        /**
         * 
         * @param	numericValue
         * @return
         */
        private function _doCustomBinarySearch( numericValue:Number ):BinarySearchResult {
            var len:int = _list.length - 1;
            var min:int  = 0;
            var max:int  = len;
            var mid:int = Math.floor( (min + max) / 2 );
            //探索
            while(min <= max && KeyValueStoreItem( _list[mid] ).getNumericValue() != numericValue){
                if( KeyValueStoreItem( _list[mid] ).getNumericValue() > numericValue){
                    max = mid - 1;
                }else{
                    min = mid + 1;
                }
                mid = Math.floor( (min + max) / 2 );
            }
            //結果返却
            if ( min <= max ) {
                min = mid;
                max = mid;
                //最小INDEXを探す.
                while ( 0 < min && KeyValueStoreItem( _list[ min - 1 ] ).getNumericValue() == numericValue ) {
                    min--;
                }
                //最大INDEXを探す.
                while ( max < len && KeyValueStoreItem( _list[ max + 1 ] ).getNumericValue() == numericValue ) {
                    max++;
                }
                return new BinarySearchResult( min , max , true );
            }else {
                return new BinarySearchResult( Math.min( min , max ) , Math.max( min , max ) , false );
                
            }
        }
        
        //------- INTERNAL ---------------------------------------------------------------------
        
    }
    
}

/**
 * 内部バイナリーサーチの結果.
 */
class BinarySearchResult {
    private var _minIndex:int;
    private var _maxIndex:int;
    private var _isFinded:Boolean;
    public function BinarySearchResult( minIndex:int , maxIndex:int , isFinded:Boolean ):void {
        _minIndex = minIndex;
        _maxIndex = maxIndex;
        _isFinded = isFinded;
    }
    
    public function isFinded():Boolean {
        return _isFinded;
    }
    
    public function getMinIndex():int {
        return _minIndex;
    }
    public function getMaxIndex():int {
        return _maxIndex;
    }
    public function toString():String {
        return "min : " + _minIndex + "  max : " + _maxIndex;
    }
}



/**
 * KeyValueStore用に作成された内部保持クラス.
 */
class KeyValueStoreItem{
    private var _obj:*;
    private var _numericValue:Number;
    
    public function KeyValueStoreItem( obj:* , numericValue:Number ):void {
        _obj          = obj;
        _numericValue = numericValue;
    }
    
    public function getNumericValue():Number {
        return _numericValue;
    }
    
    public function getObj():*{
        return _obj;
    }
    
    public function toString():String {
        return "NumericValue :: " + _numericValue + "    " + _obj;
    }
    
}