PHP:为具有嵌套关联数组的对象公开 'get' 和 'set'

标签 php arrays class associative-array

我有一个用多级关联数组存储值的类:

我需要添加一种方法来访问和修改嵌套值。这是我的问题的有效解决方案,但它相当慢。是否有更好的方法

注意: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);
    }

}

将按如下方式使用:

$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

注意:“general.0.something”与“general#0.something”相同,替代标点符号是为了清楚起见。

最佳答案

好吧,这个问题很有趣,我忍不住想再多修改一下。 :-)

所以,这是我的结论。 您的实现可能是最直接和清晰的。它正在运行,所以我不会真的费心去寻找另一种解决方案。事实上,你最终会接到多少电话?性能差异是否值得麻烦(我的意思是“ super 快”和“快一半”)?

尽管如此,如果性能确实是一个问题(获得数千次调用),那么如果您重复查找数组,则有一种方法可以减少执行时间。

在您的版本中,最大的负担落在 get 函数中的字符串操作 上。在这种情况下,所有涉及字符串操作的东西都注定要失败。我最初尝试解决这个问题时确实是这样。

如果我们想要这样的语法,很难不去触及字符串,但我们至少可以限制我们进行多少字符串操作

如果您创建一个 HashMap (哈希表),以便您可以将多维数组展平到一层深度结构,那么完成的大部分计算都是一次费用。这是值得的,因为这样您几乎可以通过 get 调用中提供的 string 直接查找您的值。

我想出了大致这样的东西:

<?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;

?>

setter 尚未实现,您必须修改它以获得替代语法(# 分隔符),但我认为它传达了这个想法。

至少在我的测试平台上,与原始实现相比,执行此操作需要一半的时间原始数组访问速度仍然更快,但在我的案例中,差异约为 30-40%。此刻那是我能达到的最好成绩。我希望您的实际案例还不够大,以至于我在途中遇到了一些内存限制。 :-)

关于PHP:为具有嵌套关联数组的对象公开 'get' 和 'set',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5550507/

相关文章:

无法将参数存储为数组并在 C 中打印它们

Magento 在主 url 之后获取 url

PHP mysql 查询生成器显示我想要的内容

php - 查询所有用户配置文件并收集它们以显示给 MySQL 中的特定用户的最佳方法

java - xstream可以反序列化复杂的数组吗?

python - 当尝试设置运行计划时,类立即调用方法。函数没有发生

php - PHP 用户类应该扩展数据库类吗?

Javascript:将数组数组拆分为多个数组

c++ - 在构造函数C++中初始化属性时出现问题

ruby-on-rails - 如何以 "dynamically"打开一个类以便向其添加一个使用局部变量的作用域方法?