[Day 14 | Java] Stream API

y♡ding·2024년 10월 31일
0

데브코스 TIL

목록 보기
98/163

java.util.stream (Java SE 17 & JDK 17)
자바 스트림 API는 자바 8부터 도입된 기능으로, 컬렉션이나 배열의 데이터를 선언형 방식으로 처리하고 함수형 프로그래밍 스타일로 다룰 수 있도록 설계되었습니다.

스트림은 데이터 소스를 변경하지 않고 일련의 데이터를 연속적으로 처리할 수 있는 추상화된 데이터 흐름입니다. 스트림 API를 사용하면 데이터를 필터링하고, 매핑하고, 줄이는 작업을 단계별로 지정할 수 있으며, 코드를 간결하게 유지하면서도 읽기 쉽게 만듭니다.

Stream의 특징

  • 지연 연산(lazy evaluation): 중간 연산(예: filter, map)은 최종 연산이 호출될 때까지 실행되지 않음.
  • 데이터 변경 없음: 스트림은 데이터 소스를 변경하지 않고, 필요한 경우 새로운 스트림을 반환.
  • 단일 사용: 스트림은 한 번 사용한 후 재사용할 수 없음.
  • 병렬 처리 지원: 쉽게 병렬 스트림으로 전환할 수 있어 대용량 데이터 처리 시 성능 최적화 가능.

스트림의 생성

스트림은 컬렉션, 배열, 파일 등 다양한 소스에서 생성할 수 있습니다. 예를 들어 stream() 메서드로 컬렉션에서 스트림을 생성하거나, Arrays.stream()으로 배열에서 스트림을 생성합니다. Files.lines()로 파일에서 한 줄씩 읽어 스트림을 만들 수도 있으며, Stream.of()IntStream.range()로 특정 범위의 값을 가진 스트림을 생성할 수 있습니다.

  1. List / Map / Set → 스트림 변경 가능

    • List, Map, Set 등 컬렉션은 stream() 메서드를 통해 스트림으로 변환할 수 있습니다.
    • 예: List<String> list = Arrays.asList("a", "b", "c"); list.stream();
  2. 배열

    • Arrays.stream() 또는 Stream.of() 메서드를 사용하여 배열로부터 스트림을 생성할 수 있습니다.
    • 예: String[] array = {"a", "b", "c"}; Stream<String> stream = Arrays.stream(array);
  3. 문자열

    • chars() 또는 split()을 사용하여 문자열을 스트림으로 변환할 수 있습니다.
    • 예: "hello".chars().mapToObj(c -> (char) c); 또는 "hello world".split(" ");
  4. 특정 범위 정수

    • IntStream.range()IntStream.rangeClosed() 메서드를 사용하여 특정 범위의 정수 스트림을 생성할 수 있습니다.
    • 예: IntStream.range(1, 5); // 1, 2, 3, 4
  5. 난수

    • Random 클래스의 ints(), doubles(), longs() 메서드를 통해 무작위 스트림을 생성할 수 있습니다.
    • 예: new Random().ints(5); // 정수 난수 스트림 5개
  6. 람다

    • Stream.generate() 또는 Stream.iterate() 메서드를 사용하여 람다 표현식 기반 스트림을 생성할 수 있습니다.
    • 예: Stream.generate(() -> "element").limit(5); // 같은 문자열 "element"가 5번
  7. 빈 스트림

    • Stream.empty() 메서드를 통해 빈 스트림을 생성할 수 있습니다.
    • 예: Stream<Object> emptyStream = Stream.empty();
// List를 Stream으로 변환
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> nameStream = names.stream(); // List에서 Stream 생성

// 배열을 Stream으로 변환
String[] nameArray = {"Alice", "Bob", "Charlie"};
Stream<String> nameArrayStream = Arrays.stream(nameArray); // 배열에서 Stream 생성

// 파일을 읽어 Stream으로 변환
Stream<String> lines = Files.lines(Paths.get("file.txt")); // 파일의 각 줄을 스트림으로 처리

// 직접 값을 전달하여 Stream 생성
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5); // 정수 스트림 생성

스트림 연산

