对 PHP 数组的理解

在 PHP 中数组是使用最多的数据类型.大多数时间,我们不需要考虑 PHP 数组在编码中或者应用中的作用.我们喜欢 PHP 数组的动态特性,有时我们甚至不想去探究是否可以使用除数组意外的数据类型来解决我们的问题.下面我们来探讨一下 PHP 数组的优缺点,同时研究如何用不同的数据类型实现来使用数组实现性能的提升.我们先来解释 PHP 中数组的不同类型.然后来分析一下PHP 数组元素的内存空间以及如何通过其他数据结构来改善性能

更好的理解 PHP 数组

PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。由于数组元素的值也可以是另一个数组,树形结构和多维数组也是允许的。

PHP数组是动态灵活的,我们不需要去关注它是否像其他编程语言一样是一个有规则的,关联的,多维的数组.我们也不需要去定义数组的容量和数据类型.那 PHP 是这么实现这些的呢?答案很简单:PHP 数组的概念不仅仅是一个真正的数组而是一个 Hash-Map.

下面是数组的三种主要的类型:

  • 数字型
  • 关系型
  • 多维数组

数字型数组

数字型数组不仅意味着它能保存数字数据,事实上,它意味着数组额索引只能是数字.

在 PHP 中他们既可以是有序的也可以是无序的,但是他们必须是数字.在数字数组中,值以线性的方式存储和访问.下面举个列子

$array = [10,20,30,40,50];
$array[] = 70;
$array[] = 80;

$arraySize = count($array);
for($i = 0;$i<$arraySize;$i++) {
        echo "Position ".$i." holds the value ".$array[$i]."\n";
}
Position 0 holds the value 10 
Position 1 holds the value 20 
Position 2 holds the value 30 
Position 3 holds the value 40 
Position 4 holds the value 50 
Position 5 holds the value 70 
Position 6 holds the value 80  

上面是一个简单的数字型数组的实现,我们可以看到当我们添加通过 $array[] 在数组中添加一个新的元素,数字自动增加索引并且在新的索引上赋值.

如果我的数组是顺序的,我们可以使用 for 进行遍历.

一个大的问题的是,如果索引不是顺序的,难道我们不能描述数组了吗? 当然是可以的,看下面的例子

$array = [];
$array[10] = 100;
$array[21] = 200;
$array[29] = 300;
$array[500] = 1000;
$array[1001] = 10000;
$array[71] = 1971;

foreach($array as $index => $value) {
    echo "Position ".$index." holds the value ".$value."<br/>";
}
Position 10 holds the value 100 
Position 21 holds the value 200 
Position 29 holds the value 300 
Position 500 holds the value 1000 
Position 1001 holds the value 10000 
Position 71 holds the value 1971

关系型数组

关系型数组通过键(可以是任意字符串)来存取数据.在关系型数组中,通过键取代线性索引来存储值.和数字数组一样,我们可以通过关系型数组来存储任意类型的数据.

下面来看一组学生信息的例子:

$studentInfo = [];
$studentInfo['Name'] = "Adiyan";
$studentInfo['Age'] = 11;
$studentInfo['Class'] = 6;
$studentInfo['RollNumber'] = 71;
$studentInfo['Contact'] = "info@adiyan.com";

foreach($studentInfo as $key => $value) {
    echo $key.": ".$value."\n";
 }

输出

Name: Adiyan 
Age: 11 
Class: 6 
RollNumber: 71 
Contact: info@adiyan.com 

这里我们使用每一个键来保存数据.我们可以根据我们的需求添加键.我们利用 PHP 数组灵活的展示类似于结构体,map, 字典这些数据结构.

多维数组

顾名思义,多维数组就是存储了多个数组.换一句话说,它是存储数组的数组.这里将会在不同的例子中使用多维数组

使用多维数组来表示数据结构

在接下来的内容中,我们将会讨论不同的数据结构和算法.一个键的数据结构:图(graph).大多数时候我们都将使用 PHP 多维数组来表示邻接矩阵

