스트림(Stream)

귀찮Lee·2022년 5월 22일
1

Java

목록 보기
11/15
post-custom-banner

◎ 스트림(Stream)

  • 배열, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자
  • 장점
    • List, Set, Map, 배열 등 다양한 데이터 소스로부터 스트림을 만들 수 있고, 이를 표준화된 방법으로 다룰 수 있다.
    • 데이터 소스를 다루는 풍부한 메서드를 제공,
    • 다량의 데이터에 복잡한 연산을 수행하면서도, 가독성과 재사용성이 높은 코드를 작성 가능

◎ 스트림의 특징

  • 선언형으로 데이터 소스를 처리

    • 선언형 프로그래밍 : “어떻게" 수행하는지보다는 “무엇을” 수행하는지에 관심을 두는 프로그래밍 패러다임
    • 선언형 방식으로 작성하여 내부 동작의 원리르 모르더라도 코드가 무슨 일을 하는지 이해할 수 있다.
    • "어떻게"의 영역이 추상화 되어있다.
  • 람다식으로 요소 처리 코드를 제공

    • 람다식 또는 메서드 참조를 이용해서 요소 처리 내용을 매개값으로 전달할 수 있다.
  • 내부 반복자를 사용하므로 병렬 처리가 쉽다.

    • 외부반복자(external iterator) : 개발자가 코드로 직접 컬렉션의 요소를 반복해서 가져오는 코드 패턴
      ex) index를 사용하는 for문, Iterator를 이용하는 while문

    • 내부반복자 : 컬렉션 내부에서 요소들을 반복시키고 개발자는 요소당 처리해야할 코드만 제공하는 코드 패턴

    • 내부반복자 장점
      • 컬렉션 내부에서 어떻게 요소를 반복시킬 것인가는 컬렉션에게 맡겨두고, 개발자는 요소 처리 코드에만 집중할 수 있다
      • 요소들의 반복 순서를 변경하거나 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있게 도와주기 때문에 하나씩 처리하는 순차적 외부 반복자보다 효율적으로 요소를 반복시킬 수 있습니다.
  • 중간 연산과 최종 연산

    • 중간 연산에서는 매핑, 필터링, 정렬을 수행하고 최종 연산에서는 반복, 카운팅, 평균, 총합 등의 집계
    • 데이터를 원하는대로 변형해서 원하는 값을 구해낼 수 있다.
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);

int sum = numbers.stream()
                 .filter(number -> number > 4 && (number % 2 == 0)) // 람다식으로 요소 처리 코드를 제공
                 .mapToInt(number -> number) // 중간 연산 스트림 중 하나
                 .sum(); // 최종연산

System.out.println("# 선언형 프로그래밍: " + sum);

◎ 파이프라인

  • 파이프라인(pipelines) : 여러개의 스트림이 연결되어 있는 구조

    • 중간 연산을 거치면서 데이터릉 원하는대로 변형
  • 중간 스트림이 생성될 때 요소들이 바로 중간 연산(필터링, 매핑, 정렬)되는 것이 아니라 최종 연산이 시작되기 전까지는 지연

  • 중간 연산

    • 최종 연산을 제외한 연산
    • 스트림의 메서드로 사용되어 return 값으로 다른 스트림을 내놓음
  • 최종 연산

    • 스트림을 받아 결과물을 연산함
    List<String> people;
    // 데이터를 넣음
    
    people.stream()
      .distinct() // 중간 연산 (Stream<String>)
      .filter(s -> s.startsWith(lastName)) // 중간 연산 (Stream<String>)
      .sorted() // 중간 연산 (Stream<String>)
      .collect(Collectors.toList()); // List<String>

◎ 스트림 생성, 연산

  • 생성하는 법
 // array로 stream을 만드는 법
List<String> list = Arrays.asList("a", "b", "c");
// list로 stream을 만드는 법 : Collection 인터페이스에 메서드가 정의되어 있음
Stream<String> listStream = list.stream(); 

listStream.forEach(System.out::prinln); //스트림의 모든 요소를 출력.

// array로부터 스트림을 생성 (여러가지)
Stream<String> stream = Stream.of("a", "b", "c"); //가변인자
Stream<String> stream = Stream.of(new String[] {"a", "b", "c"});
Stream<String> stream = Arrays.stream(new String[] {"a", "b", "c"});
Stream<String> stream = Arrays.stream(new String[] {"a", "b", "c"}, 0, 3); //end 범위 미포함

// 원시형 자료형을 위한 특수한 종류의 Stream
// IntStream, LongStream, DoubleStream (각각 메서드를 찾아봐서 사용)

// 4이상 10미만의 숫자를 갖는 IntStream
IntStream stream = IntStream.range(4, 10);
  • 중간 연산, 최종 연산 메서드
    • 참고1 : Stream<>의 메서드
    • 참고2 : Option의 메서드
    • 참고3 : IntStream의 메서드
    • 이외 메서드는 직접 구글링해서 찾자

◎ 컬렉션과 스트림 비교

  • Eager : 즉시 작업을 수행
  • Lazy : 필요에 따라 데이터가 지연 (최종 연산이 시작되기 전까지 지연)
  • Short-circuit : 단락 평가

◎ Optional< T >

  • NullPointerException(NPE), 즉 null 값으로 인해 에러가 발생하는 현상을 객체 차원에서 효율적으로 방지하고자 도입

  • 연산 결과를 Optional에 담아서 반환하면, 따로 조건문을 작성해주지 않아도 NPE가 발생하지 않도록 코드를 작성할 수 있다.

  • 특징

    • Optional 클래스는 모든 타입의 객체를 담을 수 있는 래퍼(Wrapper) 클래스
    public final class Optional<T> {
        private final T value; // T타입의 참조변수
    }
    • 객체를 생성하려면 of() 또는 ofNullable()을 사용 (null 인 가능성이 있을 때, ofNullable() 사용)
    Optional<String> opt1 = Optional.ofNullable(null);
    Optional<String> opt2 = Optional.ofNullable("123");
    System.out.println(opt1.isPresent()); //Optional 객체의 값이 null인지 여부를 리턴합니다.
    System.out.println(opt2.isPresent());
    • Optional 타입의 참조변수를 기본값으로 초기화하려면 empty() 메서드를 사용
    Optional<String> opt3 = Optional.<String>empty();
    • Optional 객체에 객체에 저장된 값을 가져오려면 get()을 사용합니다. 참조변수의 값이 null일 가능성이 있다면 orElse()메서드를 사용해 디폴트 값을 지정할 수 있다.
    Optional<String> optString = Optional.of("codestates");
    System.out.println(optString);
    System.out.println(optString.get());
    
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElse("kimcoding");
    System.out.println(name);
    • 메서드 체이닝 : Optional 객체는 스트림과 유사하게 여러 메서드를 연결해서 작성할 수 있다.
    List<String> languages = Arrays.asList(
                  "Ruby", "Python", "Java", "Go", "Kotlin");
     Optional<List<String>> listOptional = Optional.of(languages);
    
     int size = listOptional
                .map(List::size)
                .orElse(0);
     System.out.println(size);
profile
배운 것은 기록하자! / 오류 지적은 언제나 환영!
post-custom-banner

0개의 댓글