PHP SPL Extension Library (1) Data Structure

硬核项目经理
中文

The SPL library is also called the PHP standard library, which is mainly a set of interfaces or classes used to solve typical problems. What are these typical problems? For example, the data structure we are going to talk about today, and the implementation of some design patterns, just like the observer pattern-related interfaces we talked about before are provided in the SPL library. Having said that, in PHP, due to the characteristics of the language, in fact, many data structures are slightly different from those implemented in C language, such as linked lists. Since there is no concept of structure, we generally use classes to represent the nodes of the linked list. . In addition to this, to write a linked list also requires operations such as adding, deleting, modifying, and checking the linked list, and the SPL library has actually helped us provide an implementation of a doubly linked list, and can also directly implement the stack on the basis of this linked list. And the operation of the queue.

Doubly linked list

In the SPL library, the doubly linked list only needs to instantiate a SplDoublyLinkedList class, and then we can perform various operations on the instantiated doubly linked list object.

$dll = new SplDoublyLinkedList();

var_dump($dll->isEmpty()); // bool(true)

$dll->push(200);
$dll->push(300);
$dll->unshift("五号");
$dll->add(2, "六号");

var_dump($dll->isEmpty()); // bool(false)

var_dump($dll);
// object(SplDoublyLinkedList)#1 (2) {
//     ["flags":"SplDoublyLinkedList":private]=>
//     int(0)
//     ["dllist":"SplDoublyLinkedList":private]=>
//     array(4) {
//       [0]=>
//       string(6) "五号"
//       [1]=>
//       int(200)
//       [2]=>
//       string(6) "六号"
//       [3]=>
//       int(300)
//     }
//   }

As can be seen from the code, the push(), unshift(), and add() methods all add data to the linked list, and isEmpty() is used to determine whether the linked list is empty. Directly print and display the contents of the linked list, you can see that the inside of the linked list is an array of data.

var_dump($dll->top()); // int(300)
var_dump($dll->bottom()); // string(6) "五号"

var_dump($dll->pop()); // int(300)
var_dump($dll->shift()); // string(6) "五号"

var_dump($dll->serialize()); // string(25) "i:0;:i:200;:s:6:"六号";"
var_dump($dll->count()); // int(2)

top() and bottom() get the data at the top and bottom of the linked list, respectively. And pop() and shift() pop data from the bottom and top respectively. We will see later that depending on the settings, they will also follow the stack or queue method to pop data.

The serialize() method can directly obtain the contents of the serialized linked list. The count() method returns the number of elements in the linked list.

$dll->offsetSet(1, '修改成新六号');
var_dump($dll->offsetGet(1)); // string(18) "修改成新六号"
var_dump($dll->offsetExists(1)); // bool(true)
$dll->offsetUnset(1);
var_dump($dll->offsetExists(1)); // bool(false)

The offset-related method function is to operate the data in the linked list according to the offset value. In fact, it can be understood as operating the data according to the position index.

By default, when we traverse the linked list, output is similar to a queue, which is a first-in, first-out state.

for($i=1;$i<5;$i++){
    $dll->push($i);
}

var_dump($dll->getIteratorMode()); // int(0)
$dll->rewind();
while($dll->valid()){
    echo '============', PHP_EOL;
    echo 'key:', $dll->key(), PHP_EOL;
    echo '    current:', $dll->current(), PHP_EOL;
    $dll->next();
}
// ============
// key:0
//     current:200
// ============
// key:1
//     current:1
// ============
// key:2
//     current:2
// ============
// key:3
//     current:3
// ============
// key:4
//     current:4

Restore the linked list pointer to the beginning through rewind(), and then use the valid() method to determine whether the current data is valid, next() is used to move the linked list pointer to the next, and data can be traversed. We can change the iterative output rules of the linked list by setting the iterative mode of the linked list. For example, we need a stack-like type of last in, first out.

$dll->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO);
$dll->rewind();
while($dll->valid()){
    echo 'IT_MODE_LIFO============', PHP_EOL;
    echo 'key:', $dll->key(), PHP_EOL;
    echo '    current:', $dll->current(), PHP_EOL;
    $dll->next();
}
// IT_MODE_LIFO============
// key:4
//     current:4
// IT_MODE_LIFO============
// key:3
//     current:3
// IT_MODE_LIFO============
// key:2
//     current:2
// IT_MODE_LIFO============
// key:1
//     current:1
// IT_MODE_LIFO============
// key:0
//     current:200

