foreach(),list(),each()结构

本文详细介绍了PHP中遍历数组的三种方式:foreach()结构,包括其两种语法形式和注意事项;list()语言结构,重点讨论了它如何将数组中的值赋给变量以及其工作机制;还有each()函数,解释了其返回数组当前指针位置的键值对并移动指针的原理。通过实例分析,帮助理解这三种遍历方式的使用和潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

a.关于foreach()

首先foreach()是一种循环控制结构,类似于for语句。

foreach()结构提供了一种简便遍历数组的方法;它只能用于数组,当你将它用于其他数据类型或者一个未初始化的变量时,会产生错误!foreach()结构有两种语法:

第一种:

foreach (array_expression as $value)

//do something
说明:这里array_expression为要遍历的数组,当foreach()开始工作的时候,会将数组中当前元素的值赋给变量$value;每执行一次循环后,数组指针向下移动一位,指向下一个数组元素;变量$value的作用就是存放遍历数组时数组指针指向的当前数组元素的值。(变量名取为value仅仅是为了便于理解,实际应用时你可以取任意合法的名字)

第二种:

foreach (array_expression as $key => $value)

//do something
说明:第二种语法和第一种不同之处在于该语法执行时会将数组指针指向的当前数组元素的关键字值或索引值存放到变量$key中,而将与该关键字或索引相对应的数组元素的值存放到变量$value中。(同样这里变量名key和value的也是为了便于理解,实际用的时候你可以自由取名)

注1:当foreach()开始执行的时候,数组内部的指针会自动指向数组的第一个元素,这就是说不需要再使用foreach()之前调用reset().

注2:foreach()操作的是指定数组的一个拷贝,而不是数组本身,它不会对数组本身造成影响;若想修改数组元素,指定数组必须是被引用的。手册上讲的foreach对数组指针有些副作用,在foreach循环中或后都不要依赖数组指针的值,除非对其重置,我的理解是:在使用foreach操作一个数组后,如要使用该数组指针,最好将该数组指针进行重置。

下面是关于数组被引用的例子:

<?php
$array_1=array(1,2,3);
foreach($array_1 as $v1)
{
   echo $v1.'</br>';  //输出:1 2 3
   $v1=$v1*2;
}

foreach($array_1 as &$v2)
{
   echo $v2.'</br>'; //输出:1 2 3
   $v2=$v2*2;
}
foreach($array_1 as $v3)
{
  echo $v3.'</br>';     //输出:2 4 6
}
?>

但是如果我们将foreach($array_1 as &$v2)中的$array_1替换成array(1,2,3),即这样:foreach(array(1,2,3) as &$v2) 将会报错,这是因为foreach要求被遍历的数组可以被引用才能够使用,前面的$array_1是个数组变量,变量是可以被引用的,而array(1,2,3)是一个语言结构,类似于echo,我的理解是语言结构是无法被引用的,前面我们只是用array(1,2,3)这个语言结构创建了数组变量$array_1.


b.关于list()

list()的作用是将数组中的值分别赋给一些变量,且它只能用于数字索引的数组并假定索引是从0开始的。

关于list只能用于数字索引的数组并假定索引从0开始,这里有个例子:

<?php
$arr_1[2]='yong';
$arr_1[0]='qiang';
$arr_1[1]='xin';
var_dump($arr_1);
list($name1,$name2,$name3)=$arr_1;
echo "$name1 -- $name2 -- $name3";
//qiang--xin--yong
?>
list开始工作总是假定目标数组的元素是从数字索引0开始,然后到1,再到2,依次从小索引值到大索引值按顺序"排列";它并不关心索引值为0的元素是否真的是数组的第一个元素,开始工作后它就按它假定的顺序给其参数赋值(这是工作步骤一)。理解这个假定规则很重要,后面讲到的list工作步骤二会涉及到目标数组元素的’顺序‘,特别是如果我们要用list和each遍历数组时,这个假定规则是必须要理解的。简言之,list工作前会将目标数组重新排序,然后以排序后的数组工作,如上例中原数组$arr_1为$arr_1=array(2=>'yong',0=>'qiang',1=>'xin'),经排序后的$arr_1为:$arr_1=array(0=>'qiang',1=>'xin',2=>'yong');当然原数组自始至终是没有改变的。这里的排序是list工作的步骤一,后面会讲到list的工作步骤二。

list()类似于array(),是一个语言结构,并不是一个真正的函数。

例子1:

<?php
$arr_1=array('yong','000','man')
list($name,$id,$sex)=$arr_1;
echo $name."--".$id."--".$sex;
//yong--000--man
?>

例2:

<?php
$a=array(1,2,3);
list($a,$b,$c)=$a;
var_dump($a,$b,$c);
//int 1
//int 2
//int 3 
?>

下面来讨论下我从官网上看到的一个关于list的一个例子:

例3
<?php
$a=array(1,2,3);
list($c,$b,$a)=$a;
var_dump($a,$b,$c);
//int 3
//null
//null
?>
比较下例3和例2有什么不同?没错,这两个例子里的list参数顺序是相反的,按照我们预期的例3输出的应该是int 3,int 2,int 1才对,但结果并不是那样.

