2012-01-04 63 views
4

我有一个SplObjectStorage实例,它存储要在容器中呈现的元素对象。我希望能够有效地添加和删除商店中任意位置的对象。重新组织SplObjectStorage实例的子项

例子:

<?php 
$store = new SplObjectStorageWrapper; 
$obj1 = new Obj; 
$obj2 = new Obj; 
$obj3 = new Obj; 

$store->attach($obj1); 
$store->attach($obj2); 
$store->insertAtIndex($obj3, 1); 

//Storage should now be organized as $obj1, $obj3, $obj2 

我怎么会去实现insertAtIndex方法?我是否使用LimitIterator在某个位置后分离并重新连接儿童?事实证明,使用基于阵列的对象存储要比实例慢得多。

,我想实现的其他方法包括removeAtIndex(integer)indexOf(object)

+0

是否需要SplObjectStorage的Set部分,例如:没有重复?如果不是,请尝试使用SplPriorityQueue类。它与'insertAt'完全不一样,但对于你的用例可能足够好了。 – Gordon 2012-01-04 18:31:10

+0

戈登,不是真的。但是由于性能的巨大差异,我需要使用SplObjectStorage而不是基于阵列的解决方案。 – Kenaniah 2012-01-04 18:31:16

回答

3

原来最简单的(显然最有效的)的方式来做到这一点是延长SplObjectStorage并利用LimitIterator。下面的代码示例:

<?php 
/** 
* Extends the SplObjectStorage class to provide index functions 
*/ 
class ObjectStorage extends SplObjectStorage { 

    /** 
    * Returns the index of a given object, or false if not found 
    * @param object $object 
    */ 
    function indexOf($object){ 

     if(!$this->contains($object)) return false; 

     foreach($this as $index => $obj) if($obj === $object) return $index; 

    } 

    /** 
    * Returns the object at the given index 
    */ 
    function itemAtIndex($index){ 

     $it = new LimitIterator($this, $index, 1); 
     foreach($it as $obj) return $obj; 

    } 

    /** 
    * Returns the sequence of objects as specified by the offset and length 
    * @param int $offset 
    * @param int $length 
    */ 
    function slice($offset, $length){ 

     $out = array(); 
     $it = new LimitIterator($this, $offset, $length); 
     foreach($it as $obj) $out[] = $obj; 
     return $out; 

    } 

    /** 
    * Inserts an object (or an array of objects) at a certain point 
    * @param mixed $object A single object or an array of objects 
    * @param integer $index 
    */ 
    function insertAt($object, $index){ 

     if(!is_array($object)) $object = array($object); 

     //Check to ensure that objects don't already exist in the collection 
     foreach($object as $k => $obj): 
      if($this->contains($obj)) unset($object[$k]); 
     endforeach; 

     //Do we have any objects left? 
     if(!$object) return; 

     //Detach any objects at or past this index 
     $remaining = array(); 
     if($index < $this->count()): 
      $remaining = $this->slice($index, $this->count() - $index); 
      foreach($remaining as $obj) $this->detach($obj); 
     endif; 

     //Add the new objects we're splicing in 
     foreach($object as $obj) $this->attach($obj); 

     //Attach the objects we previously detached 
     foreach($remaining as $obj) $this->attach($obj); 

    } 

    /** 
    * Removes the object at the given index 
    * @param integer $index 
    */ 
    function removeAt($index){ 

     $this->detach($this->itemAtIndex($index)); 

    } 

}