In addition, it also has a fun iterative mode, which is to traverse and delete directly.

$dll->setIteratorMode(SplDoublyLinkedList::IT_MODE_DELETE);
$dll->rewind();
while($dll->valid()){
    echo 'IT_MODE_DELETE============', PHP_EOL;
    echo 'key:', $dll->key(), PHP_EOL;
    echo '    current:', $dll->current(), PHP_EOL;
    $dll->next();
}
var_dump($dll);
// object(SplDoublyLinkedList)#1 (2) {
//     ["flags":"SplDoublyLinkedList":private]=>
//     int(1)
//     ["dllist":"SplDoublyLinkedList":private]=>
//     array(0) {
//     }
//   }

After traversing with IT_MODE_DELETE, the data content in the linked list becomes empty. By default, the content of this IteraotrMode is SplDoublyLinkedList::IT_MODE_KEEP | SplDoublyLinkedList::IT_MODE_FIFO This value indicates that the data remains in its original state and uses the first-in first-out rule.

Stack

The stack class SplStack is actually the same as the subsequent queue class SplQueue, inherited from the linked list class, that is to say, they are actually equivalent to the linked list object with IteratorMode set. So their methods and functions are actually not much different.

// 栈
$stack = new SplStack();
for($i=1;$i<5;$i++){
    $stack->push($i);
}
var_dump($stack->getIteratorMode()); // int(6)
var_dump($stack);
// object(SplStack)#2 (2) {
//     ["flags":"SplDoublyLinkedList":private]=>
//     int(6)
//     ["dllist":"SplDoublyLinkedList":private]=>
//     array(4) {
//       [0]=>
//       int(1)
//       [1]=>
//       int(2)
//       [2]=>
//       int(3)
//       [3]=>
//       int(4)
//     }
//   }

$stack->rewind();
while($stack->valid()){
    echo '============', PHP_EOL;
    echo 'key:', $stack->key(), PHP_EOL;
    echo '    current:', $stack->current(), PHP_EOL;
    $stack->next();
}
// ============
// key:3
//     current:4
// ============
// key:2
//     current:3
// ============
// key:1
//     current:2
// ============
// key:0
//     current:1

queue

Compared with the linked list class and the stack class, the SplQueue queue has two more methods.

// 队列
$queue = new SplQueue();
for($i=1;$i<5;$i++){
    $queue->enqueue($i);
}
var_dump($queue->getIteratorMode()); // int(4)
var_dump($queue);
// object(SplQueue)#3 (2) {
//     ["flags":"SplDoublyLinkedList":private]=>
//     int(4)
//     ["dllist":"SplDoublyLinkedList":private]=>
//     array(4) {
//       [0]=>
//       int(1)
//       [1]=>
//       int(2)
//       [2]=>
//       int(3)
//       [3]=>
//       int(4)
//     }
//   }

$queue->rewind();
while($queue->valid()){
    echo '============', PHP_EOL;
    echo 'key:', $queue->key(), PHP_EOL;
    echo '    current:', $queue->current(), PHP_EOL;
    echo '    info:', $queue->dequeue(), PHP_EOL;
    $queue->next();
}
// ============
// key:0
//     current:1
//     info:1
// ============
// key:1
//     current:2
//     info:2
// ============
// key:2
//     current:3
//     info:3
// ============
// key:3
//     current:4
//     info:4

The enqueue() and dequeue() methods mean enqueue and dequeue respectively. In fact, they can be regarded as push() and shift() operations, adding top pop at the bottom.

heap

Stack stack, there will always be people who say heap and stack are the same thing, in fact, they are two completely different data structures. The stack is a linear logical structure, while the heap is generally a tree-shaped logical structure. Of course, their storage structure can be represented by the same linked list or sequence list. In the heap, there are the concepts of large top heap and small top heap, and SPL also provides us with these two implementations respectively. (Students who don’t know Dui can check relevant information by themselves)

Big top pile

