Notice
Recent Posts
Recent Comments
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

Spring & Java

스트림 ( Stream ) 본문

JAVA 개념 확장

스트림 ( Stream )

dev.hyuck 2025. 12. 18. 16:58

예외 ( Exception )과 예외처리 (try-catch)

Optional - null을 다루는 법

제네릭 ( Generic )

컬렉션 ( Collection )

람다 ( Lambda )

스트림 ( Stream )

 

학습 키워드 점검

스트림 - 데이터를 효율적으로 처리할 수 있는 흐름

map  - 각 요소를 변환하는 중간 연산 중 하나

filter - 조건에 맞는 요소만 걸러내는 중간 연산 중 하나

 

스트림 ( stream ) 이란?

스트림이 무엇인지 알아봅시다.

● 스트림은 데이터를 효율적으로 처리할 수 있는 흐름 입니다.

● 선언형 스타일로 가독성이 굉장히 뛰어납니다.

● 데이터 준비 -> 중간 연산 -> 최종연산 순으로 처리 됩니다.

● 스트림은 컬렉션 ( List, Set 등) 과 함께 자주 활용 됩니다.

● 오늘 수업에서는 실무에서 자주 활용되는 map() 과 filter() 예시를 살펴 보겠습니다.

 

비교해보기 ( for vs 스트림 )

각 요소를 10배로 변환 후 출력하는 예시로 알아봅시다.

● arrayList 의 각 요소를 10배로 변환 합니다.

● 아래 예시를 보고 for 문과 스트림을 비교해 봅시다.

List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // ✅ for 명령형 스타일: 각 요소 * 10 처리
        List<Integer> ret1 = new ArrayList<>();
        for (Integer num : arrayList) {
            int multipliedNum = num * 10; // 각 요소 * 10
            ret1.add(multipliedNum);
        }
        System.out.println("ret1 = " + ret1); 
    }
}

public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // ✅ 스트림 선언적 스타일: 각 요소 * 10 처리
        List<Integer> ret2 = arrayList.stream().map(num -> num * 10).collect(Collectors.toList());
        System.out.println("ret2 = " + ret2);
    }
}

 

ArrayList를 List로 받는 이유 

● 다형성을 이용해 List 인터페이스로 ArrayList 구현체를 받으면 나중에 다른 구현체 ( LinkedList , vector ) 로 변경할 때 코드 수정을 최소화 할 수 있기 때문입니다.

● 실무에서 리스트를 선언할 때 대부분 아래와 같이 List 타입으로 받는 것을 권장 합니다.

List<Integer> arrayList = new ArrayList<>();

 

스트림 처리 단계를 살펴봅시다.

● 스트림은 데이터 처리를 위해 여러 API를 제공합니다

● 관련 문서 > https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

● 아래는 대표적인 API 예시 입니다.

 

스트림을 사용하여 각 요소를 10배로 변환 후 리스트로 변환하는 예제 입니다.

● Stream() > map > collect() 순으로 데이터 흐름을 처리 합니다.

● Stream(): 데이터 준비 - 데이터를 스트림으로 변환하여 연산 흐름을 만들 준비합니다.

● map () : 중간 연산 등록 - 각 요소를 주어진 함수에 적용해서 변환 합니다.

● collect () : 최종 연산 - 결과를 원하는 형태 ( List, Set) 로 수집합니다.

● 반복문 없이 간결하게 데이터 변환이 가능 합니다.

arrayList
	.stream()  // 1. 데이터 준비
	.map()     // 2. 중간 연산 등록
	.collect() // 3. 최종 연산
    
// 1. 데이터 준비: 스트림 생성
Stream<Integer> stream = arrayList.stream();

// 2. 중간 연산 등록: 각 요소를 10배로 변환 로직 등록
Stream<Integer> mappedStream = stream.map(num -> num * 10);

// 3. 최종 연산: 최종 결과 리스트로 변환
List<Integer> ret2 = mappedStream.collect(Collectors.toList());

// ✅ 한 줄로 표현 가능
List<Integer> ret2 = arrayList.stream() // 1. 데이터 준비
    .map(num -> num * 10)               // 2. 중간 연산 등록
    .collect(Collectors.toList());  // 3. 최종 연산

 

스트림과 람다식 활용

스트림과 람다식을 함께 사용해서 다시 한번 각 요소 * 10 예시를 만들어 봅시다.

// map() 메서드를 살펴봅시다.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

- > 함수형 인터페이스를 매개변수로 가지고 있습니다.

- > 즉, 함수형 인터페이스를 구현한 구현체를 매개변수로 받을 수 있습니다.

