Gibt es Java HashMap-Äquivalente in PHP?

78

Ich benötige ein PHP-Objekt ähnlich wie HashMap in Java, aber ich habe es nicht gefunden, als ich gegoogelt habe. Wenn also jemand weiß, wie ich HashMaps in PHP nachahmen kann, wäre Hilfe willkommen.

Neuling
quelle
1
Was zeichnet eine Hash-Map für Sie aus?
Felix Kling
1
Ich benötige Schlüssel / Wert-Paare und ich muss Schlüssel als Array aus der Karte erhalten.
Neuling
1
$keys = array_keys($array);(und siehe auch Sushils Antwort unten)
KingCrunch
Arrays sind tatsächlich die einzige Datenstruktur in PHP (wenn Sie Klassen / Objekte nicht als Datenstruktur betrachten). Es bietet eine Schlüssel- / Wertestruktur und Sie können die Schlüssel leicht mit erhalten array_keys. Sie können eine Wrapper-Klasse schreiben, wenn Sie möchten.
Felix Kling

Antworten:

93

Arrays in PHP können eine Schlüsselwertstruktur haben.

Sushil Bharwani
quelle
64
@Gabi: Wenn es wie eine Ente läuft, wie eine Ente schwimmt und wie eine Ente quakt ... PHP-Handbuch sagt: Ein Array in PHP ist eigentlich eine geordnete Karte.
Felix Kling
115
@ Felix Kling AFAIK PHP-Arrays haben keine O (1) Suche / Einfügung / Löschung, so dass sie nicht wie Hashmaps quaken
Gabi Purcaru
14
@Gabi Purcaru Verwandte: Zeit / Raum-Komplexität des PHP-Arrays .
Jensgramm
20
@Gabi: Die interne Implementierung von Arrays in PHP sind Hash-Maps.
KingCrunch
4
Dies ist immer noch keine HashMap, da ich keine Objekte als Schlüssel verwenden kann :(.
Knub
37

Je nachdem, was Sie möchten, könnte Sie die SPL Object Storage-Klasse interessieren.

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

Sie können Objekte als Schlüssel verwenden, haben eine Schnittstelle zum Zählen, Abrufen des Hash und anderer Extras.

$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'
Boris Gordon
quelle
2
DIES. Boztek Sie werden unterschätzt
CommaToast
SplObjectStorage hat einige Nachteile: Wenn Sie foreach verwenden, sind die Schlüssel Ganzzahlen. Und Sie können keine Liste von Schlüsseln und Werten daraus abrufen. In meinem Fall habe ich mich für eine benutzerdefinierte Klasse entschieden, die ArrayAccess, Iterator und Countable implementiert.
Carlosvini
@ Carlosvini tatsächlich können Sie php.net/manual/en/class.splobjectstorage.php#114059
Olim Saidov
beste Antwort hier
Oleg Abrazhaev
31

Erstellen Sie eine Java wie HashMap in PHP mit O (1) Lesekomplexität.

Öffnen Sie ein PHPsh-Terminal:

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

Die Komplexität von $myhashmap['mykey2']in diesem Fall scheint die konstante Zeit O (1) zu sein, was bedeutet, dass die Zeit, die zum Abrufen eines Werts bei einem Schlüssel benötigt wird, gleich bleibt, wenn sich die Größe von $ myhasmap der Unendlichkeit nähert.

Der Nachweis, dass das gelesene PHP-Array eine konstante Zeit ist:

Führen Sie dies durch den PHP-Interpreter:

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

Die Schleife fügt 1 Milliarde Schlüssel / Werte hinzu. Es dauert ungefähr 2 Minuten, um alle zur Hashmap hinzuzufügen, was Ihren Speicher erschöpfen kann.

Dann sehen Sie, wie lange es dauert, eine Suche durchzuführen:

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

Wie schnell sucht die PHP-Array-Karte?

Das 10333ist der Schlüssel, den wir nachgeschlagen haben. 1 Million Nanosekunden == 1 Millisekunde. Die Zeit, die benötigt wird, um einen Wert von einem Schlüssel zu erhalten, beträgt 2,06 Millionen Nanosekunden oder etwa 2 Millisekunden. Ungefähr die gleiche Zeit, wenn das Array leer war. Das sieht für mich nach ständiger Zeit aus.

Eric Leschinski
quelle
Keine konstante Zeit ... Angenommen, die zugrunde liegende Implementierung ist eine Array-basierte Hashmap. Aufgrund der Notwendigkeit, Kollisionen zu behandeln, ist das Beste, was Sie tun können, O (log n), wenn Kollisionen als selbstausgeglichen gespeichert werden. Bäume (wie die Java-Implementierung von Hashmap), können aber sogar in einer verknüpften Liste (Verkettung) gespeichert werden, was einen Worst-Case von O (n) ergibt. Dies gilt sowohl für das Einfügen als auch für das Nachschlagen, aber der durchschnittliche Fall liegt nahe bei O (1) ...
Daniel Valland
1
Bei einer Datensatzgröße, die den gesamten Speicher eines Computers belegt (z. B. 8 GB), beträgt die Suchzeit einige Millisekunden. Es ist also "so nahe an der konstanten Zeit, es ist im Grunde eine konstante Zeit", aber wenn Sie mathematisch korrekt sein wollen, um Dinge mit 10 Milliarden Kisten bis ins Unendliche auszuführen, ist es O (n log n). Ich kann auch nicht picken. :) Ich benutze konstante Zeit hier bedeutet "Dies wird nicht dein Engpass sein, Bruder, nicht einmal Stolperhund".
Eric Leschinski
Ich stimme zu, es wird wahrscheinlich kein Engpass sein, da O (log n) für alle Operationen immer noch sehr schnell ist. Der Punkt ist nur, dass der einzige Weg, um konstantes Zeit-Hashing zu erhalten, darin besteht, dass die Hash-Funktion perfekt ist und keine Colission existieren kann. Wenn nicht perfekt, ist das Beste, was Sie bekommen, O (log n). Laut: phpinternalsbook.com/hashtables/basic_structure.html verwendet php jedoch eine Verkettung, die den schlimmsten Fall von O (N) aufweist. Ich weiß nicht, ob dies der Fall ist, da ich erwarten würde, dass eine Lösung verwendet wird, die log n erreicht, wie z. B. ein selbstausgeglichener Baum, aber wenn dies der Fall ist, die Unterscheidung zwischen O (N) und O (1) ) ist nicht trivial.
Daniel Valland
14
$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"]