$maxHeap = new SplMaxHeap();
for($i=1;$i<5;$i++){
    $maxHeap->insert($i);
}

var_dump($maxHeap);
// object(SplMaxHeap)#4 (3) {
//     ["flags":"SplHeap":private]=>
//     int(0)
//     ["isCorrupted":"SplHeap":private]=>
//     bool(false)
//     ["heap":"SplHeap":private]=>
//     array(4) {
//       [0]=>
//       int(4)
//       [1]=>
//       int(3)
//       [2]=>
//       int(2)
//       [3]=>
//       int(1)
//     }
//   }

var_dump($maxHeap->count()); // int(4)
var_dump($maxHeap->top()); // int(4)

var_dump($maxHeap->extract()); // int(4)

var_dump($maxHeap->count()); // int(3)
var_dump($maxHeap->top()); // int(3)

var_dump($maxHeap);
// object(SplMaxHeap)#4 (3) {
//     ["flags":"SplHeap":private]=>
//     int(0)
//     ["isCorrupted":"SplHeap":private]=>
//     bool(false)
//     ["heap":"SplHeap":private]=>
//     array(3) {
//       [0]=>
//       int(3)
//       [1]=>
//       int(1)
//       [2]=>
//       int(2)
//     }
//   }

$maxHeap->rewind();
while($maxHeap->valid()){
    echo '============', PHP_EOL;
    echo 'key:', $maxHeap->key(), PHP_EOL;
    echo '    current:', $maxHeap->current(), PHP_EOL;
    $maxHeap->next();
}
// ============
// key:2
//     current:3
// ============
// key:1
//     current:2
// ============
// key:0
//     current:1


var_dump($maxHeap->isCorrupted()); // bool(false)
$maxHeap->recoverFromCorruption(); 

The SplMaxHeap class is a class template used to generate large top heap instances. It inserts data through the insert() method and extracts data through the extract() method. It also includes common method functions such as count() and top(), as well as those related to traversal.

In addition, the operation of the heap also includes two method functions, which are used to determine whether the heap is in a corrupted state isCorrupted() and recover from the corrupted state to recover fromCorruption() related operation functions.

Small top pile

The content of the small top heap is exactly the same as that of the large top heap, but its internal structure is different. The big top heap has the largest parent node, while the small top heap is the data with the smallest parent node.

$minHeap = new SplMinHeap();
for($i=1;$i<5;$i++){
    $minHeap->insert($i);
}
var_dump($minHeap);
// object(SplMinHeap)#5 (3) {
//     ["flags":"SplHeap":private]=>
//     int(0)
//     ["isCorrupted":"SplHeap":private]=>
//     bool(false)
//     ["heap":"SplHeap":private]=>
//     array(4) {
//       [0]=>
//       int(1)
//       [1]=>
//       int(2)
//       [2]=>
//       int(3)
//       [3]=>
//       int(4)
//     }
//   }

var_dump($minHeap->top()); // int(1)

Priority Queue Implemented by Big Top Heap

In addition to the common operations of the big top stack and the small top stack, there is also a class template for the priority queue implemented by the big top stack in the SPL library.

$pQueue = new SplPriorityQueue();
for($i=1;$i<5;$i++){
    $pQueue->insert($i, random_int(1,10));
}
var_dump($pQueue);
// object(SplPriorityQueue)#6 (3) {
//     ["flags":"SplPriorityQueue":private]=>
//     int(1)
//     ["isCorrupted":"SplPriorityQueue":private]=>
//     bool(false)
//     ["heap":"SplPriorityQueue":private]=>
//     array(4) {
//       [0]=>
//       array(2) {
//         ["data"]=>
//         int(3)
//         ["priority"]=>
//         int(10)
//       }
//       [1]=>
//       array(2) {
//         ["data"]=>
//         int(4)
//         ["priority"]=>
//         int(7)
//       }
//       [2]=>
//       array(2) {
//         ["data"]=>
//         int(1)
//         ["priority"]=>
//         int(3)
//       }
//       [3]=>
//       array(2) {
//         ["data"]=>
//         int(2)
//         ["priority"]=>
//         int(2)
//       }
//     }
//   }

