자바/++

Stream ( with claude )

backend dev 2025. 9. 30.

Stream은 컬렉션, 배열 등의 데이터를 함수형으로 처리하는 API입니다 (Java 8+)

 

Stream 생성 방법

// 컬렉션에서
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();

// 배열에서
String[] arr = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);

// 직접 생성
Stream<String> stream = Stream.of("a", "b", "c");

// 범위 생성
IntStream range = IntStream.range(1, 5);      // 1,2,3,4
IntStream rangeClosed = IntStream.rangeClosed(1, 5); // 1,2,3,4,5

 

배열에서 Stream 생성

// Primitive 타입
int[] intArray = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(intArray);

long[] longArray = {1L, 2L, 3L};
LongStream longStream = Arrays.stream(longArray);

double[] doubleArray = {1.0, 2.0, 3.0};
DoubleStream doubleStream = Arrays.stream(doubleArray);

// Wrapper 타입
Integer[] integerArray = {1, 2, 3, 4, 5};
Stream<Integer> stream = Arrays.stream(integerArray);

String[] strArray = {"a", "b", "c"};
Stream<String> strStream = Arrays.stream(strArray);

 

컬렉션에서 Stream 생성

// List
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> listStream = list.stream();

// Set
Set<String> set = new HashSet<>(Arrays.asList("a", "b", "c"));
Stream<String> setStream = set.stream();

// Map (Entry로 변환)
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
map.put("two", 2);
Stream<Map.Entry<String, Integer>> mapStream = map.entrySet().stream();

 

범위로 Stream 생성

// 1부터 10까지 (10 포함)
IntStream range1 = IntStream.rangeClosed(1, 10);

// 1부터 10 미만 (10 미포함)
IntStream range2 = IntStream.range(1, 10);

// LongStream도 동일
LongStream longRange = LongStream.rangeClosed(1L, 100L);

 

기타 생성 방법

// 빈 Stream
Stream<String> emptyStream = Stream.empty();

// 값으로 직접 생성
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);

// 무한 Stream (limit과 함께 사용)
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2)
                                       .limit(10); // 0, 2, 4, 6...

 

중간 연산 (Intermediate Operations)

 

필터링

// filter - 조건에 맞는 요소만 선택
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
list.stream()
    .filter(n -> n % 2 == 0)  // 짝수만
    .forEach(System.out::println); // 2, 4, 6

// distinct - 중복 제거
Arrays.asList(1, 2, 2, 3, 3, 4)
      .stream()
      .distinct()
      .forEach(System.out::println); // 1, 2, 3, 4

 

변환

// map - 요소를 다른 형태로 변환
List<String> words = Arrays.asList("hello", "world");
words.stream()
     .map(String::length)  // 각 문자열의 길이로 변환
     .forEach(System.out::println); // 5, 5

// mapToInt, mapToLong, mapToDouble - Primitive Stream으로 변환
List<String> strings = Arrays.asList("1", "2", "3");
int sum = strings.stream()
                 .mapToInt(Integer::parseInt)
                 .sum();

// flatMap - 중첩 구조를 평탄화
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);
nested.stream()
      .flatMap(Collection::stream)
      .forEach(System.out::println); // 1, 2, 3, 4, 5, 6

 

정렬

// sorted - 기본 정렬 (오름차순)
Arrays.asList(3, 1, 4, 1, 5)
      .stream()
      .sorted()
      .forEach(System.out::println); // 1, 1, 3, 4, 5

// Comparator 사용
Arrays.asList(3, 1, 4, 1, 5)
      .stream()
      .sorted(Comparator.reverseOrder()) // 내림차순
      .forEach(System.out::println);

// 객체 정렬
class Person {
    String name;
    int age;
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 20)
);

people.stream()
      .sorted(Comparator.comparing(Person::getAge)) // 나이순 정렬
      .forEach(p -> System.out.println(p.name));

 

제한

