PHP轉(zhuǎn)Go之?dāng)?shù)組的正確使用詳解
本文概要
思維速轉(zhuǎn),PHP Array -> Go Array or Slice or Map
底層實(shí)現(xiàn)的異同
使用中的注意事項(xiàng)及坑坑
- 如何選擇 Array 和 Slice
- 值傳遞和引用傳遞的坑
- Map 的無序
- 并發(fā)安全性
一、思維速轉(zhuǎn)
PHP 中 Array 實(shí)在是太好用了,即可以聲明為索引數(shù)組,又可以聲明為關(guān)聯(lián)數(shù)組;但是在 Golang 中,被拆解成了多個(gè)數(shù)據(jù)類型,其中索引數(shù)組與 Array/Slice 更貼近,而關(guān)聯(lián)數(shù)組更貼近于 Map
// 索引數(shù)組 $colors = array("red", "green", "blue"); // Go Array: [3]string{"red", "green", "blue"} // 固定長度 // Go Slice: []string{"red", "green", "blue"} // 關(guān)聯(lián)數(shù)組 $person = array( "first_name" => "John", "last_name" => "Doe", "age" => 30 ); // 對比 Golang Map person := map[string]string{ "first_name": "John", "last_name": "Doe", "age": "30", // 特別注意強(qiáng)類型下 map 中值部分無法存放不同類型的數(shù)據(jù) }
二、底層實(shí)現(xiàn)的異同
PHP 的語法上屏蔽了索引數(shù)組和關(guān)聯(lián)數(shù)組,底層實(shí)現(xiàn)均基于 HashTable
完成,但是對于 Golang 來說,不同的類型實(shí)現(xiàn)是略有差異的;
Array 底層實(shí)現(xiàn)
Go 的 Array 一定是預(yù)先確定長度的,故底層分配的是固定連續(xù)內(nèi)存,變量在聲明后,直接指向數(shù)組存儲的內(nèi)存地址,所以讀取會非常高效
arr := [3]int32{1,2,3} fmt.Println(&arr[0],&arr[1],&arr[2]) // Output: 可以看到每個(gè)元素偏移剛好是 4 個(gè)字節(jié) 0xc0000a6030 0xc0000a6034 0xc0000a6038
特別注意,對于[3]string 字符串?dāng)?shù)組來說,string 實(shí)際上是存放在獨(dú)立的內(nèi)存中的,所以數(shù)組中保存的是字符串地址,所以打印出來的地址偏移是 16 個(gè)字節(jié)(string 底層數(shù)據(jù)結(jié)構(gòu)包括一個(gè)指向字符串真實(shí)地址的指針,以及一個(gè)字符串長度的值)
注意,所有這些字節(jié)偏移均是 64 位機(jī)器上
Slice 底層實(shí)現(xiàn)
Slice 實(shí)際上底層也是引用了數(shù)組的實(shí)現(xiàn),但是它在數(shù)組的上面又封裝了一層數(shù)據(jù)結(jié)構(gòu),當(dāng)我們打印 Slice 中每個(gè)元素的內(nèi)存地址時(shí)你會發(fā)現(xiàn)其每個(gè)元素的偏移規(guī)則與數(shù)組是一致的。差別在于當(dāng)我們打印整個(gè)數(shù)組的指針地址時(shí)會發(fā)現(xiàn)其與數(shù)組第一個(gè)元素的地址是一致的,而 Slice 的不同。
type slice struct { array unsafe.Pointer // 指向底層數(shù)組 len int cap int } arr := [3]int64{1,2,3} fmt.Printf("arr : %p \n", &arr) fmt.Println(&arr,&arr[0],&arr[1],&arr[2]) slice := []int64{1,2,3} fmt.Printf("slice : %p \n", &slice) fmt.Println(&slice,&slice[0],&slice[1],&slice[2]) // Output: arr : 0xc000018120 // 與第一個(gè)元素一致 &[1 2 3] 0xc000018120 0xc000018128 0xc000018130 slice : 0xc000010078 // 指向 Slice 結(jié)構(gòu)體指針,故與第一個(gè)元素內(nèi)存不一致 &[1 2 3] 0xc000018150 0xc000018158 0xc000018160
Slice 實(shí)際上實(shí)現(xiàn)了高效的動態(tài)擴(kuò)容能力,故對比 Array 來說應(yīng)用場景更廣泛
Map 底層實(shí)現(xiàn)
Golang 的 Map 底層實(shí)現(xiàn)也同樣是 HashTable
,相同點(diǎn)是,查找插入效率高,支持動態(tài)擴(kuò)容等,但是 Golang 下的 Map 在遍歷時(shí)是無序的,php 下的關(guān)聯(lián)數(shù)組則是有序的;
關(guān)于 Golang 中 Array,Slice,Map 更詳細(xì)的底層實(shí)現(xiàn)有大量文章分析,這里不再贅述
使用中的注意事項(xiàng)及坑坑
Array 和 Slice 的選擇
Array 在某些場景下的確是使用起來會更高效,所以原則上在某些特別注意性能,且的確是不需要數(shù)據(jù)長度變動的情況下可以選擇 Array,但是,我要特別說明,這種場景極少極少,在日常業(yè)務(wù)系統(tǒng)開發(fā)中,無腦選擇使用 Slice 通常是正確的選擇
值傳遞和引用傳遞
Array 是值傳遞,Slice 和 Map 是引用傳遞,換句話說,Array 將數(shù)據(jù)傳遞到子函數(shù)后在子函數(shù)做任何修改,不會影響父函數(shù)內(nèi)原始數(shù)據(jù),但是 Slice 和 Map 不一樣,子函數(shù)對數(shù)據(jù)的修改會修改原始數(shù)據(jù),所以在上面無腦選擇 Slice 時(shí)要特別注意這一項(xiàng),是否擔(dān)心子函數(shù)意外修改數(shù)據(jù)
Map 的無序的坑
這個(gè)從 PHP 轉(zhuǎn)到 Go 可能無法注意到的一點(diǎn),每次循環(huán) Map 居然可能會得到完全不一樣的順序的結(jié)果,所以要想保證 Map 順序通常需要配合 slice 完成,或者借助第三方庫,比如:https://github.com/iancoleman/orderedmap 和 https://github.com/wangjia184/sortedset 但實(shí)際上這種可能也會帶來 json 序列化的坑,所以從接口設(shè)計(jì)上就應(yīng)盡可能避免,對有序要求的盡可能使用 Slice
并發(fā)安全性
Go 是一個(gè)語言級別就支持并發(fā)的語言,但是 Slice 和 Map 都是并發(fā)不安全的類型,所以在并發(fā)操作 Slice 和 Map 時(shí)可能會引起沖突,故要特別注意,修改數(shù)據(jù)時(shí)需要引入 sync.Mutex 互斥鎖 or 利用 channel 完成,Map 還提供了一個(gè)并發(fā)安全的類型 sync.Map 來解決這個(gè)問題。
強(qiáng)類型下 Golang Array靈活性降低
接受它!
當(dāng)你接觸了 Golang 下數(shù)組們的實(shí)現(xiàn),會發(fā)現(xiàn)它真的沒有 php 下的數(shù)組靈活好用,這其實(shí)是強(qiáng)類型語言的通病,雖然 Golang 也可以通過定義 []interface{} 類型的 Slice,但是強(qiáng)烈不建議這樣用,強(qiáng)類型的優(yōu)點(diǎn)是讓我們盡可能早的發(fā)現(xiàn)問題,比如在編碼時(shí) IDE 就能做到提示,而非在運(yùn)行
到此這篇關(guān)于PHP轉(zhuǎn)Go之?dāng)?shù)組的正確使用詳解的文章就介紹到這了,更多相關(guān)PHP轉(zhuǎn)Go內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
php 批量替換html標(biāo)簽的實(shí)例代碼
這篇文章主要是對php批量替換html標(biāo)簽的實(shí)例代碼進(jìn)行了詳細(xì)的介紹,需要的朋友可以過來參考下,希望對大家有所幫助2013-11-11php 利用socket發(fā)送HTTP請求(GET,POST)
作為php程序員一定會接觸http協(xié)議,也只有深入了解http協(xié)議,編程水平才會更進(jìn)一步。最近我一直在學(xué)習(xí)php的關(guān)于http的編程,許多東西恍然大悟,受益匪淺。希望分享給大家。本文需要有一定http基礎(chǔ)的開發(fā)者閱讀。2015-08-08php實(shí)現(xiàn)購物車功能(以大蘋果購物網(wǎng)為例)
本文主要介紹了php實(shí)現(xiàn)購物車功能(以大蘋果購物網(wǎng)為例)的實(shí)現(xiàn)方法,具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-03-03php獲取通過http協(xié)議post提交過來xml數(shù)據(jù)及解析xml
php 如何獲取請求的xml數(shù)據(jù),對方通過http協(xié)議post提交過來xml數(shù)據(jù),php如何獲取到這些數(shù)據(jù)呢?2012-12-12PHP基于單例模式實(shí)現(xiàn)的數(shù)據(jù)庫操作基類
這篇文章主要介紹了PHP基于單例模式實(shí)現(xiàn)的數(shù)據(jù)庫操作基類,涉及PHP操作數(shù)據(jù)庫的基本配置與增刪改查等操作技巧,需要的朋友可以參考下2016-01-01