Java foreach总结

本文详细介绍了Java中的foreach循环,包括其特性总结,如仅适用于数组和实现了Iterable接口的集合,不支持索引操作,以及不能直接使用流程控制关键字。同时,深入解析了foreach的内部工作原理,揭示了它依赖迭代器和lambda表达式,解释了为何在lambda中无法使用return/break/continue,以及为什么不能改变外部变量的值。

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

foreach

foreach循环是Java5针对数组和集合推出的语法糖,Java8开始融入函数式编程和lambda表达式

先看几个简单的例子

package pers.aslania.thinkinjava.submodule;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

/**
 * Created by Aslania on 2018/3/25.
 */
public class ForEach {

    public static void traditionalForeach(Collection collection) {
        Iterator iterator = collection.iterator();
        if (iterator != null) {
            for (; iterator.hasNext();)
                System.out.println(iterator.next());
        }
    }

    public static void traditionalForeach(Object[] arrays) {
        for (int i = 0; i < arrays.length; i++) {
            System.out.println(arrays[i]);
        }
    }

    public static void Java5Foreach(Collection collection) {
        for (Object o : collection) {
            System.out.println(o);
        }
    }

    public static void Java5Foreach(Object[] arrays) {
        for (Object o : arrays) {
            System.out.println(o);
        }
    }

    public static void Java8Foreach(Collection collection) {
        collection.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });
    }

    public static void Java8ForeachWithLambda(Collection collection) {
        collection.forEach(item -> System.out.println(item));
    }

    public static void main(String[] args) {
        Integer[] intArray = new Integer[]{0, 1, 2, 3, 4};
        List<Integer> intList = Arrays.asList(intArray);

        traditionalForeach(intList);
        traditionalForeach(intArray);

        Java5Foreach(intList);
        Java5Foreach(intArray);

        Java8Foreach(intList);
        Java8ForeachWithLambda(intList);
    }
}

foreach特性总结

  • 仅面向数组、实现了Iterable接口的集合
  • 无法使用索引
  • 无法正常使用return,break,continue等关键字进行流程控制
  • foreach所使用的外部变量只能是final变量
  • foreach不能改变所遍历元素的值
  • 对数组和Java提供的Iterable实现类只能使用正向遍历

foreach深入解析

  • foreach循环的核心在于迭代器,遍历的方式取决于迭代器next()方法的实现,所以对Java提供的Iterable实现类只能使用正向遍历
package pers.aslania.thinkinjava.submodule.foreach;

import java.util.Iterator;

/**
 * @Author: Aslania
 * @Date: Created in 16:37 2018/3/26
 * @Description: Reversed 
 */
public class ReversedList<E> implements Iterable<E> {
    private Node<E> first;
    private Node<E> last;

    public void add(E element) {
        if (last != null) {
            last.next = new Node<E>(last, element, null);
            last = last.next;
        } else {
            first = last = new Node<E>(null, element, null);
        }
    }

    /**
     * foreach循环的关键,利用集合实现的iterator()方法所提供的迭代器来实现集合的循环遍历
     * @return
     */
    @Override
    public Iterator<E> iterator() {
        return new ReversedListIterator(last);
    }

    private class ReversedListIterator implements Iterator<E> {
        private Node<E> current;

        public ReversedListIterator(Node<E> current) {
            this.current = current;
        }

        @Override
        public boolean hasNext() {
            return (null != current) && (null != current.prev);
        }

        /**
         * foreach循环的关键,决定循环遍历的方式,这里采用自定义的反向遍历
         * @return
         */
        @Override
        public E next() {
            if (current != null) {
                E element = current.item;
                current = current.prev;
                return element;
            }
            return null;
        }
    }

    private static class Node<E> {
        E item;
        ReversedList.Node<E> next;
        ReversedList.Node<E> prev;

        Node(ReversedList.Node<E> prev, E element, ReversedList.Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

    public static void main(String[] args) {
        ReversedList<Integer> reversedList = new ReversedList<>();
        for (int i = 0; i < 10; i++) {
            reversedList.add(i);
        }

        reversedList.forEach(item -> System.out.println(item));
    }
}
///////////////////////////////////////////////
/* Output
9
8
7
6
5
4
3
2
1
*/
///////////////////////////////////////////////
  • foreach的实现原理

比较普通的for循环和foreach的代码,可以发现foreach帮开发者简化了很多代码,实际上简化的这部分代码编译器会帮忙补上去。翻阅Iterable接口的foreach方法的默认实现可以发现编译器会将foreach的lambda表达式的内容会以Consume的形式传递给foreach,所以下面Java8Foreach和Java8ForeachWithLambda方法底层代码是相似的

// Iterable.java
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
    public static void Java8Foreach(Collection collection) {
        collection.forEach(new Consumer() {
            @Override
            public void accept(Object o) {
                System.out.println(o);
            }
        });
    }

    public static void Java8ForeachWithLambda(Collection collection) {
        collection.forEach(item -> System.out.println(item));
    }

了解了这一点后,不难理解下面两点:
1. return/break/continue在foreach的lambda中不能起到预期的作用
2. foreach代码块内不能改变外部变量的值,例如下面代码编译会报错

    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add(i);
    }
    int sum = 0;
    list.forEach(item -> sum += item); // Compiler error: Variable used in lambda expression should be final or effectively final
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值