1. 개요

Java 8에서 도입 된 forEach 루프는 프로그래머 에게 컬렉션을 반복하는 새롭고 간결하며 흥미로운 방법을 제공합니다.

이 튜토리얼에서는 컬렉션과 함께 forEach 를 사용하는 방법 , 인수의 종류,이 루프가 향상된 for-loop 와 어떻게 다른지 살펴볼 것 입니다.

일부 Java 8 개념을 다듬어야하는 경우 기사 모음이 도움이 될 수 있습니다.

2. forEach의 기초

Java에서  Collection  인터페이스에는  Iterable 이 수퍼 인터페이스로 있습니다. 이 인터페이스에는 Java 8로 시작하는 새로운 API가 있습니다.

void forEach(Consumer<? super T> action)

간단히 말해 forEachJavadoc" 모든 요소가 처리되거나 작업이 예외를 throw 할 때까지 Iterable 의 각 요소에 대해 지정된 작업을 수행합니다"라고 말합니다 .

따라서 forEach 를 사용하면 다른 Iterator 처럼 컬렉션을 반복하고 각 요소에 대해 주어진 작업을 수행 할 수 있습니다 .

예를 들어, 문자열 컬렉션반복하고 인쇄하는 for-loop 버전을 고려하십시오 .

for (String name : names) {
    System.out.println(name);
}

forEach를 사용하여 이것을 작성할 수 있습니다 .

names.forEach(name -> {
    System.out.println(name);
});

3. forEach 메소드 사용

우리는 사용  대해 forEach를 콜렉션을 반복하고 각 요소에 대해 특정 작업을 수행. 수행 할 작업은 Consumer 인터페이스 를 구현하는 클래스에 포함되어 있으며 forEach  에 인수로 전달됩니다 .

소비자 인터페이스는 기능 인터페이스 (하나의 추상적 인 방법으로 인터페이스). 입력을 받아들이고 결과를 반환하지 않습니다.

정의는 다음과 같습니다.

@FunctionalInterface
public interface Consumer {
    void accept(T t);
}

따라서 모든 구현, 예를 들어 단순히 String을 인쇄하는 소비자 :

Consumer<String> printConsumer = new Consumer<String>() {
    public void accept(String name) {
        System.out.println(name);
    };
};

forEach 에 인수로 전달할 수 있습니다 .

names.forEach(printConsumer);

그러나 이것이 소비자를 통해 작업을 생성하고 forEach API를 사용하는 유일한 방법은 아닙니다 .

forEach 메서드를 가장 많이 사용하는 세 가지 방법을 살펴 보겠습니다 .

3.1. 익명 소비자 구현

익명 클래스를 사용하여 Consumer 인터페이스 의 구현을 인스턴스화 한 다음 forEach 메서드에 인수로 적용 할 수 있습니다 .

Consumer<String> printConsumer= new Consumer<String>() {
    public void accept(String name) {
        System.out.println(name);
    }
};
names.forEach(printConsumer);

이것은 잘 작동합니다. 그러나 예제를 분석하면 유용한 부분이 실제로 accept () 메서드 내부의 코드라는 것을 알 수 있습니다 .

Lambda 표현식이 이제 표준이며이를 수행하는 더 쉬운 방법이지만 소비자 인터페이스 를 구현하는 방법을 아는 것은 여전히 ​​가치가 있습니다.

3.2. 람다 식

Java 8 기능 인터페이스의 주요 이점은 Lambda 표현식을 사용하여 인스턴스화하고 부피가 큰 익명 클래스 구현 사용을 피할 수 있다는 것입니다.

때문에 소비자 인터페이스 기능 인터페이스, 우리는 람다 그것을 표현할 수있다 :

