PHP学习笔记2:数组
图源:php.net
php不像其他编程语言那样,有数组、切片、映射、队列、集合等多种数据结构,这些在php中都可以用数组来进行表示。
语法
定义
php的数组由键值对组成,在概念上更像是Go语言的map
或者Python的dict
:
$student = array(
"name" => 'Li lei',
"age" => 20
);
键值对由=>
组成,并用,
间隔,在多行书写时,最后的键值对后可以添加,
,也可以不加,前者可以更便于后续继续添加新的键值对:
$student = array(
"name" => 'Li lei',
"age" => 20,
"new_element" => 'test',
);
单行书写时,同样可加可不加,不过习惯上更倾向于不加。
此外,可以使用短数组语法[]
替代array()
:
$student = [
"name" => "Li lei",
"age" => 20
];
两者本质上是相同的。
数组的key分为两种:数值索引和字符串索引。两者可以共存:
$students = array(
0 => array("name" => "Li lei","age"=>20),
"Han Mei" => array("name" => "Han Mei", "age"=>10),
);
echo $students[0]["name"].PHP_EOL;
// Li lei
echo $students["Han Mei"]["name"].PHP_EOL;
// Han Mei
在很多编程语言中,由纯数字索引组成的数组被称作“索引数组”,由纯字符串索引组成的数组被称作“关联数组”,但在php中,因为数组中可以同时出现数字索引和字符串索引,所以并不区分索引数组或关联数组。
数值索引由正整数构成,比较特别的是,某些类型会被转换为数值索引:
- 包含十进制整数的数字字符串。
- float类型。
- bool类型。
- null。
$array = array(
"8"=>"8",
"+8"=>"+8",
"08"=>"08",
3.5 => 3.5,
true => true,
false => false,
null => null,
);
var_dump($array);
// array(7) {
// [8]=>
// string(1) "8"
// ["+8"]=>
// string(2) "+8"
// ["08"]=>
// string(2) "08"
// [3]=>
// float(3.5)
// [1]=>
// bool(true)
// [0]=>
// bool(false)
// [""]=>
// NULL
// }
从示例可以看到,十进制的数字字符串、bool、float都被转换成了对应的整形,而null则被转换成了空字符串。
在很多编程语言中,无论是索引数组还是关联数组,其key都是不允许在定义时重复出现,否则会产生一个致命错误。但php并不会,不过只会保留最后一个重复定义的key的值,其余的会被丢弃:
$array = array(
"test",
0 => "test2",
"key" => "test3",
"key" => "test4",
);
var_dump($array);
// array(2) {
// [0]=>
// string(5) "test2"
// ["key"]=>
// string(5) "test4"
// }
在创建数组时,key可以不指定,此时会以当前数组中最大的数值索引+1来作为新的key,如果还未指定过数值索引,从0开始:
$array = array(
"a",
"b",
6=>"c",
"d",
"e",
);
var_dump($array);
// array(5) {
// [0]=>
// string(1) "a"
// [1]=>
// string(1) "b"
// [6]=>
// string(1) "c"
// [7]=>
// string(1) "d"
// [8]=>
// string(1) "e"
// }
访问数组元素
在php8.0之前,使用[]
或{}
都可以访问数组元素,8.0之后只有[]
可以:
$student = array(
"name" => "Xiao Li",
"age" => 20,
);
echo "name:{$student["name"]}, age:{$student["age"]}".PHP_EOL;
// name:Xiao Li, age:20
试图访问一个未定义的key,会引发一个E_NOTICE
级别的错误,并返回一个null值:
var_dump($student["test"]);
// Warning: Undefined array key "test" in D:\workspace\http\php-notes\ch2\get_value.php on line 8
// NULL
修改元素
可以使用[]
新建或修改元素:
<?php
$student = array(
"name" => "Li lei",
"age" => 20,
);
$student["career"] = "student";
$student[] = "male";
var_dump($student);
// array(4) {
// ["name"]=>
// string(6) "Li lei"
// ["age"]=>
// int(20)
// ["career"]=>
// string(7) "student"
// [0]=>
// string(4) "male"
// }
使用[] = value
的方式给数组添加新元素的时候,会分配一个新的数值索引,其规则与定义时相同。需要注意的是,并不是基于数组中已有的最大数值索引来生成,而是数组“曾经有过的”最大数值索引来生成,除非这个数组的键被重新生成过:
$array = array("a","b","c","d");
unset($array[3]);
unset($array[2]);
var_dump($array);
$array[] = "e";
var_dump($array);
// array(2) {
// [0]=>
// string(1) "a"
// [1]=>
// string(1) "b"
// }
// array(3) {
// [0]=>
// string(1) "a"
// [1]=>
// string(1) "b"
// [4]=>
// string(1) "e"
// }
可以看到,虽然使用unset()
删除了数字索引2和3对应的键值对,但新添加的元素给予的数字索引是4,而非2。
实用函数
array_values
unset()
可以删除数组中的元素,但并不会重建索引,可以使用array_values()
函数重建数字索引:
$array = array("a","b","c","d");
unset($array[3]);
unset($array[2]);
$array[] = "e";
$array = array_values($array);
var_dump($array);
// array(3) {
// [0]=>
// string(1) "a"
// [1]=>
// string(1) "b"
// [2]=>
// string(1) "e"
// }
事实上array_values()
的作用是用数组元素组成一个纯数字索引的数组后返回:
$student = array("name" => "Li Lei", "age" => 20);
$studentVals = array_values($student);
var_dump($studentVals);
// array(2) {
// [0]=>
// string(6) "Li Lei"
// [1]=>
// int(20)
// }
这个过程会丢弃key中可能包含的信息。
array_keys
array_keys()
与array_values()
作用相反,只会返回数组的key,不会返回值:
$array = array(
0 => 'val1',
7 => 'val2',
"key1" => 'val3',
"key2" => 'val4',
);
var_dump(array_keys($array));
// array(4) {
// [0]=>
// int(0)
// [1]=>
// int(7)
// [2]=>
// string(4) "key1"
// [3]=>
// string(4) "key2"
// }
array_key_exists
array_key_exists()
比较常用,它可以判断数组是否包含某个key:
$students = array(
array("name" => "Li Lei", "Math" => 90, "English" => 100),
array("name" => "Han Mei", "Math" => 80, "English" => 90),
array("name" => "Xiao Li", "Math" => 70, "English" => 95),
);
$total = array();
foreach ($students as $std){
foreach ($std as $key => $val){
if ($key != "name" && is_int($val)){
if (!array_key_exists($key, $total)){
$total[$key] = 0;
}
$total[$key]+=$val;
}
}
}
foreach ($total as $key => $val){
echo "[$key] total score: $val".PHP_EOL;
}
// [Math] total score: 240
// [English] total score: 285
这段代码中,$students
包含多个学生的各科成绩,$total
数组负责汇总,收集所有学生的成绩总和。因为事先不知道$students
中包含哪些科目的成绩,所以$total
中是空的。在实际遍历$students
时我们需要判断$total
是否已经含有科目条目,如果没有,就要进行初始化,然后在这基础上计算总和。
其中这段判断key是否存在并初始化的代码:
if (!array_key_exists($key, $total)){
$total[$key] = 0;
}
在某些强类型语言(如Java或Go)中是可有可无的,原因是在强类型语言中,$total
数组的元素类型是确定的(这里应当是整形),所以解释器可以在生成新元素时自动用相应类型的0值来初始化元素,而php就不同了,php中变量可以表示多种不同的类型,解释器无法判断具体需要用什么来进行初始化。
当然在这个例子中,注释这一段代码是不影响执行结果的,原因是$total[$key]+=$val;
这段代码可以让解释器知晓数组新元素大概率是一个整形,并参与数学运算。但这依然会产生一个“访问了不存在的数组key”的E_NOTICE
级别的错误,所以应当避免这种问题存在。
有些程序员会使用isset()
来检测key是否存在:
if (!isset($total[$key])){
$total[$key] = 0;
}
大多数情况下这样做并不会出现问题,但如果数组的元素是null:
$array = array("key" => null);
if (isset($array["key"])) {
echo "key exists" . PHP_EOL;
} else {
echo "key not exists" . PHP_EOL;
}
// key not exists
所以应当避免使用isset()
来判断数组key是否存在。
in_array
in_array()
也是一个比较常用的数组函数,它可以判断数组是否包含某个值:
function testHttpMehtodAllow($method){
$httpMethod = ["GET","POST","DELETE","PUSH"];
if (in_array($method, $httpMethod)){
echo "$method is a validate http method.".PHP_EOL;
}
else{
echo "$method is not a validate http method.".PHP_EOL;
}
}
testHttpMehtodAllow('get');
testHttpMehtodAllow('GET');
// get is not a validate http method.
// GET is a validate http method.
就像上面展示的,in_array()
往往被用于对输入的合法性验证。
需要注意的是,php中的比较是分为“宽松比较”和“精确比较”的,普通情况下都是进行宽松比较,相当于直接使用==
进行比较,此时某些值会被认为是相等的,比如123
和"123"
:
function testInArray($value, $array)
{
if (in_array($value, $array)) {
echo "$value is in array." . PHP_EOL;
}
}
$array = ["123"];
testInArray("123", $array);
testInArray(123, $array);
// 123 is in array.
// 123 is in array.
如果需要精确比较,可以通过in_array()
的第三个参数指定:
function testInArray($value, $array)
{
if (in_array($value, $array, true)) {
echo "$value is in array." . PHP_EOL;
}
}
$array = ["123"];
testInArray("123", $array);
testInArray(123, $array);
// 123 is in array.
此时相当于使用===
进行比较,只有类型和值都一致才会认为是true。
其它更多的数组相关的函数,可以阅读官方手册数组 函数。
字符串索引
在php8以前,不加引号的字符串索引也可以使用,但在php8中已经被禁止了:
$student = array();
$student[name] = "Li Lei";
// Fatal error: Uncaught Error: Undefined constant "name" in ...
现在会产生一个致命错误。在以前的版本中,未定义的常量,比如示例中的name
,会被解析为相应的字符串,所以代码依然可以运行,但这样会产生一些潜在的问题,比如说后续代码重构中该常量被定义。
正确的做法是总是为字符串索引字面量添加上引号(双引号或单引号):
$student = array();
$student["name"] = "Li Lei";
echo "${student['name']}".PHP_EOL;
echo "${student["name"]}".PHP_EOL;
echo "{$student['name']}".PHP_EOL;
echo "{$student["name"]}".PHP_EOL;
// Li Lei
// Li Lei
// Li Lei
// Li Lei
在字符串中使用数组元素,可能需要结合变量解析的相关语法,就像上面展示的那样。具体可以阅读PHP学习笔记1:基础中字符串的部分。
转换为数组
对于一般的类型(整形、字符串、浮点型、bool、resource),尝试将其转换为数组会得到一个以0为key,以对应数据为值的数组,也就是说(array)$target
与array($target)
的结果是一致的:
var_dump((array)1);
var_dump((array)1.5);
var_dump((array)"hello");
var_dump((array)true);
// array(1) {
// [0]=>
// int(1)
// }
// array(1) {
// [0]=>
// float(1.5)
// }
// array(1) {
// [0]=>
// string(5) "hello"
// }
// array(1) {
// [0]=>
// bool(true)
// }
比较特别的是对于对象,转换为数组后是以属性的变量名为key,以属性值为value:
class Student
{
var $name;
var $age;
public function __construct($name, $age)
{
$this->name = $name;
$this->age = $age;
}
}
$student = new Student("Li Lei", 20);
var_dump((array)$student);
// array(2) {
// ["name"]=>
// string(6) "Li Lei"
// ["age"]=>
// int(20)
// }
这样设计是有意义的,因为我们往往需要将对象转换为字符串,比如json、XML或持久化对象。而数组往往比对象要简单,可以借由数组让这种转化变得更容易。
需要注意的是,对于priave
和protected
属性,转换后的键名会有所区别:
class Student
{
private $name;
protected $age;
public function __construct($name, $age)
{
$this->name = $name;
$this->age = $age;
}
}
$student = new Student("Li Lei", 20);
var_dump((array)$student);
// array(2) {
// ["Studentname"]=>
// string(6) "Li Lei"
// ["*age"]=>
// int(20)
// }
对于private
的属性,会在变量名前添加一个类名,对于protected
的属性,会在变量名前添加一个*
。需要注意的是,额外添加的字符会以NUL
字节间隔:
var_export((array)$student);
// array (
// '' . "\0" . 'Student' . "\0" . 'name' => 'Li Lei',
// '' . "\0" . '*' . "\0" . 'age' => 20,
// )
数组比较
利用array_diff
可以比较两个数组的值是否相等:
/**
* 如果数组相等,返回true,否则返回false。
* @param array $arr1
* @param array 4arr2
* @return bool
*/
function array_compare($arr1, $arr2)
{
$diff1 = array_diff($arr1, $arr2);
$diff2 = array_diff($arr2, $arr1);
return empty($diff1) && empty($diff2);
}
$arr1 = array("a", "b", "c");
$arr2 = array("b", "d", "e");
var_dump(array_compare($arr1, $arr2));
// bool(false)
$arr1 = array("a","b","c");
$arr2 = array("b","c","a","e");
var_dump(array_compare($arr1, $arr2));
// bool(false)
$arr1 = array("a","b","c","e");
$arr2 = array("b","c","a");
var_dump(array_compare($arr1, $arr2));
// bool(false)
$arr1 = array("a",7=>"b","c");
$arr2 = array("b","c","a");
var_dump(array_compare($arr1, $arr2));
// bool(true)
这种情况下只会比较值,而不比较索引,所以array("a",7=>"b","c")
和array("b","c","a")
相等的。关于array_diff()
函数的详细说明可以阅读官方文档array_diff。
此外,还可以使用数组运算符来进行比较:
/**
* 如果数组相等,返回true,否则返回false。
* @param array $arr1
* @param array 4arr2
* @return bool
*/
function array_compare($arr1, $arr2)
{
return $arr1 == $arr2;
}
$arr1 = array("a","b","c");
$arr2 = array("b","c","a");
var_dump(array_compare($arr1, $arr2));
$arr1 = array("a","b","c");
$arr2 = array("a","b","c");
var_dump(array_compare($arr1, $arr2));
// bool(false)
// bool(true)
这种比较相对更为严格,不仅要键和值都一样,而且键出现的顺序也要完全一致才行。
更多的数组运算符相关说明可以查阅官方文档数组运算符。
以上就是数组相关的全部内容,谢谢阅读。
本系列文章的全部代码都收录在php-notes。