这里面涉及到list工作机制(即工作步骤二)的问题:list()是从最右边的参数开始赋值的,是否还记得前面讲的工作步骤一,这里的最右边是经重新排序后的数组的最右边,如例2中,当list开始工作时,首先将$a数组中的数组元素3的值赋给变量$c,其次是将数组元素2的值赋给变量$b,最后才将数组元素1的值赋给变量$a;它的这种工作机制在list()的参数都是单纯变量的时候没有问题---我们预想的和我们得到是一致的,但当我们使用具有索引的数组时,情况可能会刚好相反,如下例2.1:

<?php
$arr_2=array('name','id','sex');
list($ar[0],$ar[1],$ar[2])=$arr_2;
foreach($ar as $key=>$value)
{
   echo $key.'=>'.$value.'</br>';
}
//2=>sex
//1=>id
//0=>name
?>
我们期望的结果是:0=>name

                              1=>id

                              2=>sex

但事实是相反的,为什么会这样呢?当例2.1中list开始赋值工作时,首先将‘sex’赋值给$ar[2],注意就在此刻!数组变量$ar被创建了,并且它有了一个索引值为2,元素值为‘sex’的元素, 此时$ar[2]是数组$ar的第一个元素,然后list会将‘id’赋值给$ar[1],此时进行的是数组$ar添加了第二个元素,元素的索引为1,值为‘id’;后面依次类推,这就是为什么我们得到的结果和预期是相反的原因。

我们再回到例3,理解了例2.1,例3就方便理解了。

例3中当list开始赋值工作时,将进行这样一个操作:$a=$a[3],初看这个操作是将数组$a的第3个,也即最后个元素的值赋值给变量$a,但其实是对变量$a进行了重新赋值,同时更改了变量的数据类型---由数组数据类型更改为了整型(integer)。此时!的$a不再是原来的那个数组变量$a了,于是当list准备再去读取‘数组变量’$a的倒数第二个元素的值(也就是我们看到的2)时,发现找不到原先的‘数组变量’$a了,只有一个整型变量$a(当然它是否能够辨别,这里就不管啦),整型参数不符合工作条件啦,这样工作进行不下去了;于是无法完成给变量$b,$c赋值,最终$b,$c被系统设置为NULL类型变量。


c.关于each()

each()语法:

array each(array   &$array)

该函数的作用:返回 array 数组中当前指针位置的键/值对并向前移动数组指针,键值对被返回为四个单元的数组,键名为 0,1,key 和 value,单元 0 和 key 包含有数组单元的键名,1 和 value 包含有数据。如果内部指针越过了数组的末端,则 each() 返回 FALSE。

先看一个例子:

例1:

<?php
    $a=array('green'=>'tree','blue'=>'sky','white'=>'cloud');
     $b=each($a);
     print_r($b);
     // array([1]=>tree,[value]=>tree,[0]=>green,[key]=>green)
?>
实际上each()工作中会进行两个操作,一个即返回当前指针位置的键/值对,第二个是向前移动数组指针;因此使用完each后,如果需要再次使用数组的指针,则最好先使用reset().

使用each()和list()可以实现遍历数组的功能:

例4

<?php
$x=array('apple','banana','pear');
while(list(,$fruit)=each($x))
{
   echo "the fruit is ".$fruit."</br>";
}
//the fruit is apple
//the fruit is banana
//the fruit is pear
?>
当然如果仅仅遍历一个数组,foreach()实现相同功能会更简洁些:

<?php
$x=array('apple','banana','pear');
foreach($x as $fruit)
{
  echo "the fruit is ".$fruit."</br>"; 
}
//the fruit is apple
//the fruit is banana 
//the fruit is pear
?>


OK,关于foreach(),list(),each()目前就了解这些啦,若有遗漏,欢迎大家补充!





### 如何在Java中通过 `forEach` 循环更新 List 元素 在 Java 中,`forEach` 方法主要用于对集合中的每个元素执行某种操作。然而需要注意的是,在默认情况下,`forEach` 并不支持直接修改原始列表中的对象状态[^2]。这是因为 Lambda 表达式通常被设计为函数式接口的一部分,强调不可变性和副作用最小化。 但是,当需要更新 `List` 的元素时,可以通过以下方式实现: #### 使用引用类型的对象 如果 `List` 存储的是复杂对象(而非基本数据类型),则可以在 `forEach` 内部修改这些对象的状态。例如: ```java import java.util.Arrays; import java.util.List; class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } public class Main { public static void main(String[] args) { List<Person> personList = Arrays.asList( new Person("Alice", 25), new Person("Bob", 30) ); // 更新每个人的年龄加1岁 personList.forEach(person -> person.setAge(person.getAge() + 1)); personList.forEach(System.out::println); } } ``` 上述代码展示了如何利用 `forEach` 对象内部属性进行更改[^4]。由于 `Person` 是一个可变类,因此可以直接对其字段赋新值。 #### 注意事项:并发修改异常 尝试直接替换或删除 `List` 中的元素可能会触发 `ConcurrentModificationException` 异常[^3]。这意味着不能简单地在一个增强型 `for-each` 或流式操作期间改变底层结构。不过对于本例来说,只要不对容器本身做结构性变更(比如增删节点),就不会遇到这个问题。 #### 替代方案——Stream API 结合 map() 除了使用 `forEach` 外,还可以借助 Stream API 来完成类似的转换任务: ```java personList = personList.stream() .map(p -> { p.setAge(p.getAge()+1); return p; }) .collect(Collectors.toList()); System.out.println(personList); ``` 这种方法更加清晰地表达了意图,并且返回了一个新的列表实例而不是原地修改旧的一个。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值