使用 Java foreach 遇到的问题

本文通过实例对比了使用foreach与for循环修改数组元素的效果,揭示了foreach无法直接修改数组元素的原因,并提供了正确的实现方式。

本来以为使用foreach就和使用for循环是一样的效果,结果,却遇到了意料之外的事情。

先来看一个例子:

    public void testArray() {
        //定义一个一维数组 
        int arr[] = {1,3,2,4}; 
        System.out.println("----输出刚刚定义的数组----"); 
        for (int x : arr) {             
            System.out.println(x);          
        } 

        //通过索引给数组元素赋值 
        System.out.println("----通过foreach循环修改数组元素的值----"); 
        for (int x : arr) { 
            x *= 2;
            System.out.println(x);          
        } 

        //循环输出创建的数组 
        System.out.println("----修改后,foreach重新输出数组元素的值----"); 
        for (int x : arr) { 
            System.out.println(x); 
        }        
    }

运行出来的结果:

----输出刚刚定义的数组----
1
3
2
4
----通过foreach循环修改数组元素的值----
2
6
4
8
----修改后,foreach重新输出数组元素的值----
1
3
2
4

咦,怎么没有修改过来?

网上查找答案,这个说得比较明白:

https://www.zhihu.com/question/36905481

给你举一个例子,你看一下可能比较能理解:

for(int element:array1){//question:为什么用Foreach无法给数组赋值。
   element =  r.nextInt(101)+1;
   System.out.println(element);

}//虽然可以输出十个随机数,但并没有把十个随机数赋给array1数组。

这其中的执行过程是这样的
element = array1[i]
element = 随机数
syso(element)
element是个基本数据类型,他不指向数组元素的地址,他只代表数字他自己。

当理解了这个例子,你现在就应该知道foreach这东西的限制了。那就是不能修改数组元素,为什么呢?因为每一次loop你只是获得了当前元素的值,却没有获得当前元素在数组中的对象句柄。这样你就无法直接操作对应的句柄。

所以当你想遍历数组并只想获得元素值的时候才用foreach。其他的时候比如想修改数组,或者想获得index的时候就别用foreach。

现在,我们是想修改数组的值,那么就不能使用foreach了,还是使用for循环吧:

    public void testArray1() {
        //定义一个一维数组 
        int arr[] = {1,3,2,4}; 
        System.out.println("----输出刚刚定义的数组----"); 
        for (int x : arr) {             
            System.out.println(x);          
        } 

        //通过索引给数组元素赋值 
        System.out.println("----通过循环变量给数组元素赋值----");        
        for (int i = 0; i < arr.length; i++) { 
            arr[i] *= 2; 
            System.out.println(arr[i]);         
        } 

        //循环输出创建的数组 
        System.out.println("----修改后,foreach重新输出数组元素的值----"); 
        for (int x : arr) { 
            System.out.println(x); 
        }        
    }

运行结果如下:

----输出刚刚定义的数组----
1
3
2
4
----通过循环变量给数组元素赋值----
2
6
4
8
----修改后,foreach重新输出数组元素的值----
2
6
4
8

长吁一口气,终于恢复正常了。

参考:
http://www.cnblogs.com/hnrainll/archive/2011/11/13/2247422.html
https://www.zhihu.com/question/36905481

Java 编程中,`Stream.forEach()` 方法用于遍历流中的每个元素并对它们执行给定的操作。如果遇到 `java.lang.NullPointerException` 错误,则通常意味着程序试图访问空对象引用的内容或调用其方法。当涉及到 Stream API 和 `forEach()` 时,这个异常可能是由以下几个常见的原因引起的: 1. **集合本身为空(null)**:如果你尝试将一个 null 的 List 或 Set 转换为 stream 并对其进行迭代,那么这将会导致 NPE。所以在创建流之前应该先检查是否为非空。 2. **流内部含有 null 元素**:即使集合不是 null,但如果其中包含了 null 值并且你在 forEach 内部直接对这些元素进行某些未考虑过的情况下的操作(如调用成员变量、方法等),这也可能会引发 NullPointerException。 3. **外部资源依赖问题**:有时候我们会在 lambda 表达式里面使用来自闭包作用域之外的对象或静态字段。如果有任何一个这样的对象是 null,并且在其上调用了方法或者是进行了其他不允许有 null 值的操作的话,同样会造成 NPE。 为了避免这些问题的发生,可以采取以下措施来保证代码的安全性和健壮性: - 确保传入的 Collection 不为 null,并使用 Optional 类型或者其他机制来进行空值处理。 - 如果允许存在 null 元素,在 foreach 循环体内加入适当的判断逻辑以防止潜在的 NullPointException 异常抛出。 - 尝试简化 Lambda 表达式的复杂度,尽量减少对外围环境变量的依赖;同时也要注意线程安全的问题,尤其是在并发环境下工作的时候。 这里给出一段改进后的样例代码作为示范: ```java import java.util.Arrays; import java.util.List; import java.util.Optional; public class Main { public static void main(String[] args) { // 初始化一个包含可能为 null 元素的列表 List<String> stringList = Arrays.asList("Hello", "World", null, "!"); // 正确的方式: 在转换成 Stream 之后过滤掉所有 null 元素再做后续操作 stringList.stream() .filter(Optional::isPresent) // 注意这里的写法适用于 String 及基本类型包装类 .map(Optional::get) .forEach(System.out::println); // 或者更简单地直接排除 nulls: stringList.stream() .filter(item -> item != null) .forEach(System.out::println); // 错误的做法会触发NullPointerException //stringList.stream().forEach(s -> System.out.println(s.length())); // 这里如果没有前面的 filter() 判断就会报错 } } ``` 上述代码片段展示了两种有效的方式来避免因为 null 导致的运行期错误:一种是在进入 map 操作前利用 filter 来去除所有的 null 元素;另一种则是明确地通过条件表达式 (`item != null`) 排除掉任何可能导致问题的地方。 另外需要注意的是,在实际项目开发过程中,最好养成习惯始终对可能出现 null 地方保持警惕并加以适当防护,无论是显式还是隐式的形式都如此。这样做不仅可以提高应用程序的整体质量,也能大大降低调试成本和维护难度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值