Java의 반복문과 Stream API 활용법

Yunsung·2025년 1월 7일
post-thumbnail

1. 반복문과 순회의 개념

반복문은 데이터를 처리하기 위해 사용되는 기본적인 프로그래밍 도구입니다. Java에서는 데이터를 순회하는 다양한 방법이 제공되며, 각 방식은 특성과 성능에서 차이가 있습니다.

이번 글에서는 Java에서 가장 많이 사용되는 4가지 반복문과 Stream API를 활용한 데이터 처리 방법을 예제와 함께 소개하겠습니다.


2. 반복문 종류 및 예제 코드

1) 기본 for 반복문

for문은 인덱스를 사용하여 데이터를 순회하는 가장 전통적인 방식입니다. 리스트나 배열 등 순서가 있는 데이터 구조에서 주로 사용되며, 인덱스 기반의 세부적인 작업에 적합합니다.

for (int i = 0; i < perAry.size(); i++) {
    PersonVO obj = perAry.get(i);
    System.out.println(obj.perInfo());
}

설명

  • for문은 인덱스를 직접 사용하여 데이터를 제어할 수 있습니다.
  • 데이터의 순서를 바꾸거나 특정 인덱스에 접근해야 하는 경우 유용합니다.
  • 하지만, 코드가 길어질 수 있고, 반복문의 종료 조건을 신경 써야 하므로 코드 유지보수성이 떨어질 수 있습니다.

2) Enhanced forEach

forEach는 Java 5부터 추가된 방식으로, 컬렉션이나 배열을 순회할 때 더욱 간결하게 작성할 수 있습니다.

for (PersonVO obj : perAry) {
    System.out.println(obj.perInfo());
}

설명

  • forEach는 데이터의 순서를 몰라도 컬렉션의 모든 요소를 순회할 수 있습니다.
  • 간결한 문법으로 작성하기 쉽고, 가독성이 뛰어납니다.
  • 하지만, 인덱스 기반 작업이 불가능하며, 특정 요소에 접근하거나 수정하기에는 적합하지 않습니다.

3) Iterator

Iterator는 컬렉션을 순회하기 위해 Java에서 제공하는 전용 인터페이스입니다. 이 방식은 내부적으로 순회 최적화가 이루어져 있으며, 컬렉션을 순회하는 가장 안전한 방식 중 하나입니다.

Iterator<PersonVO> ite = perAry.iterator();
while (ite.hasNext()) {
    System.out.println(ite.next().perInfo());
}

설명

  • Iterator는 컬렉션의 요소를 반복적으로 탐색하며, 동시성 문제를 방지합니다.
  • hasNext()메서드 : 현재 위치에서 다음 요소가 있는지 여부를 확인합니다. (true, false 반환)
  • next() 메서드 : 현재 위치의 다음 요소를 반환하고, 내부적으로 현재 위치를 다음 요소로 이동합니다.
  • 반복 중 컬렉션의 요소가 변경되면 예외를 발생시켜 데이터 무결성을 보장합니다.
  • 성능이 뛰어나며, 컬렉션에서 요소를 동적으로 제거( remove() )하는 등의 작업이 가능합니다.

4) Stream Lambda

Stream API는 Java 8부터 도입된 기능으로, 함수형 프로그래밍 스타일로 데이터를 처리할 수 있습니다. Stream은 데이터 필터링, 매핑, 집계 등 복잡한 작업을 간단하게 처리할 수 있는 도구입니다.

System.out.println("Stream lambda 순회");
perAry.stream().forEach(obj -> System.out.println(obj.perInfo()));

설명

  • Stream은 컬렉션의 요소를 병렬 처리하거나, 순차적으로 변환 및 집계할 때 유용합니다.
  • 간결하고 직관적인 코드를 작성할 수 있으며, 대량 데이터를 처리할 때 성능 최적화가 가능합니다.
  • forEach와 달리 필터링, 매핑 등 추가 작업을 간편하게 할 수 있습니다.

3. Stream API 활용 예제

1) 조건 필터링

Stream API를 사용하면 데이터 필터링 작업이 매우 간단합니다.
예를 들어, 이름이 'j'로 시작하는 객체를 필터링하려면 아래와 같이 작성할 수 있습니다.

System.out.println("Question01)");
list.stream()
    .filter(entity -> entity.getName().startsWith("j"))
    .forEach(System.out::println);

설명

  • filter는 조건에 맞느 요소만 추출합니다.
  • 함수형 인터페이스인 람다를 사용해 간결하게 조건을 정의할 수 있습니다.

2) Optional로 안전한 처리

필터링한 데이터가 없을 경우, Optional을 사용하면 null대신 안전한 처리를 보장받을 수 있습니다.

System.out.println("Question02) filter 요소를 찾을 때");
Optional<TestDTO> result = list.stream()
    .filter(entity -> entity.getName().startsWith("j"))
    .findFirst();
System.out.println(result);

설명

  • Optional은 값이 존재하지 않을 때 발생할 수 있는 NullPointerException을 방지합니다.
  • findFirst는 조건에 맞는 첫 번째 요소를 반환합니다.

3) Map과 Reduce로 집계하기

Stream API의 mapreduce를 사용하면 데이터를 집계하거나 연산할 수 있습니다.
예를 들어, 객체들의 나이를 모두 합산하려면 아래와 같이 작성합니다.

System.out.println("Question02) 각각 객체의 나이요소를 집계하고 싶다면?");
Optional<Integer> sum = list.stream()
    .map(TestDTO::getAge)
    .reduce(Integer::sum);

sum.ifPresent(System.out::println);

설명

  • map은 객체의 특정 필드를 추출합니다.
  • reduce는 추출된 데이터를 집계하거나 누적 연산을 수행합니다.

4. 결론

  • 기본 반복문에서 Stream API로 갈수록 코드의 간결성과 효율성이 증가합니다.
  • Stream API는 특히 데이터 필터링, 변환, 집계와 같은 작업에서 뛰어난 생산성을 제공합니다.
  • 적절한 반복문 방식을 선택하여 코드의 가독성과 유지보수성을 높을 수 있습니다.

이 글이 반복문과 Stream API에 대한 이해를 높이는 데 도움이 되었길 바랍니다! 😊

profile
풀스택 개발자로서의 도전을 하는 중입니다. 많은 응원 부탁드립니다!!😁

0개의 댓글