Чи існує еквівалент Java HashMap у PHP?


78

Мені потрібен PHP-об'єкт, схожий на HashMap в Java, але я не знайшов, коли гуглив, тому, якщо хтось знає, як я можу імітувати HashMaps у PHP, допомога буде вдячна.


1
Що характеризує для вас хеш-карту?
Фелікс Клінг,

1
Мені потрібні пари ключ / значення, і мені потрібно отримати ключі як масив, що формує карту.
новачок

1
$keys = array_keys($array);(а також див. відповідь sushils нижче)
KingCrunch

Масиви насправді є єдиною структурою даних у PHP (якщо ви не розглядаєте класи / об'єкти як структуру даних). Він надає структуру ключ / значення, і ви можете легко отримати ключі за допомогою array_keys. Ви можете написати клас обгортки, якщо хочете.
Фелікс Клінг,

Відповіді:


95

Масиви в PHP можуть мати структуру Key Value.


64
@Gabi: Якщо він ходить, як качка, плаває, як качка, і квакає, як качка ... Посібник PHP говорить: масив у PHP насправді є впорядкованою картою.
Фелікс Клінг

115
Масиви @Felix Kling AFAIK PHP не мають пошуку O / 1 / вставки / видалення, тому вони не шаркають, як хеш-карти
Габі Пуркару


20
@Gabi: Внутрішня реалізація масивів у PHP - це хеш-карти.
KingCrunch

4
Це все ще не HashMap, тому що я не можу використовувати об'єкти як ключі :(.
knub

37

Залежно від того, що ви хочете, вас може зацікавити клас SPL Object Storage.

http://php.net/manual/en/class.splobjectstorage.php

Це дозволяє використовувати об’єкти як ключі, має інтерфейс для підрахунку, отримання хешу та інших смакот.

$s = new SplObjectStorage;
$o1 = new stdClass;
$o2 = new stdClass;
$o2->foo = 'bar';

$s[$o1] = 'baz';
$s[$o2] = 'bingo';

echo $s[$o1]; // 'baz'
echo $s[$o2]; // 'bingo'

2
ЦЕ. boztek ви недооцінені
CommaToast

У SplObjectStorage є деякі мінуси: коли ви використовуєте foreach, ключі є цілими числами. І ви не можете отримати з нього список ключів і значень. У моєму випадку я вирішив використовувати власний клас, що реалізує ArrayAccess, Iterator і Countable.
carlosvini

@carlosvini насправді ти можеш php.net/manual/en/class.splobjectstorage.php#114059
Саїдов

найкраща відповідь тут
Олег Абражаєв

32

Створіть Java як HashMap у PHP зі складністю читання O (1).

Відкрийте термінал phpsh:

php> $myhashmap = array();
php> $myhashmap['mykey1'] = 'myvalue1';
php> $myhashmap['mykey2'] = 'myvalue2';
php> echo $myhashmap['mykey2'];
myvalue2

Складність $myhashmap['mykey2'] цьому випадку здається, є постійним часом O (1), тобто, коли розмір $ myhasmap наближається до нескінченності, кількість часу, необхідного для отримання значення, заданого ключем, залишається незмінним.

Свідченням прочитаного масиву php є постійний час:

Запустіть це через інтерпретатор PHP:

php> for($x = 0; $x < 1000000000; $x++){
 ... $myhashmap[$x] = $x . " derp";
 ... }

Цикл додає 1 мільярд ключів / значень, потрібно близько 2 хвилин, щоб додати їх усі до хеш-карти, що може вичерпати вашу пам’ять.

Потім подивіться, скільки часу потрібно для пошуку:

php> system('date +%N');echo "  " . $myhashmap[10333] . "  ";system('date +%N');
786946389  10333 derp  789008364

Отже, як швидко здійснюється пошук карти масивів PHP?

Це 10333ключ, який ми шукали. 1 мільйон наносекунд == 1 мілісекунда. Кількість часу, необхідного для отримання значення з ключа, становить 2,06 мільйона наносекунд або близько 2 мілісекунд. Приблизно стільки ж часу, якщо масив був порожнім. Мені це здається постійним часом.


Не постійний час ... Якщо припустити, що основна реалізація - це хеш-карта на основі масиву, то через необхідність обробляти зіткнення найкраще, що ви могли б зробити, це O (log n) у випадку, коли зіткнення зберігаються як самоврівноважені- дерева (наприклад, реалізація Java хеш-карти), але може навіть зберігатися у зв’язаному списку (ланцюжку), що дає найгірший випадок O (n). Це вірно як для вставки, так і для пошуку, але середній випадок буде близький до O (1) ...
Даніель Валланд

1
Для набору даних, який споживає всю пам’ять одного комп’ютера (скажімо, 8 ГБ), час пошуку становить кілька мілісекунд. Отже, це "так близько до постійного часу, це в основному постійний час", але якщо ви хочете бути математично правильним, виводячи речі до 10 мільярдів коробок, здійснених до нескінченності, це O (n log n). Я теж можу загризти. :) Я тут використовую постійний час, щоб сказати: "Це не буде твоїм вузьким місцем, брате, навіть не собаку".
Ерік Лещинський,