스트림 연산은 중간 연산최종 연산으로 나눌 수 있습니다. 중간 연산은 새로운 스트림을 반환하여 연속적인 처리가 가능하고, 최종 연산은 스트림을 소비하여 결과를 반환합니다.

  • 중간 연산: filter(조건에 맞는 요소 선택), map(각 요소를 변환), sorted(요소 정렬).
// 중간 연산 (Intermediate Operations)

// filter: 조건에 맞는 요소만 남기는 연산 (이름이 "A"로 시작하는 요소)
names.stream()
     .filter(name -> name.startsWith("A"))
     .forEach(System.out::println); // "Alice", "Anna" 출력

// map: 각 요소를 변환하는 연산 (각 이름을 문자열의 길이로 변환)
names.stream()
     .map(String::length)
     .forEach(System.out::println); // 각 이름의 길이 출력 (5, 3, 7, 4)

// sorted: 요소를 정렬하는 연산
names.stream()
     .sorted()
     .forEach(System.out::println); // 알파벳 순서대로 출력 (Alice, Anna, Bob, Charlie)
  • 최종 연산: forEach(각 요소를 소비), collect(컬렉션으로 변환), reduce(모든 요소를 결합하여 하나의 결과 반환), count(요소 개수 반환).
// 최종 연산 (Terminal Operations)

// forEach: 각 요소를 소비하는 연산 (모든 이름 출력)
names.stream()
     .forEach(System.out::println);

// collect: 스트림의 결과를 컬렉션으로 변환하는 연산 (이름이 "A"로 시작하는 요소를 리스트로 수집)
List<String> result = names.stream()
                           .filter(name -> name.startsWith("A"))
                           .collect(Collectors.toList());
System.out.println(result); // [Alice, Anna] 출력

// reduce: 모든 요소를 결합하여 하나의 결과를 반환 (숫자의 합 계산 예시)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
                 .reduce(0, Integer::sum); // 초기값 0에서 시작하여 모든 요소 합산
System.out.println(sum); // 15 출력

// count: 요소의 개수를 반환 (이름이 "A"로 시작하는 요소의 개수)
long count = names.stream()
                  .filter(name -> name.startsWith("A"))
                  .count();
System.out.println(count); // 2 출력
연산 유형메서드 예시설명
중간 연산filter, map, sorted요소 변환 및 필터링, 중간 스트림 반환
최종 연산forEach, collect, reduce, count스트림을 소비하여 결과 반환
병렬 처리 지원parallelStream스트림을 병렬 처리로 전환하여 멀티코어 활용
  • Code Snippet
// 이름이 "A"로 시작하는 요소만 필터링, 정렬 후 출력
names.stream()
     .filter(name -> name.startsWith("A")) // "A"로 시작하는 이름 필터링
     .sorted()                             // 알파벳 순서대로 정렬
     .forEach(System.out::println);         // 필터링된 이름을 출력
// 숫자 리스트를 Stream으로 처리하여 제곱의 합 계산
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sumOfSquares = numbers.stream()
                          .map(n -> n * n)              // 각 숫자를 제곱
                          .reduce(0, Integer::sum);     // 제곱한 값을 모두 더함
System.out.println(sumOfSquares); // 제곱의 합 출력

수업코드

배열을 스트림화

import java.util.Arrays;
import java.util.stream.Stream;

public class StreamEx01 {
    public static void main(String[] args) {
    	// 데이ㅓ 생성 
        String[] nameArr = { "IronMan", "Captain", "SpiderMan", "Thor" };

        // 데이터 처리 - 배열을 정렬
        Arrays.sort(nameArr);

        // 데이터 출력 - 정렬된 배열을 반복문으로 출력
        for (String name : nameArr) {
            System.out.println(name);
        }

        // Stream 생성
        Stream<String> nameStream = Arrays.stream(nameArr);

        // Stream 처리 - 정렬된 데이터를 출력
        nameStream.sorted().forEach(System.out::println);
    }

}

