PHP的foreach是一個(gè)非常整潔和切中要害的語(yǔ)言結(jié)構(gòu)。仍然有些人不喜歡使用它,因?yàn)樗麄冋J(rèn)為它是緩慢的。一個(gè)通常命名的原因是foreach復(fù)制它迭代的數(shù)組。
因此,一些人建議寫(xiě):
$keys = array_keys($array);
$size = count($array);
for ($i = 0; $i $size; $i++) {
$key = $keys[$i];
$value = $array[$key];
// ...
}
而不是更直觀和直接:
foreach ($array as $key => $value) {
// ...
}
這里有兩個(gè)問(wèn)題:
Microoptimization是不好的。通常,它只會(huì)浪費(fèi)您的時(shí)間,不會(huì)帶來(lái)任何可度量的性能改進(jìn)。
foreach的復(fù)制行為比大多數(shù)人認(rèn)為的要復(fù)雜一些。通常情況下,“優(yōu)化”的版本會(huì)比原始版本慢。
foreach什么時(shí)候復(fù)制?
foreach是否復(fù)制數(shù)組以及復(fù)制的數(shù)量取決于三件事:
是否引用了迭代數(shù)組、它的refcount有多高以及迭代是否通過(guò)引用完成。
沒(méi)有引用,refcount == 1
在下面的代碼中,$array沒(méi)有被引用,并且refcount為1。在這種情況下,foreach不會(huì)復(fù)制數(shù)組(證明)——這與流行的觀點(diǎn)相反,即foreach總是復(fù)制沒(méi)有引用的迭代數(shù)組。
test();
function test() {
$array = range(0, 100000);
foreach ($array as $key => $value) {
// ...
}
}
原因很簡(jiǎn)單:為什么要這樣做?foreach修改$array的唯一地方是它是內(nèi)部數(shù)組指針。這是預(yù)期的行為,因此不需要預(yù)防。
未引用,refcount > 1
下面的代碼看起來(lái)非常類似于前面的代碼。唯一的區(qū)別是數(shù)組現(xiàn)在作為參數(shù)傳遞。這似乎是一個(gè)無(wú)關(guān)緊要的區(qū)別,但它確實(shí)改變了foreach的行為:
它現(xiàn)在將復(fù)制數(shù)組結(jié)構(gòu),而不是值(證明;如果你想知道這只是復(fù)制的結(jié)構(gòu),比較一下這個(gè)和那個(gè)腳本。第一個(gè)只復(fù)制結(jié)構(gòu),第二個(gè)兩個(gè)都復(fù)制)。
$array = range(0, 100000);
test($array);
function test($array) {
foreach ($array as $key => $value) {
// ...
}
}
乍一看這可能有點(diǎn)奇怪:
為什么當(dāng)數(shù)組通過(guò)參數(shù)傳遞時(shí),它會(huì)復(fù)制,但如果它是在函數(shù)中定義的,它就不會(huì)復(fù)制了?原因是數(shù)組zval現(xiàn)在在多個(gè)變量之間共享:函數(shù)外部的$array變量和函數(shù)內(nèi)部的$array變量。如果foreach在不復(fù)制數(shù)組結(jié)構(gòu)的情況下迭代數(shù)組,那么它不僅會(huì)改變函數(shù)中$array變量的數(shù)組指針,還會(huì)改變函數(shù)外$array變量的指針。因此foreach需要復(fù)制數(shù)組結(jié)構(gòu)(即散列表)。另一方面,這些值仍然可以共享zvals,因此不需要復(fù)制。
引用
下一種情況與前一種情況非常相似。唯一的區(qū)別是數(shù)組是通過(guò)引用傳遞的。在這種情況下,數(shù)組將不會(huì)被復(fù)制(證明)。
$array = range(0, 100000);
test($array);
function test($array) {
foreach ($array as $key => $value) {
// ...
}
}
在這種情況下,相同的推理適用于前一種情況:外部$數(shù)組和內(nèi)部$數(shù)組共享zvals。不同的是,它們現(xiàn)在是引用(isref == 1),因此在這種情況下,對(duì)內(nèi)部數(shù)組的任何更改都將對(duì)外部數(shù)組進(jìn)行。所以如果內(nèi)部數(shù)組的數(shù)組指針改變了,外部數(shù)組的數(shù)組指針也應(yīng)該改變。這就是foreach不需要復(fù)制的原因。
迭代通過(guò)引用
上面的例子都是按值迭代的。對(duì)于引用迭代,應(yīng)用相同的規(guī)則,但是附加值引用更改數(shù)組值的復(fù)制行為(關(guān)于結(jié)構(gòu)復(fù)制的行為保持不變)。
情況“未引用,refcount == 1”沒(méi)有改變。引用迭代意味著如果$值有任何變化,我們想要改變?cè)紨?shù)組,這樣數(shù)組就不會(huì)被復(fù)制(證明)。
“被引用”的情況也保持不變,在這種情況下,對(duì)$value的更改應(yīng)該會(huì)更改引用迭代數(shù)組的所有變量(證明)。
只有“未引用,refcount > 1”的情況發(fā)生了變化,因?yàn)楝F(xiàn)在需要復(fù)制數(shù)組結(jié)構(gòu)及其值。數(shù)組結(jié)構(gòu),因?yàn)榉駝t函數(shù)外部的$array變量的數(shù)組指針會(huì)改變,而對(duì)$value的改變也會(huì)改變外部的$array值(證明)。
總結(jié)
當(dāng)且僅當(dāng)?shù)鷶?shù)組未被引用且具有refcount > 1時(shí),foreach將復(fù)制數(shù)組結(jié)構(gòu)
foreach還將復(fù)制數(shù)組值,前提是且僅當(dāng)上一個(gè)點(diǎn)應(yīng)用并且迭代是通過(guò)引用完成時(shí)
您可能感興趣的文章:- 在PHP中靈活使用foreach+list處理多維數(shù)組的方法
- vue.js 雙層嵌套for遍歷的方法詳解, 類似php foreach()
- PHP運(yùn)用foreach神奇的轉(zhuǎn)換數(shù)組(實(shí)例講解)
- PHP在彈框中獲取foreach中遍歷的id值并傳遞給地址欄
- php優(yōu)化查詢foreach代碼實(shí)例講解