上次提到PHP7的数组分为packed array 和 hash array,区别在于packed array取值不需要通过slot获得arData的下标,它的key对应的就是下标,所以取值速度上更有优势和比较省内存。
packed array要满足的条件是key必须是递增的正整型。
转换为packed array的操作
shuffle,array_keys等函数最终都会调用 zend_hash_to_packed 转换为packed array。
转换为hash array的操作
- 往packed array添加string key的操作
- 添加的key是整型但是之前这个key被unset过(Z_TYPE == IS_UNDEF 状态)
-
添加的key 大于nTableSize(数组容量)并且 (key » 1) > nTableSize (nTableSize » 1) > nNumOfElements 这里😂太拗口我就直接贴代码好了
关于②PHP数组unset操作并不会立马删除指定的bucket,而且把它标识为IS_UNDEF
的状态,等待rehash或者扩容的时候再处理(这是后话,有机会再填坑吧)。
比如有一个packed array
这时候就已经转化成了hash array
关于③先要各个字段之间的关系是,数组容量(nTableSize,初始化数组容量是8) = 已使用(nNumUsed) + 未使用, 已使用(nNumUsed) = 有效(nNumOfElements,我们count数组得到的就是这个大小) + 无效(unset的) 。结合上面的描述得到 $arr = [0 => 1, 8 => 2]; 是一个hash array。
打印出这时候的HashTable可以看到,u.flag = 26 & HASH_FLAG_PACKED(4) == 0 已经不是packed array了,虽然我们第二个元素下标是8超过了容量nTableSize,但是这时候并没有扩容,而是转为hash array使得内存使用边得更紧凑。
参考:
- 《PHP 7底层设计与源码实现》
- PHP7.2-SRC
- nikic 博客