// limit - 개수 제한
IntStream.range(1, 100)
         .limit(5)  // 처음 5개만
         .forEach(System.out::println); // 1, 2, 3, 4, 5

// skip - 앞에서 n개 건너뛰기
IntStream.range(1, 10)
         .skip(3)  // 처음 3개 건너뛰기
         .forEach(System.out::println); // 4, 5, 6, 7, 8, 9

 

확인

// peek - 중간에 각 요소를 확인 (디버깅용)
IntStream.range(1, 5)
         .peek(n -> System.out.println("before: " + n))
         .map(n -> n * 2)
         .peek(n -> System.out.println("after: " + n))
         .sum();

 

최종 연산 (Terminal Operations)

수집

// toList - List로 수집
List<Integer> list = IntStream.range(1, 6)
                              .boxed()  // Integer로 boxing
                              .collect(Collectors.toList());

// toSet - Set으로 수집
Set<Integer> set = Arrays.asList(1, 2, 2, 3)
                         .stream()
                         .collect(Collectors.toSet());

// toArray - 배열로 수집
int[] intArray = IntStream.range(1, 6).toArray();
Integer[] integerArray = IntStream.range(1, 6)
                                  .boxed()
                                  .toArray(Integer[]::new);

// toMap - Map으로 수집
Map<String, Integer> map = Arrays.asList("a", "bb", "ccc")
    .stream()
    .collect(Collectors.toMap(
        s -> s,              // key
        String::length       // value
    ));

// groupingBy - 그룹화
Map<Integer, List<String>> grouped = 
    Arrays.asList("a", "bb", "ccc", "dd")
          .stream()
          .collect(Collectors.groupingBy(String::length));
// {1=[a], 2=[bb, dd], 3=[ccc]}

 

집계

// count
long count = IntStream.range(1, 10).count(); // 9

// sum (Primitive Stream만 가능)
int sum = IntStream.of(1, 2, 3, 4, 5).sum(); // 15

// average (Primitive Stream만 가능)
double avg = IntStream.of(1, 2, 3, 4, 5)
                      .average()
                      .orElse(0.0); // 3.0

// max, min
int max = IntStream.of(1, 5, 3, 2, 4)
                   .max()
                   .orElse(0); // 5

int min = IntStream.of(1, 5, 3, 2, 4)
                   .min()
                   .orElse(0); // 1

// Wrapper 타입의 집계
Integer maxValue = Arrays.asList(1, 5, 3, 2, 4)
    .stream()
    .max(Integer::compareTo)
    .orElse(null);

// reduce - 사용자 정의 집계
int product = IntStream.of(1, 2, 3, 4, 5)
                       .reduce(1, (a, b) -> a * b); // 120

// summaryStatistics - 한 번에 여러 통계 정보
IntSummaryStatistics stats = IntStream.range(1, 11)
                                      .summaryStatistics();
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());

 

매칭

// anyMatch - 하나라도 조건을 만족하는지
boolean hasEven = IntStream.of(1, 3, 5, 6, 7)
                           .anyMatch(n -> n % 2 == 0); // true

// allMatch - 모두 조건을 만족하는지
boolean allPositive = IntStream.of(1, 2, 3, 4, 5)
                               .allMatch(n -> n > 0); // true

// noneMatch - 모두 조건을 만족하지 않는지
boolean noNegative = IntStream.of(1, 2, 3, 4, 5)
                              .noneMatch(n -> n < 0); // true

 

검색

// findFirst - 첫 번째 요소
OptionalInt first = IntStream.of(1, 2, 3, 4, 5)
                             .findFirst(); // 1

// findAny - 아무 요소나 (병렬 처리시 유용)
OptionalInt any = IntStream.of(1, 2, 3, 4, 5)
                           .findAny();

 

반복

// forEach - 각 요소에 대해 작업 수행
IntStream.range(1, 5)
         .forEach(System.out::println);

// forEachOrdered - 순서를 보장하며 반복 (병렬 Stream에서도)
IntStream.range(1, 5)
         .parallel()
         .forEachOrdered(System.out::println);

 