接下来分析一下下面的这个图
[图片上传失败...(image-e97294-1511863276604)]

现在如果我们认为图的每一个节点是数组的一个值,我们可以这样描述节点

$node = ['A','B','C','D','E'];

但是上面的表达式无法表示节点间的连接关系,因此我们需要设计一个二维数组,这个二维数组 keys 表示names, 基于两个节点的内联 values 为0或者1.因为提供的图没设定方向所以我们假设这里是双向连接.

首先,我们需要为图创建一个数组,并且初始化二维数组的每个节点的值是0.代码如下:

$graph = [];
$nodes = ['A', 'B', 'C', 'D', 'E'];
foreach ($nodes as $xNode) {
    foreach ($nodes as $yNode) {
        $graph[$xNode][$yNode] = 0;
    }
}

打印 *$graph

0       0       0       0       0
0       0       0       0       0
0       0       0       0       0
0       0       0       0       0
0       0       0       0       0

现在我们将这样定义节点间的连接,每两个节点相连将会被表示为1:

$graph["A"]["B"] = 1;
$graph["B"]["A"] = 1;
$graph["A"]["C"] = 1;
$graph["C"]["A"] = 1;
$graph["A"]["E"] = 1;
$graph["E"]["A"] = 1;
$graph["B"]["E"] = 1;
$graph["E"]["B"] = 1;
$graph["B"]["D"] = 1;
$graph["D"]["B"] = 1; 

因为图谱中是无方向的,我们将认为是无方向的树,因此我们对于每一个连接都设置为1.后面我们会了解到为什么我这做.现在我们继续打印 *$graph:

0       1       1       0       1
1       0       0       1       1
1       0       0       0       0
0       1       0       0       0
1       1       0       0       0

好了,在后续关于图的实际操作的文章中我们将继续探讨这个数据结构.

利用 SplFixedArray 生成固定长度的数组

聊了这么多,我们已经对 PHP 数组有了很深入的了解了,但是我们从没有定义过数组的长度.PHP 数组长度可以根据我们的命令增长或者缩小.这一灵活性来自于强大的内存管理.我们下面就来探讨这些内容,来看看使用 PHP SPL 来生成固定长度的 PHP 数组.

为什么我们不生成固定长度的 PHP 数组呢?难道没有任何好处吗?答案是:当我们仅仅需要数组中某一个数字的时候,我们使用固定长度的数组可以减少内存的使用.在进行内存分析的之前,让我们使用 SplFixedArray 举个例子:

$array = new \SplFixedArray(10);

for ($i = 0; $i < 10; $I++)
    $array[$i] = $I;

for ($i = 0; $i < 10; $I++)
    echo $array[$i] . "\n";

输出结果为0-9,当我访问超过10的索引将会报错.

PHP Fatal error:  Uncaught RuntimeException: Index invalid or out of range 

PHP数组和SplFixedArray的基本区别为:

  • SplFixedArray必须有有限固定的长度
  • SplFixedArray的索引必须为integers且在0-n 范围之内, n 为我们定义的数组长度

当我们有很多有限固定长度数组或者有数组长度限制的时候 SplFixedArray 将会很好用.

常规 PHP 数组和 SplFixedArray 性能比较

在上一节中我们认为的一个关键的问题:为什么我们要要使用 SplFixedArray 取代传统 PHP 数组?通过对 PHP 数组的概念的理解,PHP 数组不仅仅是数组,还是 hash maps. 下面来运行一个例子来看一下 PHP 5.x 中 PHP 数组内存使用

创建一个100000唯一的整数数组. 每一个整数占用8 bytes,所以会消耗800000 bytes

$startMemory = memory_get_usage();
$array = range(1,100000);
$endMemory = memory_get_usage();
echo ($endMemory - $startMemory)." bytes";

输出

14649032 bytes