생성

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class StreamEx02 {
    public static void main(String[] args) {
        // 스트림 생성 예시

        // 1. List를 Stream으로 변환
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        Stream<Integer> intStream = list.stream();

        // forEach를 사용하여 스트림의 요소를 출력
        intStream.forEach(System.out::println);

        // 2. Stream.of() 메서드를 사용하여 스트림 생성 - 문자열 스트림 생성
        Stream<String> strStream = Stream.of("a", "b", "c");
        strStream.forEach(System.out::println); // 스트림의 문자열 요소를 출력

        // 3. 빈 스트림 생성
        Stream<Integer> emptyStream = Stream.empty(); // 빈 스트림 생성
        System.out.println("출력"); // 빈 스트림에는 출력할 요소가 없음
    }
}

필터링 (filter)


import java.util.stream.IntStream;

public class StreamEx03 {
    public static void main(String[] args) {
        // 1 ~ 10까지의 숫자를 포함하는 IntStream 생성
        IntStream test1 = IntStream.rangeClosed(1, 10);
        // test1.forEach(System.out::println); // 스트림의 모든 숫자 출력

        // 짝수만 필터링하여 출력
        // test1.filter(i -> i % 2 == 0).forEach(System.out::println);

        // 여러 개의 숫자로 이루어진 IntStream 생성
        IntStream test2 = IntStream.of(1, 2, 3, 3, 2, 5, 7, 6, 9);
        
        // 중복 제거 후 각 요소 출력
        test2.distinct().forEach(System.out::println); // 중복을 제거한 각 숫자 출력
    }
}

정렬 (sort)

public class StreamEx04 {
    public static void main(String[] args) {
        // 문자열 스트림 생성
        Stream<String> strStream = Stream.of("b", "cc", "D", "C", "AA", "F");

        // 오름차순 정렬
        // strStream.sorted().forEach(System.out::println);

        // 오름차순 정렬 - compareTo 메서드 사용
        // strStream.sorted((s1, s2) -> s1.compareTo(s2)).forEach(System.out::println);

        // 내림차순 정렬 - compareTo 메서드를 반대로 사용
        // strStream.sorted((s1, s2) -> s2.compareTo(s1)).forEach(System.out::println);

        // 내림차순 정렬 - Comparator.reverseOrder() 사용
        // strStream.sorted(Comparator.reverseOrder()).forEach(System.out::println);

        // 대소문자 구분 없이 오름차순 정렬
        strStream.sorted(String.CASE_INSENSITIVE_ORDER)
                .forEach(System.out::println); // 대소문자 무시하고 정렬 후 출력
    }
}

변환 (map)

import java.io.File;
import java.util.stream.Stream;

public class StreamEx05 {
    public static void main(String[] args) {
        // File 객체 배열 생성
        File file1 = new File("ext1.java");
        File file2 = new File("ext1.bak");
        File file3 = new File("ext2.java");
        File file4 = new File("ext3");
        File file5 = new File("ext1.txt");

        // File 객체들을 배열로 생성
        File[] fileArr = {file1, file2, file3, file4, file5};

        // 배열을 Stream으로 변환
        Stream<File> fileStream = Stream.of(fileArr);

        fileStream
                .map(File::getName)                  // File 객체에서 파일명 추출
                .filter(name -> name.endsWith(".java")) // 확장자가 ".java"인 파일만 필터링
                .forEach(System.out::println);       // 필터링된 결과를 출력

        System.out.println("===========");
        fileStream = Stream.of(fileArr);

        fileStream.map(File::getName)               // 파일명 추출
                .filter(s -> s.indexOf('.') != -1) // 확장자가 없는 파일 제외 ('.'이 있는 파일만 필터링)
                .map(s -> s.substring(s.indexOf('.') + 1)) // 확장자 부분만 추출
                .distinct()                       // 중복 제거
                .forEach(System.out::println);    // 결과 출력
    }
}

peek() 을 통한 조회

        // 확장자를 추출 (확장자가 없는 것은 제외, 중복 제거)
        fileStream.map(File::getName)
                .filter(s -> s.indexOf(".") != -1)   
                .peek( s -> System.out.println("peek : " + s ))
                .map(s -> s.substring(s.indexOf('.') + 1)) 
                .peek( s -> System.out.println("peek : " + s ))
                .distinct()
                .forEach(System.out::println);
    }

0개의 댓글

관련 채용 정보