framework/collections包 主要包含一些常用的数据结构,有
CTypeList
CTypeMap
CAttribute
Collection
CConfiguration
CList
CMap
CQueue
CStack
CListIterator
CMapIterator
CQueueIterator
CStackIterator1.CList
1.CList 把数组(数字索引)与对象结合使用。
$r[] = 'test';与$r->add('test')我觉得后者较好理解。
CList实现了几个接口,ArrayAccess Countable IteratorAggregate,
让$r数组对象,既能使用foreach循环读取(自定义的数组,而不是本来的只能遍历对象的公开属性)、又能使用count()函数,还能使用$r[]的方式访问。
CList的getIterator返回的是CListIterator,这个只是实现了Iterator接口。
2.CMap 把数组(字符串或数字索引)与对象结合使用
CMap与CList实现的接口一样。
CMapIterator与CListIterator类似,但是有点不同,CListIterator是数字索引,所以遍历的时候,可以用类似$i++之类的来移动指针,CMapIterator则不行。
CMapIterator的思路是把keys用array_keys取出来,放进一个数组,然后把数字和keys对应,遍历的问题就解决了。
3.CQueue 实现的接口 IteratorAggregate,Countable, 实现了几个方法dequeue() enqueue() peek(),靠的是array_shift()和array_push()两个方法。
CQueueIterator 与CListIterator没多大区别。
4.CStack与CQueue类似,只不过它是实现栈,实现pop() push() peek()方法,靠的是array_pop(),array_push()两个方法。
5.CTypeList是CList的加强版,它限制这个列表的item必须是某种类型的。
6.CTypeMap是CMap的加强版,它限制这个列表的item必须是某种类型的。
7.CConfiguration类似CMap,它专门处理配置信息的,从文件读取配置数组(loadFromFile()),并把它绑定到相应的对象上(applyTo())。
------------------------------------------------------------------------------------------------------------------------------
上面是之前写的笔记,今天整理整理,下面结合源码的接口讨论讨论
1.CList
class CList extends CComponent implements IteratorAggregate,ArrayAccess,Countable
{
private $_d=array(); //数据的容器,php里我目前知道的最多用来存临时数据的容器就只有数组,不知道有别的结构没。人家起名也够简洁的,一个d代表data
private $_c=0; //一看就懂,count,数据数组里有多少个元素,这个值会在增删的时候实时变化。
private $_r=false; //这个像个只读锁,可以把数据放到容器里面去,然后加个锁(有个setReadOnly()方法),
//此后在它的生存周期内容器内的数据就是只读的啦,后面的insertAt()和removeAt()方法都会去判断是否有加锁。
/**构造器,可以在构造的时候传入数据,并可以设置是否加只读锁*/
public function __construct($data=null,$readOnly=false)
{
if($data!==null)
$this->copyFrom($data);//copyFrom,作用就是把一个数据给拷到本身的数据容器数组里去,如果提供的是CList实例,就把它的数据导过来
$this->setReadOnly($readOnly);
}
/**是否只读*/
public function getReadOnly()
{
return $this->_r;
}
/**
* 加个锁
*/
protected function setReadOnly($value)
{
$this->_r=$value;
}
/**
* 返回一个Iterator遍历器,实现IteratorAggregate用的。遍历器用来干嘛的,foreach用的
*/
public function getIterator()
{
return new CListIterator($this->_d);
}
/**
* 元素总数,主要是因为实现Countable接口,可以用count($clist_instance)
*/
public function count()
{
return $this->getCount();
}
/**
* 功能同count,返回元素总数
*/
public function getCount()
{
return $this->_c;
}
/**
* 跟offsetGet方法一模一样
*/
public function itemAt($index)
{
if(isset($this->_d[$index]))
return $this->_d[$index];
else if($index>=0 && $index<$this->_c) // in case the value is null
return $this->_d[$index];
else
throw new CException(Yii::t('yii','List index "{index}" is out of bound.',
array('{index}'=>$index)));
}
/**
* 末尾加个元素
*/
public function add($item)
{
$this->insertAt($this->_c,$item);
return $this->_c-1;
}
/**
* 根据数字索引插入一个元素,这里主要用到了array_aplice,这个方法牛逼啊,删除替换都能搞,有大牛告诉我它内部怎么实现的吗?
*/
public function insertAt($index,$item)
{
if(!$this->_r)
{
if($index===$this->_c)
$this->_d[$this->_c++]=$item;
else if($index>=0 && $index<$this->_c)
{
array_splice($this->_d,$index,0,array($item));
$this->_c++;
}
else
throw new CException(Yii::t('yii','List index "{index}" is out of bound.',
array('{index}'=>$index)));
}
else
throw new CException(Yii::t('yii','The list is read only.'));
}
/**
* 根据数字索引删除一个元素,
*/
public function remove($item)
{
if(($index=$this->indexOf($item))>=0)
{
$this->removeAt($index);
return $index;
}
else
return false;
}
/**
* remove的实现,操作前判断是否加锁,看,这里又是用array_splice
*/
public function removeAt($index)
{
if(!$this->_r)
{
if($index>=0 && $index<$this->_c)
{
$this->_c--;
if($index===$this->_c)
return array_pop($this->_d);
else
{
$item=$this->_d[$index];
array_splice($this->_d,$index,1);
return $item;
}
}
else
throw new CException(Yii::t('yii','List index "{index}" is out of bound.',
array('{index}'=>$index)));
}
else
throw new CException(Yii::t('yii','The list is read only.'));
}
/**
* 清空数据,之前搞不懂为啥不直接根据是否有加锁进行操作,没加锁的话,直接把$_d清掉和$_c设置为0不就完了吗?不过现在想想,它这么做还是有道理的,把删除的操作交给删除的接口去干,
* 那么如果在删除接口操作的时候,它有实现一些其它的钩或者其它的逻辑,那些逻辑就能正确调用!
*/
public function clear()
{
for($i=$this->_c-1;$i>=0;--$i)
$this->removeAt($i);
}
/**
* 查查数据容器里有没有这一项,跟查花名册似的
*/
public function contains($item)
{
return $this->indexOf($item)>=0;
}
/**
* 上面contains的实现,数组的查询这里用到了array_search,如果只是查是否有某个key的话,用isset就行了,这里是查value
*/
public function indexOf($item)
{
if(($index=array_search($item,$this->_d,true))!==false)
return $index;
else
return -1;
}
/**
*返回数据容器
*/
public function toArray()
{
return $this->_d;
}
/**
* 从比的地方把数据倒过来,如果原来这里有数据,会先清掉
*/
public function copyFrom($data)
{
if(is_array($data) || ($data instanceof Traversable))
{
if($this->_c>0)
$this->clear();
//这里有个问题,我在这如果加一行var_dump($data->_d);会报错,提示Trying to get property of non-object
//可是var_dump($data);明明是个CList对象,是否$data经过instanceof CList之后才知道它是个CList对象(貌似不是这样的),求高手解答
if($data instanceof CList)
$data=$data->_d;
foreach($data as $item)
$this->add($item);
}
else if($data!==null)
throw new CException(Yii::t('yii','List data must be an array or an object implementing Traversable.'));
}
/**
* 其实就是把另一个数组追加到本数组容器里。
*/
public function mergeWith($data)
{
if(is_array($data) || ($data instanceof Traversable))
{
if($data instanceof CList)
$data=$data->_d;
foreach($data as $item)
$this->add($item);
}
else if($data!==null)
throw new CException(Yii::t('yii','List data must be an array or an object implementing Traversable.'));
}
/**
* ArrayAccess接口的实现
*/
public function offsetExists($offset)
{
return ($offset>=0 && $offset<$this->_c);
}
/**
* 这里跟itemAt()一样,为什么不直接把itemAt()代码放这就行了?原因跟之前的clear()方法里面有点像,相当于增删改查的逻辑交给一个代理,这个代理有权限控制。下面offsetSet、offsetUnset也是一样。
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}
/**
* 同上
*/
public function offsetSet($offset,$item)
{
if($offset===null || $offset===$this->_c)
$this->insertAt($this->_c,$item);
else
{//这里本来可以直接在$_d[$offset] = $item;但交给代理去处理就多了一层权限控制
$this->removeAt($offset);
$this->insertAt($offset,$item);
}
}
/**
* 同上
*/
public function offsetUnset($offset)
{
$this->removeAt($offset);
}
}
==============================================还没写完,有空接着写=====================================
Yii源码分析——collections
最新推荐文章于 2021-10-14 11:09:24 发布