Lea*_*yes 5 php arrays associative-array class
我有一个使用多级关联数组存储值的类:
我需要添加一种访问和修改嵌套值的方法.这是我的问题的一个有效的解决方案,但它很慢.有没有更好的方法呢?
注意:使用get/set函数不是必需的,但需要有一种有效的方法来定义默认值.
class Demo {
protected $_values = array();
function __construct(array $values) {
$this->_values = $values;
}
public function get($name, $default = null) {
$token = strtok($name, '.#');
$node = $this->_values;
while ($token !== false) {
if (!isset($node[$token]))
return $default;
$node = $node[$token];
$token = strtok('.#');
}
return $node;
}
public function set($name, $value) {
$next_token = strtok($name, '.#');
$node = &$this->_values;
while ($next_token !== false) {
$token = $next_token;
$next_token = strtok('.#');
if ($next_token === false) {
$node[ $token ] = $value;
break;
}
else if (!isset($node[ $token ]))
$node[ $token ] = array();
$node = &$node[ $token ];
}
unset($node);
}
}
Run Code Online (Sandbox Code Playgroud)
将使用如下:
$test = new Demo(array(
'simple' => 27,
'general' => array(
0 => array(
'something' => 'Hello World!',
'message' => 'Another message',
'special' => array(
'number' => 27
)
),
1 => array(
'something' => 'Hello World! #2',
'message' => 'Another message #2'
),
)
));
$simple = $test->get('simple'); // === 27
$general_0_something = $test->get('general#0.something'); // === 'Hello World!'
$general_0_special_number = $test->get('general#0.special.number'); === 27
Run Code Online (Sandbox Code Playgroud)
注意:'general.0.something'与'general#0.something'相同,替代标点符号是为了清晰起见.
嗯,这个问题很有趣,我忍不住再修改一下。:-)
所以,这是我的结论。您的实现可能是最直接、最清晰的。而且它正在工作,所以我不会真正费心去寻找另一个解决方案。事实上,你最终会接到多少电话?性能差异是否值得这么麻烦(我的意思是“超级超快”和“几乎一半快”之间)?
不过,如果性能确实是一个问题(收到数千个调用),那么如果您重复查找数组,就有一种方法可以减少执行时间。
在您的版本中,最大的负担落在函数中的字符串操作get上。在这种情况下,所有涉及字符串操作的事情都注定会失败。我最初尝试解决这个问题时确实是这样。
如果我们想要这样的语法,很难不碰字符串,但我们至少可以限制我们进行的字符串操作的数量。
如果您创建一个哈希映射(哈希表),以便可以将多维数组展平为一层深度结构,那么完成的大部分计算都是一次性费用。这是有回报的,因为这样您几乎可以通过调用中提供的字符串get直接查找您的值。
我想出了大致这样的东西:
<?php
class Demo {
protected $_values = array();
protected $_valuesByHash = array();
function createHashMap(&$array, $path = null) {
foreach ($array as $key => &$value) {
if (is_array($value)) {
$this->createHashMap($value, $path.$key.'.');
} else {
$this->_valuesByHash[$path.$key] =& $value;
}
}
}
function __construct(array $values) {
$this->_values = $values;
$this->createHashMap($this->_values);
// Check that references indeed work
// $this->_values['general'][0]['special']['number'] = 28;
// print_r($this->_values);
// print_r($this->_valuesByHash);
// $this->_valuesByHash['general.0.special.number'] = 29;
// print_r($this->_values);
// print_r($this->_valuesByHash);
}
public function get($hash, $default = null) {
return isset($this->_valuesByHash[$hash]) ? $this->_valuesByHash[$hash] : $default;
}
}
$test = new Demo(array(
'simple' => 27,
'general' => array(
'0' => array(
'something' => 'Hello World!',
'message' => 'Another message',
'special' => array(
'number' => 27
)
),
'1' => array(
'something' => 'Hello World! #2',
'message' => 'Another message #2'
),
)
));
$start = microtime(true);
for ($i = 0; $i < 10000; ++$i) {
$simple = $test->get('simple', 'default');
$general_0_something = $test->get('general.0.something', 'default');
$general_0_special_number = $test->get('general.0.special.number', 'default');
}
$stop = microtime(true);
echo $stop-$start;
?>
Run Code Online (Sandbox Code Playgroud)
setter尚未实现,您必须修改它以使用替代语法(分隔#符),但我认为它传达了这个想法。
至少在我的测试台上,与原始实现相比,执行此操作所需的时间减少了一半。原始数组访问仍然更快,但在我的情况下差异约为 30-40%。目前这是我能达到的最好成绩。我希望你的实际情况还不够大,以至于我在途中遇到了一些内存限制。:-)