자료구조별 활용법

배열 (Array)

// 배열 정렬
int[] arr = {3, 1, 4, 1, 5};
arr = Arrays.stream(arr)
            .sorted()
            .toArray();

// 배열 필터링
int[] filtered = Arrays.stream(arr)
                       .filter(n -> n > 2)
                       .toArray();

// 배열 변환
int[] doubled = Arrays.stream(arr)
                      .map(n -> n * 2)
                      .toArray();

// 2차원 배열 평탄화
int[][] matrix = {{1, 2}, {3, 4}, {5, 6}};
int[] flattened = Arrays.stream(matrix)
                        .flatMapToInt(Arrays::stream)
                        .toArray();

// 배열 통계
int sum = Arrays.stream(arr).sum();
double avg = Arrays.stream(arr).average().orElse(0.0);
int max = Arrays.stream(arr).max().orElse(0);
 

리스트 (List)

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

// 리스트 필터링
List<Integer> evens = list.stream()
                          .filter(n -> n % 2 == 0)
                          .collect(Collectors.toList());

// 리스트 변환
List<String> strings = list.stream()
                           .map(String::valueOf)
                           .collect(Collectors.toList());

// 리스트 정렬 (원본 유지)
List<Integer> sorted = list.stream()
                           .sorted()
                           .collect(Collectors.toList());

// 특정 조건으로 분리
Map<Boolean, List<Integer>> partitioned = 
    list.stream()
        .collect(Collectors.partitioningBy(n -> n % 2 == 0));
// {false=[1, 3, 5], true=[2, 4]}

// 중복 제거
List<Integer> unique = Arrays.asList(1, 2, 2, 3, 3, 4)
                             .stream()
                             .distinct()
                             .collect(Collectors.toList());

 

셋 (Set)

Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));

// Set을 List로
List<Integer> list = set.stream()
                        .collect(Collectors.toList());

// Set 필터링
Set<Integer> filtered = set.stream()
                           .filter(n -> n > 3)
                           .collect(Collectors.toSet());

// 두 Set의 교집합
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Arrays.asList(2, 3, 4));
Set<Integer> intersection = set1.stream()
                                .filter(set2::contains)
                                .collect(Collectors.toSet());

// 두 Set의 합집합
Set<Integer> union = Stream.concat(set1.stream(), set2.stream())
                           .collect(Collectors.toSet());

 

맵 (Map)

Map<String, Integer> map = new HashMap<>();
map.put("apple", 100);
map.put("banana", 200);
map.put("cherry", 150);

// Map의 value로 필터링
Map<String, Integer> expensive = map.entrySet()
    .stream()
    .filter(e -> e.getValue() > 100)
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue
    ));

// Map의 key를 List로
List<String> keys = map.keySet()
                       .stream()
                       .collect(Collectors.toList());

// Map의 value를 List로
List<Integer> values = map.values()
                          .stream()
                          .collect(Collectors.toList());


// List를 Map으로
List<String> words = Arrays.asList("a", "bb", "ccc");
Map<String, Integer> wordLengths = words.stream()
    .collect(Collectors.toMap(
        w -> w,
        String::length
    ));
    
    
// value 기준 오름차순 정렬
List<Map.Entry<String, Integer>> list = map.entrySet().stream()
    .sorted(Map.Entry.comparingByValue())
    .collect(Collectors.toList());
    
// value 기준 내림차순 정렬
List<Map.Entry<String, Integer>> list = map.entrySet().stream()
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
	.collect(Collectors.toList());
    
    
// key 기준 오름차순 정렬
List<Map.Entry<String, Integer>> list = map.entrySet().stream()
    .sorted(Map.Entry.comparingByKey())
    .collect(Collectors.toList());
    
// key 기준 내림차순 정렬
List<Map.Entry<String, Integer>> list = map.entrySet().stream()
    .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
	.collect(Collectors.toList());

 

댓글