PHP класс словарь принимать объекты в качестве ключей


Я начал создавать класс словарь, которая принимает объекты, как есть ключи:

class Dictionary
    implements ArrayAccess
{    
    private $_keys = array();

    private $_values = array();

    public function offsetExists( $key )
    {
        return false !== array_search( $key, $this->_keys, true );
    }

    public function offsetGet( $key )
    {
        if( false === ( $index = array_search( $key, $this->_keys, true ) ) )
        {
            throw new OutOfBoundsException( 'Invalid dictionary key' );
        }

        if( !isset( $this->_values[ $index ] ) )
        {
            throw new LogicException( 'No matching value found for dictionary key' );
        }

        return $this->_values[ $index ];
    }

    public function offsetSet( $key, $value )
    {
        if( false !== ( $index = array_search( $key, $this->_keys, true ) ) )
        {
            $this->_values[ $index ] = $value;
        }
        else
        {
            $this->_keys[] = $key;
            $this->_values[] = $value;
        }
    }

    public function offsetUnset( $key )
    {
        if( false === ( $index = array_search( $key, $this->_keys, true ) ) )
        {
            throw new OutOfBoundsException( 'Invalid dictionary key' );
        }

        if( !isset( $this->_values[ $index ] ) )
        {
            throw new LogicException( 'No matching value found for dictionary key' );
        }

        array_splice( $this->_keys, $index, 1 );
        array_splice( $this->_values, $index, 1 );
    }
}

Она работает довольно хорошо для даже элементарных тестов я сделал до сих пор. Однако, я беспокоюсь о трех вещах:

  1. Можете ли вы вспомнить ситуацию, где $этом->_keys и $этом->_values не будут синхронизированы друг с другом больше, что приводит к повреждению данных? Я подумал, что там может возникнуть ситуация, когда что-то может произойти между двумя array_spliceс, например, в:

    public function offsetUnset( $key )
    {
        ...
    
        array_splice( $this->_keys, $index, 1 );
        array_splice( $this->_values, $index, 1 );
    

    ... приводит к повреждению данных.

  2. Я чувствую array_search() может оказаться неэффективным, но я специально не хочу прибегать к помощи spl_object_hash() в качестве ключей (и тем самым избежать необходимость для внутреннего ключей и значений массива), а у меня вера это не пуленепробиваемый, так как он не гарантирует уникальность хэш-объектов на всех.

  3. Я чувствую array_splice() может оказаться неэффективным, но я не хочу прибегать к помощью unset() , как я чувствую, это может привести к громоздким внутренний массивс ключами в конце концов. Я бы хотел сохранить внутренний массивс ключами, как плотно, как это возможно.

С нетерпением ждем ваших отзывов.



1946
2
задан 27 сентября 2011 в 04:09 Источник Поделиться
Комментарии
2 ответа

Во-первых, я должен сказать, что ты выглядишь, как вы повторно изобретать колесо. Вы можете получить все это с SplObjectStorage. Поскольку вы используете OutOfBoundsException, вы уже используете шлицов типов.



На ваши конкретные вопросы:


  1. Трудно думать о такой ситуации, но это не значит, что это немыслимо.

  2. В то время как spl_object_hash не гарантируется уникальная, весьма вероятно, что вы будете запускать из памяти задолго до появления хэш-коллизии. В качестве документа государственного образца: строку, которая является уникальной для каждого в настоящее время существующий объект и всегда одинаков для каждого элемента. Это и быстрее и дешевле, чем сохранение и поддержание собственного внутреннего состояния.

  3. Да, внутренние ключи не совсем так, как приказал во внутренние массивы, но это будет только сделать разницу, если у вас действительно есть проблема учета двух массивов синхронизированы — в принципе, если вы можете получить #1 работать стабильно и без сбоев, то сбросить не важно здесь.

3
ответ дан 27 сентября 2011 в 05:09 Источник Поделиться

Я наткнулся на вашу реализацию и я очень ценю это - я думаю, что это будет хорошо работать для большинства людей без экстремальной инженерии.

К сожалению, spl_object_hash - это просто строковое представление ссылки на объект в памяти. Например, если вы создаете два экземпляра того же самого массива: массив( 'а' => 1, 'Б' => 2 ), каждый объект будет возвратить другой spl_object_hash.

Другие языки (такие как Java и C#) есть понятие Equatable объектов. Equatable объектов делать 2 вещи:


  1. Метод GetHashCode()

  2. Равно( $другие ) - это, видимо, то, что ваш array_search() делает.

Но (обратите особое внимание) метод GetHashCode() не является полностью уникальным. Метод GetHashCode() это 2 разные вещи:


  • Последовательны!

  • Быстро!

Идея здесь заключается в том, что вы можете сократить время прошло в array_search очень быстро, используя метод GetHashCode(), то вы обнаружите, собственно матч с помощью array_search()/равны().

Код может выглядеть примерно так:

$objectiveLookup = new ObjectiveLookup();

//create an association between $obj1 and $val1
$objectiveLookup[ $obj1 ] = $val1;

//Here's what actually happens:
{
$hash_code = $obj1->GetHashCode();
if( !isset( $objectiveLookup->_similarValues[ $hash_code ] ) )
$objectiveLookup->_similarValues[ $hash_code ] = array();

//regular array
$objectiveLookup->_similarValues[ $hash_code ][] = $obj1;

//this is an SplObjectStorage
$objectiveLookup->_actualValues[ $obj1 ] = $val1;
}

Затем, после тысячи, или даже миллионы объектов в памяти, только эта доля должна быть _similarValues.

Из-за этого, поиск значений должно быть намного быстрее:

//this NEW object should be "IsEqual()" to $obj1
$obj1075383 = new CoolObject();

//get a value
$val1 = $objectiveLookup[ $obj1075383 ];

//Here's what actually happens:
$hash_code = $obj1075383->GetHashCode();
if( isset( $objectiveLookup->_similarValues[ $hash_code ] ) )
{
$similar_values = $objectiveLookup->_similarValues[ $hash_code ];

//find the one that IsEqual()
$matching_index = array_search( $similar_values, $obj1075383 );
$actual_object_key = $similar_values[ $matching_index ];

$actual_value = $objectiveLookup->_actualValues[ $actual_object_key ];
return $actual_value;
}

В целом, хорошая работа fireeyedboy!!

0
ответ дан 22 октября 2011 в 03:10 Источник Поделиться