第五天:PHP开发者快速掌握Go数组、切片与Map | 从PHP数组到Go复合类型的转换
作为PHP开发者,你早已习惯了“万能数组”——既可以当索引数组用,也可以当关联数组用,长度动态变化、元素类型不限。而Go没有“万能数组”,而是将复合数据类型拆分为数组(array)、切片(slice)、映射(map):数组对应PHP固定长度索引数组,切片对应PHP动态索引数组,Map对应PHP关联数组。今天我们就以PHP数组为参照,快速掌握Go这三种核心复合类型的声明、操作和核心差异。
一、Go数组(对标PHP固定长度索引数组)
Go数组是“长度固定、元素类型统一”的有序集合,对标PHP中手动限制长度的索引数组(如array_fill(0, 3, '')),但Go的长度是编译期常量,无法动态修改。
1. 数组声明与初始化
| 声明方式 | Go代码示例 | PHP等效代码 |
|---|---|---|
| 指定长度+类型 |
var arr [3]int(长度3,int类型,默认值0)
|
$arr = array_fill(0, 3, 0);
|
| 声明并初始化 |
var arr [3]int = [3]int{10, 20, 30}
|
$arr = [10, 20, 30];
|
| 长度推导(...) |
arr := [...]int{10, 20, 30}(自动推导长度3)
|
$arr = [10, 20, 30];(PHP自动推导长度)
|
基础操作示例:Go数组 vs PHP索引数组
// Go数组操作
func main() {
// 声明并初始化数组
arr := [3]int{10, 20, 30}
// 访问元素(下标从0开始,对标PHP)
fmt.Println(arr[0]) // 输出10
// 修改元素(仅能修改已有下标,不能新增)
arr[1] = 200
fmt.Println(arr) // 输出[10 200 30]
// 获取长度(len函数,对标PHP count())
fmt.Println(len(arr)) // 输出3
// 错误:数组长度固定,无法新增元素
// arr[3] = 400 // 编译报错:index 3 out of range [0:3]
}
PHP对应写法:
// PHP索引数组操作 $arr = [10, 20, 30]; echo $arr[0] . "\n"; // 输出10 $arr[1] = 200; print_r($arr); // 输出Array ( [0] => 10 [1] => 200 [2] => 30 ) echo count($arr) . "\n"; // 输出3 // PHP可新增元素(动态长度) $arr[3] = 400; print_r($arr); // 输出Array ( [0] => 10 [1] => 200 [2] => 30 [3] => 400 )
⚠️ 核心差异:
1. Go数组长度固定,编译期确定,无法新增/删除元素;PHP索引数组长度动态,可随意增删;
2. Go数组元素类型必须统一(如全int);PHP数组可混合类型(如[int, string, bool]);
3. Go数组是值类型(赋值/传参时复制整个数组);PHP数组是引用类型(赋值默认浅拷贝)。
二、Go切片(slice):对标PHP动态索引数组
Go数组的“长度固定”特性限制了灵活性,因此实际开发中几乎不用数组,而是用切片(slice)——切片是数组的“动态视图”,长度可动态扩容,完全对标PHP的动态索引数组,是Go中最常用的集合类型。
1. 切片声明与初始化
切片无固定长度,声明时无需指定长度,核心初始化方式如下:
| 初始化方式 | Go代码示例 | PHP等效代码 |
|---|---|---|
| 空切片 |
var s []int(长度0,容量0)
|
$s = [];
|
| 字面量初始化 |
s := []int{10, 20, 30}
|
$s = [10, 20, 30];
|
| 基于数组创建 |
arr := [3]int{10,20,30}; s := arr[0:2](取数组0-1下标)
|
$arr = [10,20,30]; $s = array_slice($arr, 0, 2);
|
| make创建(指定容量) |
s := make([]int, 2, 5)(长度2,容量5)
|
$s = array_fill(0, 2, 0);(PHP无“容量”概念)
|
2. 切片核心操作(对标PHP数组操作)
(1)新增元素:append(对标PHP $arr[] = 值)
// Go切片新增元素(append)
func main() {
s := []int{10, 20}
// 追加单个元素(对标PHP $s[] = 30)
s = append(s, 30)
// 追加多个元素(对标PHP array_push($s, 40, 50))
s = append(s, 40, 50)
fmt.Println(s) // 输出[10 20 30 40 50]
}
PHP对应写法:
// PHP新增数组元素 $s = [10, 20]; $s[] = 30; array_push($s, 40, 50); print_r($s); // 输出Array ( [0] => 10 [1] => 20 [2] => 30 [3] => 40 [4] => 50 )
(2)切片截取:s[start:end](对标PHP array_slice)
// Go切片截取
func main() {
s := []int{10,20,30,40,50}
// 取下标1到3(含1,不含3)
sub := s[1:3]
fmt.Println(sub) // 输出[20 30]
// 省略start:从0开始
sub1 := s[:2] // [10,20]
// 省略end:到最后
sub2 := s[3:] // [40,50]
}
PHP对应写法:
// PHP array_slice截取 $s = [10,20,30,40,50]; $sub = array_slice($s, 1, 2); // 从下标1取2个 print_r($sub); // 输出Array ( [0] => 20 [1] => 30 ) $sub1 = array_slice($s, 0, 2); // [10,20] $sub2 = array_slice($s, 3); // [40,50]
(3)删除元素:append组合截取(对标PHP unset)
// Go切片删除元素(删除下标2的元素)
func main() {
s := []int{10,20,30,40,50}
// 拼接:前2个元素 + 从3开始的元素
s = append(s[:2], s[3:]...)
fmt.Println(s) // 输出[10 20 40 50]
}
PHP对应写法:
// PHP删除数组元素 $s = [10,20,30,40,50]; unset($s[2]); // 重置下标(可选,Go切片无此问题) $s = array_values($s); print_r($s); // 输出Array ( [0] => 10 [1] => 20 [2] => 40 [3] => 50 )
💡 核心知识点:
1. 切片是引用类型(底层指向数组),赋值/传参时仅复制引用,不复制数据;
2. 切片有“长度(len)”和“容量(cap)”:长度是当前元素个数,容量是底层数组的可用长度,扩容时会自动分配更大的底层数组。
三、Go Map(对标PHP关联数组)
Go Map是“键值对”集合,完全对标PHP的关联数组,但Go的键/值类型必须统一(如键为string、值为int),且键必须是“可比较类型”(如string、int,不能是切片/Map)。
1. Map声明与初始化
| 初始化方式 | Go代码示例 | PHP等效代码 |
|---|---|---|
| 空Map |
var m map[string]int(需make初始化才能使用)
|
$m = [];
|
| make创建 |
m := make(map[string]int, 5)(容量5,可选)
|
$m = [];(PHP无容量概念)
|
| 字面量初始化 |
m := map[string]int{"age":30, "score":95}
|
$m = ["age"=>30, "score"=>95];
|
2. Map核心操作(对标PHP关联数组)
// Go Map CRUD操作
func main() {
// 初始化Map
user := make(map[string]interface{}) // 值为interface{}可存任意类型(类似PHP混合类型)
// 新增/修改元素(对标PHP $user["name"] = "张三")
user["name"] = "张三"
user["age"] = 30
user["score"] = 95.5
// 访问元素(对标PHP $user["name"])
fmt.Println(user["name"]) // 输出张三
// 判断键是否存在(Go独有,对标PHP isset($user["gender"]))
gender, ok := user["gender"]
if !ok {
fmt.Println("gender键不存在") // 输出此内容
user["gender"] = "男" // 新增键
}
// 删除元素(delete函数,对标PHP unset($user["score"]))
delete(user, "score")
fmt.Println(user) // 输出map[age:30 gender:男 name:张三]
// 遍历Map(对标PHP foreach)
for k, v := range user {
fmt.Printf("键:%s,值:%v\n", k, v)
}
}
PHP对应写法:
// PHP关联数组CRUD操作
$user = [];
// 新增/修改
$user["name"] = "张三";
$user["age"] = 30;
$user["score"] = 95.5;
// 访问
echo $user["name"] . "\n"; // 输出张三
// 判断键是否存在
if (!isset($user["gender"])) {
echo "gender键不存在\n";
$user["gender"] = "男";
}
// 删除
unset($user["score"]);
print_r($user); // 输出Array ( [name] => 张三 [age] => 30 [gender] => 男 )
// 遍历
foreach ($user as $k => $v) {
echo "键:{$k},值:{$v}\n";
}
⚠️ 核心差异:
1. Go Map的键类型必须统一且可比较(如全string);PHP关联数组键可混合int/string;
2. Go访问不存在的键返回值类型的默认值(如int返回0),需用ok判断是否存在;PHP访问不存在的键返回null;
3. Go Map遍历顺序不固定(每次遍历可能不同);PHP关联数组遍历顺序与插入顺序一致(PHP7+)。
四、实战案例:PHP vs Go 处理用户列表数据
需求:定义用户列表,筛选出年龄≥18的用户,输出用户名和年龄
Go写法(切片+Map)
package main
import "fmt"
func main() {
// 用户列表(切片嵌套Map)
users := []map[string]interface{}{
{"name": "张三", "age": 25},
{"name": "李四", "age": 17},
{"name": "王五", "age": 30},
}
// 筛选成年用户
adultUsers := []map[string]interface{}{}
for _, user := range users {
age := user["age"].(int) // 类型断言(对标PHP类型转换)
if age >= 18 {
adultUsers = append(adultUsers, user)
}
}
// 输出结果
fmt.Println("成年用户:")
for _, u := range adultUsers {
fmt.Printf("姓名:%s,年龄:%d\n", u["name"], u["age"])
}
}
PHP写法(关联数组嵌套)
// 用户列表(关联数组嵌套)
$users = [
["name" => "张三", "age" => 25],
["name" => "李四", "age" => 17],
["name" => "王五", "age" => 30],
];
// 筛选成年用户
$adultUsers = [];
foreach ($users as $user) {
if ($user["age"] >= 18) {
$adultUsers[] = $user;
}
}
// 输出结果
echo "成年用户:\n";
foreach ($adultUsers as $u) {
echo "姓名:{$u['name']},年龄:{$u['age']}\n";
}
五、今日小结
今天我们以PHP数组为参照,掌握了Go的三大复合数据类型:
-
Go数组长度固定、值类型,实际开发中极少使用,对标PHP固定长度索引数组;
-
Go切片是动态数组、引用类型,核心操作
append/截取/删除,完全对标PHP动态索引数组,是Go最常用的集合类型; -
Go Map是键值对集合,类型约束更严格,需用
ok判断键是否存在,对标PHP关联数组; -
Go无“万能数组”,需根据场景选择切片/Map,类型安全性远高于PHP数组。
明天我们将学习Go的结构体与接口(对标PHP的类/接口),详解Go面向对象编程的核心逻辑,继续用PHP思维快速上手Go的面向对象特性。