while($pQueue->valid()){
    var_dump($pQueue->extract());
}
// int(3)
// int(4)
// int(1)
// int(2)

Its operation method function is the same as the heap operation method function, except that there is an additional parameter in the insert() method to set the priority of the data. By setting different priorities, we can see that the data and the results of the traversal output will change, and the order is determined by the priority.

Fixed array

What is a fixed array? In PHP, the structure of the array is very powerful. It can be an array of ordinary subscript type or an array of HashMap key-value pairs. Its length is also unlimited. As long as the memory is sufficient, the array can be flexibly processed. length. However, in static languages, especially in the C language we have learned, arrays are of fixed length. That is to say, the memory size of the array is determined when the array is initialized. If the operation exceeds the length of the array, , It will cause cross-border problems. Let's see through an example.

// 数组
$norArr = [];
$norArr[1] = 'b';
$norArr[4] = 'f';

var_dump($norArr);
// array(2) {
//     [1]=>
//     string(1) "b"
//     [4]=>
//     string(1) "f"
//   }

$fArr = new SplFixedArray(5);
$fArr[1] = 'b';
$fArr[4] = 'f';

var_dump($fArr);
// object(SplFixedArray)#7 (5) {
//     [0]=>
//     NULL
//     [1]=>
//     string(1) "b"
//     [2]=>
//     NULL
//     [3]=>
//     NULL
//     [4]=>
//     string(1) "f"
//   }

norArr is a normal PHP array. After adding two data, there are only two elements in this array. The fArr instantiated by the following SplFixedArray class is a fixed array. It must pass a construction parameter to specify the length of the array when it is instantiated. It can be seen that the output result of fArr is fixed with 5 data, and the data we have not assigned will be given a default NULL value. Is it the same as the C array?

Of course, fixed arrays will have the problem of array subscripts out of bounds.

$fArr[6] = 'h'; // Fatal error: Uncaught RuntimeException: Index invalid or out of range 

But we can manually modify the size of the array to change the length of the data.

$fArr->setSize(7);
$fArr[6] = 'h';
var_dump($fArr->getSize()); // int(7)

Now, the length of the array is 7, and it can store 7 pieces of data. It can also be directly converted from an ordinary array, but it should be noted that the conversion array must be an array of numeric subscript type, and a HashMap array of string keys is not acceptable.

$fArr2 = SplFixedArray::fromArray(range(1,3));
var_dump($fArr2);
// object(SplFixedArray)#8 (3) {
//     [0]=>
//     int(1)
//     [1]=>
//     int(2)
//     [2]=>
//     int(3)
//   }

// $fArr3 = SplFixedArray::fromArray(['new'=>1, 'old'=>2]);
// var_dump($fArr3);
// PHP Fatal error:  Uncaught InvalidArgumentException: array must contain only positive integer keys

The rest is the same operation method functions as other data structures.

var_dump($fArr->count()); // int(7)

var_dump($fArr->offsetGet(2)); // NULL
$fArr->offsetSet(2, 'new'); 
var_dump($fArr->offsetGet(2)); // string(3) "new"
var_dump($fArr->offsetExists(2)); // bool(true)
$fArr->offsetUnset(2);
var_dump($fArr->offsetExists(2)); // bool(false)


$fArr->rewind();
while($fArr->valid()){
    echo '============', PHP_EOL;
    echo 'key:', $fArr->key(), PHP_EOL;
    echo '    current:', $fArr->current(), PHP_EOL;
    $fArr->next();
}
// ============
// key:0
//     current:
// ============
// key:1
//     current:b
// ============
// key:2
//     current:
// ============
// key:3
//     current:
// ============
// key:4
//     current:f
// ============
// key:5
//     current:
// ============
// key:6
//     current:h

Since it is an array object, iterating does not need to be so troublesome. We can use for and foreach directly. Like other array structures, it implements the Iterator and Countable interfaces, both of which can be traversed through for and foreach.

foreach($fArr as $f){
    var_dump($f);
}
for($i=0;$i<count($fArr);$i++){
    var_dump($fArr[$i]);
}

Object data mapping

The last data structure, object data mapping. What the hell is this? The simplest and most straightforward understanding is actually to treat an object as a [key], and then use these keys to form an array structure.

