Java Stream

이건희·2023년 3월 30일
2

Stream

자바를 공부하면서 스트림(Stream)이란 단어는 많이 들어봤다. 하지만 들어보기만 했지 스트림이 어떤건지, 어떻게 사용하는건지 잘 몰라 한번 알아보았다.

Stream이란?

여러 자료의 처리에 대한 기능을 구현해 놓은 클래스 !

  • 자료에 따라 기능을 각각 새로 구현하는 것이 아니라 처리해야 하는 자료가 무엇인지와 상관없이 같은 방식으로 메소드를 호출한다 !

한번 예시를 들어보자.

int[] arr = {1,2,3,4,5};
for(int i = 0; i < arr.length(); i++){
   System.out.println(arr[i]);    
}

위와 같은 코드가 있다고 가정할 때, 스트림을 사용하면 다음과 같이 편리하게 사용 할 수 있다.

int[] arr = {1,2,3,4,5};>Arrays.stream(arr).forEach(i -> System.out.println(i));

이렇게 코드가 간결해진다. 위 예시는 단순한 예제라 그렇지 코드가 매우 복잡해지면 더욱 더 간결해진다.


Stream에는 어떤 연산 기능이 구현되어 있을까?

Stream 연산

  • Stream 연산의 종류에는 크게 중간 연산최종 연산이 있다.
  • 중간 연산은 자료를 거르거나 변경하여 또 다른 자료를 내부적으로 생성한다.
  • 최종 연산은 생성된 내부 자료를 소모해가면서 연산을 수행한다.

여기서 중요한 개념이 있다.

  • 최종 연산은 마지막에 한번만 호출된다 !

  • 최종 연산이 호출 되어야 중간 연산의 결과가 만들어진다 !

    • 즉, 중간 연산이 여러개 호출되더라도 최종 연산이 호출되어야 스트림의 중간 연산이 모두 적용된다.
    • 중간 연산으로 자료를 정렬하고 검색해도 최종 연산이 호출되지 않으면 결과를 가져올 수 없다.
    • 이를 지연 연산(lazy evalutation)이라 한다.

중간연산

  • 대표적으로 filter(), map()이 있다.

    filter()

    filter()는 조건을 넣고 조건에 맞는 참인 경우만 추출하는 함수이다.
    예시로 문자열의 길이가 5 이상인 경우만 출력하는 코드는 다음과 같다.

    sList.stream().filter(s -> s.length() >= 5).forEach(s -> System.out.println(s));

    위 코드를 분석해보자.

    filter(s -> s.length() >= 5) 
    //중간 연산이다. lambda식으로 sList에 있는 각각의 원소가 매개변수인 s에 대입되고, 이를 조건식에 부합하는 원소만 반환한다.
    forEach(s -> System.out.println(s));
    //최종 연산이다. lambda식으로 중간 연산에서 조건에 부합한 원소들만 출력한다.

    map()

    map()은 요소를 다른 요소로 반환하는데 사용한다. 다른 타입으로 변환할 수도 있고 다른 값으로 반환할수도 있다.
    아래는 클래스에서 이름을 가져오는 예시이다.

    customerList.stream().map(c -> c.getName()).forEach(s -> System.out.printlnt(s));
    //customerList c에서 String 타입인 이름을 가져와 출력한다.

최종 연산

  • 대표적으로 forEach(), count(), sum(), reduce()가 있다.
  • 최종 연산은 자료를 소모하면서 연산을 수행하기 때문에, 최종 연산이 수행되고 나면 해당 스트림은 더 이상 사용할 수 없다.
    int sum = Arrays.stream(arr).sum(); //합계를 반환한다.
    int count = (int)Arrays.stream(arr).count();//개수를 반환한다(return type이 long이므로 int로 형변환)

    이외에도 count(), sum(), max(), min(), average() 등이 있다.


Collection에서 Stream 사용하기

Collection 인터페이스를 구현한 클래스(ex. ArrayList)에서도 Stream을 사용할 수 있는데, Collection 인터페이스 메소드를 살펴보면 다음과 같은 메소드가 있다.

  • Stream<E> stream() : 스트림 클래스를 반환

쉽게 예시를 보며 이해해보자.

Stream<String> stream = sList.stream(); //제네릭을 이용하여 자료형 명시, 스트림 새로 생성
stream.forEach(s -> System.out.println(s)); //최종 연산인 forEach() 사용하여 출력
stream.sorted().forEach(s ->System.out.println(s)); //중간 연산인 sorted()에 의해 정렬되고, 이를 최종 연산인 forEach()로 출력

이렇게 생성된 스트림은 ArrayList의 모든 요소를 가지고 있다. forEach 메소드가 수행되면 요소가 하나씩 차례로 변수 s에 대입되고 이를 매개변수로 받아 출력문이 호출된다.


스트림의 특징

1. 자료의 대상과 관계없이 동일한 연산을 수행한다.

  • 스트림은 여러 자료 구조에 대해 다양한 작업들을 일관성 있게 처리할 수 있는 메소드를 제공한다.

2. 한번 생성하고 사용한 스트림은 재사용 할 수 없다.

  • 어떤 자료에 대한 스트림을 생성하고 메서드를 호출하여 사용하였다면 다시 다른 연산에 사용할 수 없다.
  • 최종 연산에 대해 요소들이 '소모'된다고 하는데 소모된 요소는 재사용 할 수 없다.
  • 따라서 다른 기능을 호출하려면 스트림을 새로 생성해야 된다.

3. 스트림의 연산은 기존 자료를 변경하지 않는다.

  • 스트림 연산을 위해 사용하는 메모리 공간이 별도로 존재하므로 기존 자료의 영향을 끼치지 않는다.

4. 스트림의 연산은 중간 연산과 최종 연산이 있다.

  • 스트림의 중간 연산은 여러개가 적용 될 수 있고, 최종 연산은 마지막에 한번만 적용된다.
  • 중간 연산이 여러개 호출되었더라도 최종 연산이 호출되어야 중간 연산이 모두 적용된다.
  • 이를 지연 연산(lazy evaluation) 이라 한다.

출처: Do it 자바 프로그래밍 정리

profile
광운대학교 정보융합학부 학생입니다.

0개의 댓글