Я згоден, це, мабуть, не буде вузьким місцем, оскільки O (log n) для всіх операцій все ще дуже швидкі. Справа лише в тому, що єдиним способом отримати постійний хешування часу є те, що хеш-функція досконала, і жодна колізія не може існувати. Якщо не ідеальний, найкраще, що ви отримуєте, - O (log n). Однак, згідно: phpinternalsbook.com/hashtables/basic_structure.html php використовує ланцюжок, який має гірший випадок O (N). Я не знаю, чи це так, оскільки я би очікував, що буде використано рішення, яке досягає log n, наприклад, самозбалансоване дерево, але якщо це так, то різниця між O (N) та O (1 ) не є тривіальним.
Даніель Валланд,

15
$fruits = array (
    "fruits"  => array("a" => "Orange", "b" => "Banana", "c" => "Apple"),
    "numbers" => array(1, 2, 3, 4, 5, 6),
    "holes"   => array("first", 5 => "second", "third")
);

echo $fruits["fruits"]["b"]

виводи "банан"

взято з http://in2.php.net/manual/en/function.array.php


Що робити, якщо я хочу оголосити порожній масив і зробити присвоєння типу: "fruits" => array("a" => "Orange", "b" => "Banana", "c" => "Apple")after?
diegoaguilar

2
@Diego $fruits = array(); $fruits['fruits'] = array('a' => 'Orange',...); ... насправді, ви навіть можете просто зробити це прямо зараз: $fruits['fruits']['a'] = 'Orange'; $fruits['holes']['first'] = 5; $fruits['numbers'][] = 1;вам навіть не потрібно обов'язково створювати будь-які масиви за допомогою array().
zamnuts

10

HashMap, який також працює з ключами, крім рядків та цілими числами зі складністю читання O (1) (залежно від якості вашої власної хеш-функції).

Ви можете зробити просту хеш-карту самостійно. Що робить hashMap - це зберігання елементів у масиві, використовуючи хеш як індекс / ключ. Хеш-функції дають зіткнення час від часу (не часто, але можуть траплятися), тому вам доведеться зберігати кілька елементів для запису в hashMap. Це просто - hashMap:

class IEqualityComparer {
    public function equals($x, $y) {
        throw new Exception("Not implemented!");
    }
    public function getHashCode($obj) {
        throw new Exception("Not implemented!");
    }
}

class HashMap {
    private $map = array();
    private $comparer;

    public function __construct(IEqualityComparer $keyComparer) {
        $this->comparer = $keyComparer;
    }

    public function has($key) {
        $hash = $this->comparer->getHashCode($key);

        if (!isset($this->map[$hash])) {
            return false;
        }

        foreach ($this->map[$hash] as $item) {
            if ($this->comparer->equals($item['key'], $key)) {
                return true;
            }
        }

        return false;
    }

    public function get($key) {
        $hash = $this->comparer->getHashCode($key);

        if (!isset($this->map[$hash])) {
            return false;
        }

        foreach ($this->map[$hash] as $item) {
            if ($this->comparer->equals($item['key'], $key)) {
                return $item['value'];
            }
        }

        return false;
    }