(argument) -> { //body }

따라서 printConsumer 가 단순화되었습니다.

name -> System.out.println(name)

그리고 우리는 그것을 forEach에 전달할 수 있습니다  .

names.forEach(name -> System.out.println(name));

Java 8에 Lambda 표현식이 도입 된 이후로 이것은 아마도 forEach 메서드 를 사용하는 가장 일반적인 방법 일 것입니다 .

Lambda는 매우 실제적인 학습 곡선을 가지고 있으므로 시작하는  경우이 글  에서는 새로운 언어 기능을 작동하는 몇 가지 모범 사례를 살펴 봅니다.

3.3. 방법 참조

클래스에서 작업을 수행하기위한 메서드가 이미 존재하는 일반적인 Lambda 구문 대신 메서드 참조 구문을 사용할 수 있습니다.

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

4. forEach 작업

4.1. 컬렉션 반복

컬렉션 유형의 모든 이터 러블  list , set , queue 등 — forEach 를 사용하는 데 동일한 구문이 있습니다.

따라서 우리가 본 것처럼 List의 요소를 다음과 같이 반복 할 수 있습니다.

List<String> names = Arrays.asList("Larry", "Steve", "James");

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

그리고 세트는 비슷합니다.

Set<String> uniqueNames = new HashSet<>(Arrays.asList("Larry", "Steve", "James"));

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

마지막으로 컬렉션 이기도 한 Queue살펴 보겠습니다 .

Queue<String> namesQueue = new ArrayDeque<>(Arrays.asList("Larry", "Steve", "James"));

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

4.2. Map의 forEach를 사용하여 맵 반복

맵은 Iterable 이 아니지만 BiConsumer 를 허용하는 forEach  의 자체 변형을 제공합니다 .

자바는 8 개 소개하는 BiConsumer  대신 소비자 의 Iterable의에 대해 forEach 액션이의 키와 값을 모두 수행 할 수 있도록 Map 를 동시에.

다음 항목 으로 생성 해 보겠습니다 .

Map<Integer, String> namesMap = new HashMap<>();
namesMap.put(1, "Larry");
namesMap.put(2, "Steve");
namesMap.put(3, "James");

다음으로  Map의 forEach를 사용하여 namesMap반복 해 보겠습니다 .

namesMap.forEach((key, value) -> System.out.println(key + " " + value));

여기에서 볼 수 있듯이 BiConsumer 를 사용하여 Map 항목을 반복했습니다 .

(key, value) -> System.out.println(key + " " + value)

4.3. entrySet 를 반복 하여 반복

Iterable의 forEach를 사용하여 Map EntrySet 반복 할 수도 있습니다 .

때문에  a의 항목 Map가 A의 저장되어있는 세트 라는  EntrySet을, 우리는 사용하는 것을 반복 할 수 대해 forEach를 :

namesMap.entrySet().forEach(entry -> System.out.println(
  entry.getKey() + " " + entry.getValue()));

5. Foreach 대 For-Loop

단순한 관점에서 두 루프는 동일한 기능을 제공합니다. 즉, 컬렉션의 요소를 반복합니다.

그들 사이의 주요 차이점은 서로 다른 반복 자라는 것입니다. 향상된 for 루프 는 외부 반복자 인 반면 새로운 forEach 메서드는 내부입니다.

5.1. 내부 반복기 — forEach

이 유형의 반복기는 백그라운드에서 반복을 관리하고 프로그래머는 컬렉션의 요소로 수행 할 작업을 코딩 할 수만 있습니다.

대신 반복기는 반복을 관리하고 요소를 하나씩 처리합니다.

내부 반복기의 예를 살펴 보겠습니다.

names.forEach(name -> System.out.println(name));

위의 forEach 메서드에서 제공된 인수가 람다 식임을 알 수 있습니다. 즉, 메서드 는 수행 할 작업 만 알면 되고 모든 반복 작업은 내부적으로 처리됩니다.

5.2. 외부 반복기 — for-loop

외부 반복기 는 루프가 수행되는 내용방법을 혼합 합니다.

Enumerations , Iterators 및 향상된 for-loop 는 모두 외부 반복자입니다 ( iterator () , next () 또는 hasNext () ?). 이 모든 반복자에서 반복을 수행하는 방법을 지정하는 것이 우리의 임무입니다.

이 익숙한 루프를 고려하십시오.

for (String name : names) {
    System.out.println(name);
}

List을 반복하는 동안 명시 적으로 hasNext () 또는 next () 메서드를 호출하지는 않지만 이 반복 작업을 수행하는 기본 코드는 이러한 메서드를 사용합니다. 이것은 이러한 작업의 복잡성이 프로그래머에게 숨겨져 있지만 여전히 존재한다는 것을 의미합니다.

컬렉션이 자체적으로 반복을 수행하는 내부 반복 기와는 달리, 여기에서는 컬렉션에서 모든 요소를 ​​가져 오는 외부 코드가 필요합니다.

6. 결론

이 기사에서 우리는 forEach 루프가 일반 for 루프 보다 더 편리 하다는 것을 보여주었습니다 .

또한 forEach 메서드가 작동하는 방식과 컬렉션의 각 요소에 대해 작업을 수행하기 위해 인수로받을 수있는 구현 유형 도 살펴 보았습니다 .

마지막으로이 기사에 사용 된 모든 스 니펫은 GitHub 저장소 에서 사용할 수 있습니다 .