● 익명 클래스와 람다를 만들고 map () 을 활용해 봅시다.

● 람다식을 활용했을 때와 아닐 때를 비교해 보세요.

 

익명 클래스를 변수에 담아 활용

● Function < T, R> T는 매개변수, R은 반환 타입을 의미 합니다.

public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // ✅ 1. 익명클래스를 변수에 담아 활용
        Function<Integer, Integer> function = new Function<>() {

            @Override
            public Integer apply(Integer integer) {
                return integer * 10;
            }
        };
        
        List<Integer> ret3 = arrayList.stream()
                .map(function)
                .collect(Collectors.toList());
        System.out.println("ret3 = " + ret3);
    }
}

람다식을 변수로 활용

public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // ✅ 2. 람다식을 변수에 담아 활용
        Function<Integer, Integer> functionLambda = (num -> num * 10);
        List<Integer> ret4 = arrayList.stream()
                .map(functionLambda)
                .collect(Collectors.toList());
        System.out.println("ret4 = " + ret4);
    }
}

람다식을 매개변수에 직접 활용
public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // ✅ 3. 람다식을 직접 활용
        List<Integer> ret5 = arrayList.stream()
                .map(num -> num * 10)
                .collect(Collectors.toList());
        System.out.println("ret5 = " + ret5);
    }
}

										전체 코드
                                        
public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // 스트림 없이: 각 요소 * 10 처리
        ArrayList<Integer> ret1 = new ArrayList<>();
        for (Integer num : arrayList) {
            int multipliedNum = num * 10;
            ret1.add(multipliedNum);
        }
        System.out.println("ret1 = " + ret1);

        // 스트림 활용: 각 요소 * 10 처리
        List<Integer> ret2 = arrayList.stream().map(num -> num * 10).collect(Collectors.toList());
        System.out.println("ret2 = " + ret2);

        // 직접 map() 활용해보기
        // 1. 익명클래스를 변수에 담아 전달
        Function<Integer, Integer> function = new Function<>() {

            @Override
            public Integer apply(Integer integer) {
                return integer * 10;
            }
        };
        List<Integer> ret3 = arrayList.stream()
                .map(function)
                .collect(Collectors.toList());
        System.out.println("ret3 = " + ret3);

        // 2. 람다식을 변수에 담아 전달
        Function<Integer, Integer> functionLambda = (num -> num * 10);
        List<Integer> ret4 = arrayList.stream()
                .map(functionLambda)
                .collect(Collectors.toList());
        System.out.println("ret4 = " + ret4);
				
				// 람다식 직접 활용
        List<Integer> ret5 = arrayList.stream()
                .map(num -> num * 10)
                .collect(Collectors.toList());
        System.out.println("ret5 = " + ret5);
    }
}

 

스트림 중간연산 함께 사용하기 ( 예 : filter + map )

리스트에서 짝수만 10배로 변환시키는 예시를 통해 알아 봅시다.

● 다양한 중간 연산을 조립하여 데이터 처리 흐름을 만들 수 있습니다.

● 아래 예시에서는 중간 연산 ( filter() 와 map() ) 을 조합하여 짝수만 10 변환 시킵니다.

● 선언적 코딩으로 코드의 유지 보수성과 가독성이 뛰어 납니다.

- > 명령형 스타일 ( for 문) 과 비교해 보세요.

List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

// ✅ filter() + map() 사용 예제
List<Integer> ret6 = arrayList.stream() // 1. 데이터 준비: 스트림 생성
        .filter(num -> num % 2 == 0)    // 2. 중간 연산: 짝수만 필터링
        .map(num -> num * 10)           // 3. 중간 연산: 10배로 변환
        .collect(Collectors.toList()); // 4. 최종 연산: 리스트로 변환

System.out.println(ret6); // 출력: [20, 40]


// For 문 예시
List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));


List<Integer> ret6 = new ArrayList<>();
for (int num : arrayList) {
    int remain = num % 2;
    if (remain == 0) {
        int data = num * 10;
        ret6.add(data);
    }
}
// 5. 결과 출력
System.out.println(ret6); // 출력: [20, 40]

 

TIL : 람다를 먼저 익혀야 되는 것 같은데 스트림은 람다가 많은 비중을 차지 하는 것 같다. 

 

'JAVA 개념 확장' 카테고리의 다른 글

메모 요점정리 노트  (0) 2025.12.18
쓰레드 ( Thread )  (0) 2025.12.18
람다 (Lambda)  (0) 2025.12.17
제네릭 ( Generic )  (0) 2025.12.15
컬렉션 ( Collection )  (0) 2025.12.15