    public function del($key) {
        $hash = $this->comparer->getHashCode($key);

        if (!isset($this->map[$hash])) {
            return false;
        }

        foreach ($this->map[$hash] as $index => $item) {
            if ($this->comparer->equals($item['key'], $key)) {
                unset($this->map[$hash][$index]);
                if (count($this->map[$hash]) == 0)
                    unset($this->map[$hash]);

                return true;
            }
        }

        return false;
    }

    public function put($key, $value) {
        $hash = $this->comparer->getHashCode($key);

        if (!isset($this->map[$hash])) {
            $this->map[$hash] = array();
        }

        $newItem = array('key' => $key, 'value' => $value);        

        foreach ($this->map[$hash] as $index => $item) {
            if ($this->comparer->equals($item['key'], $key)) {
                $this->map[$hash][$index] = $newItem;
                return;
            }
        }

        $this->map[$hash][] = $newItem;
    }
}

Щоб він функціонував, вам також потрібна хеш-функція для вашого ключа та порівняльник для рівності (якщо у вас лише кілька елементів або з іншої причини не потрібна швидкість, ви можете дозволити хеш-функції повернути 0; всі елементи будуть покладіть в одне відро, і ви отримаєте складність O (N))

Ось приклад:

class IntArrayComparer extends IEqualityComparer {
    public function equals($x, $y) {
        if (count($x) !== count($y))
            return false;

        foreach ($x as $key => $value) {
            if (!isset($y[$key]) || $y[$key] !== $value)
                return false;
        }

        return true;
    }

    public function getHashCode($obj) {
        $hash = 0;
        foreach ($obj as $key => $value)
            $hash ^= $key ^ $value;

        return $hash;
    }
}

$hashmap = new HashMap(new IntArrayComparer());

for ($i = 0; $i < 10; $i++) {
    for ($j = 0; $j < 10; $j++) {
        $hashmap->put(array($i, $j), $i * 10 + $j);
    }
}

echo $hashmap->get(array(3, 7)) . "<br/>";
echo $hashmap->get(array(5, 1)) . "<br/>";

echo ($hashmap->has(array(8, 4))? 'true': 'false') . "<br/>";
echo ($hashmap->has(array(-1, 9))? 'true': 'false') . "<br/>";
echo ($hashmap->has(array(6))? 'true': 'false') . "<br/>";
echo ($hashmap->has(array(1, 2, 3))? 'true': 'false') . "<br/>";

$hashmap->del(array(8, 4));
echo ($hashmap->has(array(8, 4))? 'true': 'false') . "<br/>";

Що дає як вихід:

37
51
true
false
false
false
false

3
IEqualityComparerмає бути interfaceтут
vp_arth

0

Ви можете створити власний клас HashMap для цього в php. приклад, як показано нижче, що містить основні атрибути HashMap, такі як get і set.

class HashMap{

        public $arr;

        function init() {

            function populate() {
                return null;
            }
            
            // change to 999 for efficiency
            $this->arr = array_map('populate', range(0, 9));

            return $this->arr;

        }
        
        function get_hash($key) {
            $hash = 0;

            for ($i=0; $i < strlen($key) ; $i++) { 
                $hash += ord($key[$i]);
            }
            
            // arr index starts from 0
            $hash_idx = $hash % (count($this->arr) - 1); 
            return $hash_idx;
            
        }

        function add($key, $value) {
            $idx = $this->get_hash($key);
            
            if ($this->arr[$idx] == null) {
                $this->arr[$idx] = [$value];
            } else{

                $found = false;

                $content = $this->arr[$idx];
                
                $content_idx = 0;
                foreach ($content as $item) {

                    // checking if they have same number of streams
                    if ($item == $value) {

                        $content[$content_idx] = [$value];
                        $found = true;
                        break;

                    }
                    
                    $content_idx++;
                }

                if (!$found) {
                    // $value is already an array
                    array_push($content, $value);

                    // updating the array
                    $this->arr[$idx] = $content;
                }

            }

            return $this->arr;

        }

        function get($key) {

            $idx = $this->get_hash($key);
            $content = $this->arr[$idx];

            foreach ($content as $item) {
                if ($item[1] == $key) {
                    return $item;
                    break;
                }
            }
                
        }

    }

Сподіваюся, це було корисно

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.