我们可以看见结果是 14649032 bytes,几乎是设想的18.5倍.意味着数组中的每一个元素超过144 bytes (18 * 8 bytes). 那么,额外的144 bytes来自哪里?为什么 PHP 没有利用这额外的内存? 这里有一个关于PHP数组额外内存占用的解释:

[图片上传失败...(image-116276-1511863276604)]

这幅图展示了 PHP 数组内部是如何工作的.PHP 数组存储将数据存储在bucket中.为了管理这个动态特性,这里可以看到这里为数组实现了双向链表和 hash 表.结果就导致耗费大量额外的内存.

在 PHP 7中 PHP 数组得到了优化:

$array = Range(1,10000) 32 bit 64 bit
PHP 5.6 or below 7.4MB 14MB
PHP 7 3 MB 4 MB

在 PHP7中对64位系统数组优化了3.5倍,下面来看看 SplFixedArray:

$items = 100000;
$startMemory = memory_get_usage();
$array = new \SplFixedArray($items);
for ($i = 0; $i < $items; $i++) {
    $array[$i] = $I;
}
$endMemory = memory_get_usage();

$memoryConsumed = ($endMemory - $startMemory) / (1024*1024);
$memoryConsumed = ceil($memoryConsumed);
echo "memory = {$memoryConsumed} MB\n";
memory = 2 MB
100000 条数据 使用 PHP 数组 64 bit
PHP 5.6 or below 14MB 6 MB
PHP 7 5 MB 2 MB

不仅在内存使用率上,SplFixedArray而且在进行数组比较,取值,赋值上都更快.

更多使用 SplFixedArray 的例子

既然 SplFixedArray 在性能方面有更好的表现,我们尽量在数据类型和算法中使用它.

将 PHP 数组转化为 SplFixedArray

我们已经知道如何创建一个定长的SplFixedArray,那么怎么实时的将一个普通 PHP 数组转化为SplFixedArray?

$array =[1 => 10, 2 => 100, 3 => 1000, 4 => 10000]; 
$splArray = SplFixedArray::fromArray($array); 
print_r($splArray);
SplFixedArray Object
(
    [0] => 
    [1] => 10
    [2] => 100
    [3] => 1000
    [4] => 10000
)

当我们想要实时的将一个数组转化为一个定长的数组,如果我们后面不再使用常规数组,最好 unset 常规数组,这样会最大限度的节省内存

将 SplFixedArray 转化为 PHP 数组

我需要将 SplFixedArray 转化为 PHP 数组来使用很多系统预定义的 array 函数.

$items = 5; 
$array = new \SplFixedArray($items); 
for ($i = 0; $i < $items; $i++) { 
    $array[$i] = $i * 10; 
} 

$newArray = $array->toArray(); 
print_r($newArray);
Array 
( 
    [0] => 0 
    [1] => 10 
    [2] => 20 
    [3] => 30 
    [4] => 40 
)

SplFixedArray 定义后改变长度

$items = 5; 
$array = new \SplFixedArray($items); 
for ($i = 0; $i < $items; $i++) { 
    $array[$i] = $i * 10; 
} 

$array->setSize(10); 
$array[7] = 100; 

使用 SplFixedArray 生成多维数组

$array = new \SplFixedArray(100);

for ($i = 0; $i < 100; $i++) 
    $array[$i] = new \SplFixedArray(100);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • //我所经历的大数据平台发展史(三):互联网时代 • 上篇http://www.infoq.com/cn/arti...
    葡萄喃喃呓语阅读 51,180评论 10 200
  • php.ini设置,上传大文件: post_max_size = 128Mupload_max_filesize ...
    bycall阅读 6,736评论 3 64
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 现在有请本次作品展会一等奖获得者,密炼车间的林明亮上台讲话,请大家热烈鼓掌。 终于到我啦。我快步走上台,拿起了话筒...
    涯为漫画痴阅读 627评论 1 5
  • 明天是邓丽君逝世22周年纪念日,据说今天全国很多地方在搞纪念活动,我们市民素质大讲堂也举行了一个纪念讲座,不过我...
    云沐妈妈阅读 151评论 0 0