gibt 'Banane' aus

entnommen aus http://in2.php.net/manual/en/function.array.php

klausinho
quelle
Was ist, wenn ich ein leeres Array deklarieren und die Zuweisungen wie folgt ausführen möchte: "fruits" => array("a" => "Orange", "b" => "Banana", "c" => "Apple")after?
Diegoaguilar
2
@Diego $fruits = array(); $fruits['fruits'] = array('a' => 'Orange',...);... eigentlich können Sie dies sogar gleich von Anfang an tun: $fruits['fruits']['a'] = 'Orange'; $fruits['holes']['first'] = 5; $fruits['numbers'][] = 1;Sie müssen nicht einmal unbedingt eines der Arrays mit erstellen array().
Zamnuts
10

HashMap, das auch mit anderen Schlüsseln als Zeichenfolgen und Ganzzahlen mit O (1) -Lesekomplexität funktioniert (abhängig von der Qualität Ihrer eigenen Hash-Funktion).

Sie können selbst eine einfache HashMap erstellen. Eine HashMap speichert Elemente in einem Array unter Verwendung des Hash als Index / Schlüssel. Hash-Funktionen führen gelegentlich zu Kollisionen (nicht oft, aber möglicherweise), sodass Sie mehrere Elemente für einen Eintrag in der HashMap speichern müssen. So einfach ist eine 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;
    }
}

Damit es funktioniert, benötigen Sie auch eine Hash-Funktion für Ihren Schlüssel und einen Vergleicher für Gleichheit (wenn Sie nur wenige Elemente haben oder aus einem anderen Grund keine Geschwindigkeit benötigen, können Sie die Hash-Funktion 0 zurückgeben lassen; alle Elemente sind in den gleichen Eimer geben und Sie erhalten O (N) Komplexität)

Hier ist ein Beispiel:

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/>";

Welches gibt als Ausgabe:

37
51
true
false
false
false
false
Gert Jan Schoneveld
quelle
2
IEqualityComparersollte ein interfacehier sein
vp_arth