$os = new SplObjectStorage();

$o1 = new stdClass;
$o2 = new stdClass;
$o3 = new stdClass;
$o4 = new stdClass;
$o5 = new stdClass;

$os->attach($o1);
$os->attach($o2);
$os->attach($o3);
$os->attach($o4, 'd4');
$os->attach($o5, 'd5');

var_dump($os);
// object(SplObjectStorage)#9 (1) {
//     ["storage":"SplObjectStorage":private]=>
//     array(5) {
//       ["00000000736a0aba000000002f97228d"]=>
//       array(2) {
//         ["obj"]=>
//         object(stdClass)#10 (0) {
//         }
//         ["inf"]=>
//         NULL
//       }
//       ["00000000736a0abb000000002f97228d"]=>
//       array(2) {
//         ["obj"]=>
//         object(stdClass)#11 (0) {
//         }
//         ["inf"]=>
//         NULL
//       }
//       ["00000000736a0abc000000002f97228d"]=>
//       array(2) {
//         ["obj"]=>
//         object(stdClass)#12 (0) {
//         }
//         ["inf"]=>
//         NULL
//       }
//       ["00000000736a0abd000000002f97228d"]=>
//       array(2) {
//         ["obj"]=>
//         object(stdClass)#13 (0) {
//         }
//         ["inf"]=>
//         string(2) "d4"
//       }
//       ["00000000736a0abe000000002f97228d"]=>
//       array(2) {
//         ["obj"]=>
//         object(stdClass)#14 (0) {
//         }
//         ["inf"]=>
//         string(2) "d5"
//       }
//     }
//   }

Isn't it interesting, attach() can add data to this SplObjectStorage object storage mapping class. Its second parameter can specify a data content, in fact, it can be regarded as a value in an ordinary array.

var_dump($os->count()); // int(5)
$os->detach($o2);
var_dump($os->count()); // int(4)

var_dump($os->contains($o2)); // bool(false)
var_dump($os->contains($o3)); // bool(true)

var_dump($os->getHash($o4)); // string(32) "000000003e67a2330000000040e598c9"

var_dump($os->offsetGet($o4)); // string(2) "d4"
$os->offsetSet($o4, 'new d4'); 
var_dump($os->offsetGet($o4)); // string(6) "new d4"
var_dump($os->offsetExists($o4)); // bool(true)
$os->offsetUnset($o4);
var_dump($os->offsetExists($o4)); // bool(false)

$os->rewind();
$os[$o1] = 'new d1';
while($os->valid()){
    echo '============', PHP_EOL;
    echo 'key:', $os->key(), PHP_EOL;
    if($os->getInfo() === NULL){
        $os->setInfo('new iter info');
    }
    echo '    info:', $os->getInfo(), PHP_EOL;
    echo '    current:', PHP_EOL;
    var_dump($os->current());
    
    $os->next();
}
// ============
// key:0
//     info:new d1
//     current:
// object(stdClass)#10 (0) {
// }
// ============
// key:1
//     info:new iter info
//     current:
// object(stdClass)#12 (0) {
// }
// ============
// key:2
//     info:d5
//     current:
// object(stdClass)#14 (0) {
// }

Other traversal query operations are similar to those of other data structures, so I won't talk about it here. One of the special ones is that the detach() method deletes data, and getHash() obtains the Hash value of the object in the storage collection. This value can also be regarded as the subscript of the object in the object mapping collection. Our other operation judgments for objects are actually converted into this array subscript internally for operation.

Summarize

In fact, after studying in this circle, I suddenly discovered that with these data structures of SPL, we really don’t need to care about the implementation of data structures under PHP. The direct common point is just a doubly linked list. It’s simple. Just write the algorithm. Well, learning should be solid. The data structure and algorithm really need to learn the internal thoughts and logic. Of course, since it has been provided, it is more recommended to directly use these data structures of SPL for processing in our usual business development!

Test code:

https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/01/source/3. PHP's SPL extension library (1) data structure.php

Reference documents:

https://www.php.net/manual/zh/spl.datastructures.php

阅读 677
87 声望
16 粉丝
0 条评论
87 声望
16 粉丝
文章目录
宣传栏