前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java-forEach增强for循环是值传递规则详解

Java-forEach增强for循环是值传递规则详解

作者头像
Fisherman渔夫
发布2020-02-17 23:46:01
3.1K0
发布2020-02-17 23:46:01
举报
文章被收录于专栏:渔夫

1. 引入

 正如Java语法意义,变量的传递只有值传递,虽然变量分为引用变量和基本类型变量,前者更像C中的地址概念。 在学习Lambda表达式的时候,遇到了试图在增强for循环中对原链表元素重新赋值失败的问题,网络上也没有针对此的其他博文,故开此文。

2. 数组的增强for循环

代码语言:javascript
复制
public class Test1{

    public static void main(String[] args) {
    
        int[] arr = new int[10];

        for (int temp :arr){
            temp++;
        }

        for (int temp :arr){
            System.out.println(temp);
        }


    }
}

 控制台会打出10个0,而不是1,这表明在forEach语句中temp++操作对arr数组本身没有任何影响,所以间接证明了,增强for循环中只是值传递。这也可以从原理层面解释:增强for循环作为一个语法糖,其执行顺序是:对数组第一个元素复制给临时变量temp,然后让temp执行循环中的语句;接着对数组第二个元素再次赋值给临时变量temp,再次让其执行for循环中的语句…就这般执行至数组最后一个元素。所以说,temp接受了数组元素的值,在++,这对于数组中的数字没有任何影响。所以说如果要进行原数组的更改,更好的方式是使用普通的for循环。

3. ArrayList的增强for循环

 代码需求是将其list中的String类型对象从小写转换为大写;

代码语言:javascript
复制
public class LowercaseToUppercase{

    public static void main(String[] args) {

        List<String> list = Arrays.asList("hello", "world", "hello world");

        list.forEach(i -> {
            i = i.toUpperCase();
        });

        list.forEach(System.out::println);
    }
}

 控制还是输出小写的String类型对象,“hello”, “world”, “hello world”,倘若你查看forEach方法,你可以发现此原理和第一个例子的数组遍历实现原理是一样的,i作为一个中间变量,是临时存放了String类型的引用变量,但是对原list没有任何影响,如下面被调用的forEach方法的默认实现代码(其中t就是被定义为泛型类型T的临时变量)。 一个易错点:很多人认为:因为String内部是final修饰的数组,不能被重新赋值,临时变量i只能指向新的引用对象,所以上述代码功能才不能被实现,这是不对的,其真正的原因是对临时变量赋值是无法达到预期效果。正确的理解是对临时变量进行赋值,只能使临时变量指向新的对象,而对原String对象没有任何作用。即使将上述代码中ArrayList的对象类型由String换成StringBuilder类,在这样的情况下,虽然同一个```StringBuilder``对象的值是可以被修改的,但是使用对临时变量赋值的操作还是不能对原数据结构元素值造成影响。

 如果要实现,需要调用StringBuilder类对象的方法,一般是返回this对象,代码如下所示:

代码语言:javascript
复制
public class LowercaseToUppercase {

    public static void main(String[] args) {
   
        List<StringBuilder> list3= Arrays.asList(new StringBuilder("hello"),
        new StringBuilder("world"),new StringBuilder("hello world"));

        list3.forEach(i->
                {
                    String str= i.toString().toUpperCase();
                    i.replace(0,str.length(),str);
                }

        );
        list3.forEach(System.out::println);

    }
}

 控制台输出了大写的字符串,说明我们成功将StringBuilder类型由小写转化为大写,不过遍历中的临时变量i的赋值语句并不存在,而是调用其方法,返回this对象,才实现了转换。

 下面这个代码块是Java集合的forEach方法默认实现,一定要读懂它:

代码语言:javascript
复制
   default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

 可见Java在foreach语言的执行上保证了原数据结构的安全性,如果确定要更改原数据结构,请使用传统的for循环。并且我们在foreach语句中可以采用复制给新数据结构的方法实现类似的作用:

代码语言:javascript
复制
		List<String> list2 = new ArrayList<>();

        list.forEach(item->list2.add(item.toUpperCase()));

        list2.forEach(System.out::println);

 倘若返回list2,那么和传统的for语句也是类似的效果。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 引入
  • 2. 数组的增强for循环
  • 3. ArrayList的增强for循环
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档