【问题标题】:while(list($key, $value) = each($array)) vs. foreach($array as $key => $value)?while(list($key, $value) = each($array)) 与 foreach($array as $key => $value)?
【发布时间】:2011-03-19 07:22:01
【问题描述】:

最近我遇到了这个奇怪的问题:

while(list($key, $value) = each($array))

没有列出所有数组值,将其替换为...

foreach($array as $key => $value)

...完美运行。

而且,我现在很好奇..这两者有什么区别?

【问题讨论】:

  • 注意:foreach() 适用于 96% 的所有用例。但是,有时您必须在迭代期间更改 数组本身,这就是 each() 的强大之处,因为 foreach 在内部复制数组。请看下面我的回答。任何使用过 libxml 的人都可以证明在每种情况下盲目使用 foreach 的危险。

标签: php arrays loops foreach while-loop


【解决方案1】:

警告! Foreach 创建数组的副本,因此您不能在 foreach 对其进行迭代时对其进行修改each() 仍然有一个目的,如果您在循环遍历它的元素和索引时对数组进行实时编辑,它会非常有用。

// Foreach creates a copy
$array = [
  "foo" => ['bar', 'baz'],
  "bar" => ['foo'],
  "baz" => ['bar'],
  "batz" => ['end']
];

// while(list($i, $value) = each($array)) { // Try this next
foreach($array as $i => $value) {
  print $i . "\n";
  foreach($value as $index) {
    unset($array[$index]);
  }
}

print_r($array); // array('baz' => ['end'])

foreachwhile 都将完成它们的循环,并且数组“$array”将被更改。然而,foreach 循环在循环时并没有改变 - 所以即使我们删除了它们,它仍然会遍历每个元素。

更新:这个答案没有错。

我认为这个答案非常直截了当,但似乎这里的大多数用户无法理解我在这里提到的具体细节。

使用 libdom(如删除元素)或其他密集的 map/list/dict 过滤构建应用程序的开发人员可以证明我在这里所说的重要性。

如果你不理解这个答案,它总有一天会咬你。

【讨论】:

  • 是的。但是你总是可以使用foreach($array as $key => &$value) {},它会给你一个元素的引用。
  • @KonstantinBodnya,你完全错过了我的回答。如果你需要改变 array (不是元素)你必须使用each().
  • 传递对数组中元素的引用将改变数组。
  • @AndrewWillis,它会改变数组中的元素,但不会改变数组本身。换句话说,您不能执行移除元素之类的操作,从而更改实际的项目数组。
  • @A.L,查看“print $i”输出以查看差异。这里重要的是要了解对当前正在迭代的实际数组的修改(each())与对最终输出数组的修改(foreach()
【解决方案2】:

嗯,一个区别是each() 只能在数组上工作(当然只能正常工作)。 foreach 将适用于实现traversable 接口的任何对象(当然包括内置数组类型)。

foreach 中可能存在微优化。基本上,foreach 等同于以下内容:

$array->rewind();
while ($array->valid()) {
   $key = $array->key();
   $value = $array->current();
   // Do your code here
   $array->next();
}

each 基本上做了以下事情:

$return = $array->valid() ? array($array->key(), $array->current()) : false;
$array->next();
return $return;

所以三行对两者来说都是一样的。它们都非常相似。可能有一些微优化,each 不需要担心traversable 接口......但这充其量只是次要的。但它也将通过进行布尔转换和检查 php 代码与foreach 的编译 C 来抵消......更不用说在你的 while/each 代码中,你正在调用两种语言结构和一个函数,而对于foreach,它是一种单一的语言结构......

更不用说foreach 更易读,恕我直言...更容易阅读,更灵活意味着-对我来说-foreach 是明显的赢家。 (这并不是说each 没有它的用​​途,但我个人从来不需要它)...

【讨论】:

  • 这并不完全相同,因为foreach 的行为就像它在数组的副本中操作一样。例如,如果您在迭代期间更改了原始数组的一个元素,foreach 仍会为您提供原始元素。仅当您开始使用引用(例如foreach ($array as &$v) {})时,此行为才会发生变化,因为在这种情况下,您表示您打算更改数组。见codepad.viper-7.com/bGaIqc
  • 您的回答还有问题。数组不实现Traversable 接口;它们甚至不是对象(尝试使用具有Traversable 类型提示的数组构建IteratorIterator)。对于foreach 的目的,它们的行为类似,但在某些情况下,您必须使用ArrayIterator 将其转换为可遍历的对象。
  • 内部 Array 对象确实实现了Traversable。你不能用它来代替迭代器,因为它没有实现IteratorIteratorAggregate...但是在内部,它的功能是一样的...
  • 恐怕你弄错了。数组不是对象。通过检查class_exists("array") 的返回值可以很容易地看到没有Array 类。
【解决方案3】:

如果您传递 each 一个对象进行迭代,PHP 手册会警告它可能会产生意外结果。

$array 中到底是什么

【讨论】:

    【解决方案4】:

    您之前是否遍历过数组? each() 记住它在数组中的位置,所以如果你不 reset() 它你可能会错过项目。

    reset($array);
    while(list($key, $value) = each($array))
    

    这种遍历数组的方法很古老,而且已经被更惯用的 foreach 所取代。我不会使用它,除非您特别想利用它的一次一件物品的特性。

    数组 每个 (数组 &$array)

    从数组中返回当前的键值对并前进数组光标。

    each() 执行后,数组光标将停留在数组的下一个元素上,如果到达数组末尾,则超过最后一个元素。如果您想再次使用each 遍历数组,则必须使用reset()

    (来源:PHP Manual

    【讨论】:

    • 很好的比较。我发现while each 是在需要时从while 块内向$array 添加元素的好方法。
    • 非常有趣的比较!每种方法之间的性能差异如何,它们实际上是否相同?
    猜你喜欢
    • 2011-08-16
    • 2010-11-16
    • 2022-01-05
    • 2013-08-31
    • 2022-12-02
    • 2013-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多