ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 람다와 스트림 - 스트림 만들기
    백엔드/자바 2021. 11. 9. 21:49

    2021.09.15 - [백엔드/자바] - 람다와 스트림 - 스트림의 정의와 특징

     

    람다와 스트림 - 스트림의 정의와 특징

    스트림의 정의와 그 필요성 한마디로 말하면 컬렉션이나 배열을 다루기 쉽게 만든 것이다. 기존의 컬렉션 프레임웍을 보면 위와 같이 List, Set, Map의 인터페이스로 구성된 것을 알 수 있다. 이 중

    sgcomputer.tistory.com

     

    이전 파트에서는 스트림의 정의와 특징에 대해서 알아봤다.

     

    간단히 복습해보자면 스트림이란 컬렉션의 데이터를 쉽게 다루기 위해 만들어진 인터페이스다.

     

    List든 Set이든 Map이든 형태에 구애받지 않고 데이터만 뽑아서 스트림 속에 넣고 가공할 수있다.

     

    그렇다보니 스트림은 원본 데이터에 영향을 주지 않고 데이터만 손쉽게 뽑아 가공이 가능하다.

     

    물론 그렇게 하기 위해서는 스트림을 먼저 만들어봐야 할것이다.

     

    컬렉션의 Stream만들기 - Stream( )

    앞서 말해듯 스트림이 존재하는 가장 큰 목적을 컬렉션의 데이터를 다루기 위함이다.

     

    그리고 컬렉션을 스트림으로 만들 때 쓰이는 가장 기본적인 메서드가 바로 Stream()이다.

     

     

     

    사용 방법은 아주 간단하다. 위 코드와 같이 참조변수를 통해 stream() 메서드를 실행해주면 된다.

     

    이것만으로도 stream이 생성된다. 그런데 여기서 이상한 점 하나를 찾을 수 있을 것이다.

     

    바로 Map의 스트림 생성이다. 보시다시피 Map의 경우 메서드의 글자 색이 빨간 것을 알 수 있다. 왜그럴까?

     

     

     

    이전 컬렉션 파트에서 설명했듯 Map의 경우 자료 구조가 List, Set와는 조금 다르다.

     

    List와 Set이 나열구조라면 Map의 경우 키(key)와 값(value)로 나눠진 형태다.

     

    그렇다보니 List, Set처럼 스트림을 바로 생성해줄 수 없는 것이다.

     

     

     

    그렇다면 Map을 스트림으로 만들어주려면 어떻게해야할까? 방법은 어렵지 않다.

     

    위의 코드처럼 Map의 Set의 형태로 만들어준다음 Stream을 생성하면 된다.

     

    Map의 참조변수에서 entrySet(), keySet(), values() 메서드를 사용 후 체이닝으로 stream()을 실행하면 된다.

     

    배열의 Stream 만들기 - Stream.of( ), Arrays.stream( )

    이전 단락에선 컬렉션(List, Set, Map)의 데이터를 스트림으로 만드는 방법을 공부했다

     

    하지만 데이터를 다루는건 컬렉션만 있는게 아니다.

     

    배열 또한 데이터를 다루는 방식으로 당연히 스트림 생성이 가능하다.

     

     

     

    배열의 스트림 생성 또한 컬렉션과 마찬가지로 어렵지 않다.

     

    위와 같이 Stream 인터페이스의 of() 메서드 나 Arrays 클래스의 stream() 메서드를 호출하면 된다.

     

     

     

    그런데 여기서 이상한 점을 하나 발견했을 것이다.

     

    stream()를 사용할 때 문자열 배열과 숫자 배열을 매개변수로 제공할 때 참조변수 타입이 달라지는 것이다.

     

    게다가 조금 더 나가면 of()와 stream()의 기능이 같다면 왜 굳이 두개로 나눠놨는지 의문이 들 것이다.

     

    이 중에서 우선 첫번째 의문인 Arrays.stream()에 대해서 이야기해보자.

     

    원시 타입 자료의 스트림 만들기 - Arrays.stream()

     

    stream()을 이용해서 배열을 스트림으로 만들 때 이상한 점 한 가지를 발견했을 것이다.

     

    바로 문자열(String) 배열과 숫자 배열을 매개변수로 줄 때 반환되는 타입이 다른 것이다.

     

     

     

    실제로 자바 api 문서를 확인해봐도 둘의 반환 타입이 다른 것을 알 수 있다.

     

    이러한 사실로 미뤄보아 Arrays.stream()의 경우 주어지는 인수의 타입에 따라 반환 타입도 달라짐을 알 수 있다.

     

     

     

    그 이외에도 다양한 타입의 배열을 넣을 때마다 반환 타입이 다른 것을 확인이 가능하다.

     

    이처럼 Arrays.stream()은 배열 그 중에서도 원시 타입 숫자 배열의 스트림에 특화되어있음을 알 수 있다.

     

    그렇다면 왜 굳이 이렇게 원시타입을 특화했어야했을까?

     

     

     

    바로 원시 타입 배열 숫자들의 계산을 쉽게 도와주는 메서드가 따로 존재하기 때문이다.

     

    Arrays.stream()을 통해 원시타입 숫자배열을 스트림화하면 일반 방법보다 훨씬 편리하게 이용이 가능하다.

     

    결론만 말하면 원시타입 숫자 배열으로 스트림 생성시 Arrays.stream()을 쓰는 것이 유리하다.

     

    Stream.of()의 사용

    앞서 Arrays.stream()가 원시 타입 배열의 스트림 생성에 특화되있다고 설명한 바있다.

     

    이게 바로 Stream.of()와 Arrays.stream()의 가장 큰 차이다.

     

    근데 Arrays.stream()의 경우 원시타입 뿐 아니라 일반 String 배열도 다룰 수 있다.

     

    그렇다보면 Stream.of()이 왜 필요한지 의문이 들 수 있다.

     

     

     

    예를 들어 위와 같이 두개의 문자열 배열이 있다고 가정해보자.

     

    스트림을 이용해 이 두 개의 배열을 하나로 합치려면 어떻게 해야할까?

     

     

     

    그럴 때 바로 Stream.of( ) 메서드를 사용하면 된다.

     

    Stream.of ( )의 가장 큰 특징은 바로 가변 인자 즉 메서드의 매개변수를 여러개 받을 수 있다는 것이다.

     

     

     

    자바의 API 문서를 봐도 여러 개의 매개변수를 받을 수 있음을 알 수 있다.

     

    이처럼 Stream.of()는 배열 뿐 아니라 리스트 등의 스트림을 합치는데도 이용된다.

     

    그래서 만약 두 개 이상의 의 컬렉션이나 배열을 합칠 경우 Stream.of()를 사용하면 된다.

     

    임의의 수(랜덤값)로 스트림 만들기

     

    코드를 작성하다보면 임의의 수를 다룰 때가 있다.

     

    그럴때는 위 코드처럼 랜덤 클래스의 객체를 생성 후 각 기본 타입별의 스트림을 반환하는 메서드를 사용하면 된다.

     

    Random클래스의 객체를 생성한 뒤에 체이닝으로 붙는 메서드들은 다음과 같은 의미를 갖는다.

     

    ints( ): int를 타입으로 하는 숫자를 무한하게 만드는 스트림을 반환

    longs( ): long을 타입으로 하는 숫자를 무한하게 만드는 스트림을 반환

    doubles( ): double를 타입으로 하는 숫자를 무한하게 만드는 스트림을 반환

     

    이때 주의할 점은 ints()를 그냥 사용하면 말그대로 무한 스트림이므로 사이즈를 제한할 필요가 있다.

     

    사이즈를 제한하는 방법은 두 가지다.

     

     

     

    가장 간단한 방법은 위처럼 매개변수로 임의의 정수를 제공하는 것이다.

     

    이렇게 할 경우 처음 스트림을 반환할 때부터 제한된 사이즈의 스트림을 제공한다.

     

     

     

    두번째 방법은 무한스트림을 반환받은 다음 limit() 메서드를 사용하는 것이다.

     

    그리고 한 가지 더 만약 임의의 숫자를 출력할 때 범위를 지정하고 싶을 때는 아래와 같이 하면 된다.

     

     

     

    위와 같이 랜덤 숫자 생성 메서드 안에 매개변수로 임의의 수를 제공하면 된다.

     

    왼쪽 가장 첫번째 숫자는 스트림의 크기, 그 다음은 처음 수, 그 다음은 마지막 수다.

     

    여기서는 1, 1, 10을 넣었으므로 1~10 범위 사이에서 1개의 숫자를 랜덤하게 출력하게 만드는 스트림을 반환한다.

     

    특정 범위의 정수를 요소로 갖는 스트림 생성

     

    특정 범위의 숫자를 모두 요소로 갖는 스트림을 생성하고 싶다면 range( ), rangeClosed( )를 쓰면 된다.

     

    range()와 rangeClosed()의 매개 변수로 두 숫자를 입력하면 범위 안의 요소를 모두 갖는 스트림이 완성된다.

     

    이때 range()와 rangeClosed()의 차이는 두번째 숫자를 범위 안에 넣느냐 마느냐의 차이다.

     

    위의 코드를 보면 range()와 rangeClosed()는 각각 1,5를 매개변수로 전달했다.

     

    이때 range()는 1,2,3,4만 요소로 갖는데 반해 rangeClosed()는 1,2,3,4,5를 요소로 갖는 것을 볼 수 있다.

     

    즉 range()는 마지막 숫자를 제외한 범위, rangeClosed()는 마지막 숫자를 포함한 범위의 스트림을 만든다.

     

    특정한 값만 골라서 요소로 갖는 무한 스트림 생성

     

    지금까지 컬렉션과 배열 스트림, 랜덤 무한 스트림, 특정 범위를 갖는 무한 스트림을 만드는 것을 배웠다.

     

    하지만 가끔은 단순히 랜덤이나 특정 범위의 숫자 뿐 아니라 특정 조건을 가진 값을 갖는 스트림이 필요하기도 하다.

     

    그럴 때는 위와 같이 iterate()와 generate()를 사용하면 된다.

     

    iterate()는 매개변수로 초기값(seed)와 람다식(UnaryOperator)를 제공하면 그에 맞는 숫자를 생성한 스트림을 반환한다.

     

    위의 코드를 보면 람다식으로 초기값으로 숫자 0을 제공하고 람다식으로는 매개변수로 전달된 수에 3을 더하도록했다.

     

    결과는 어떨까?

     

     

     

    시드값을 기준으로 매번 3을 더하는 숫자를 모두 요소로 갖는 스트림이 생성됨을 볼 수 있다.

     

     

     

    대략 정리하면 위와 같다.

     

    처음 준 시드값을 람다식(UnaryOperator)의 매개변수로 제공하고 그에 따라 반환된 값을 요소로 저장한다.

     

    그리고 이 반환된 값을 시드값 삼아 계속 무한하게 숫자가 생성된다.

     

    generate()도 iterate()와 비슷하게 람다식을 제공하지만, 기능은 전혀 다르다.

     

    우선 위 코드에 따른 generate()의 결과값부터 보도록 하자.

     

     

     

    generate()는 iterate()와 달리 그저 같은 숫자를 반복할 뿐이다.

     

    generate()에 제공되는 람다식은 iterate()의 람다식과는 다른 인터페이스이므로 반환값도 다르다.

     

    generate()에는  iterate()와 달리 supplier 인터페이스의 람다식을 매개변수로 제공해야한다.

     

    즉 매개변수가 없이 어떤 특정값을 생성하는 supplier의 특성으로 반환값이 저렇게 된 것이다.

     

    빈 스트림 만들기

     

    자주는 아니더라도  텅텅 빈 스트림을 만드는 일도 있을 것이다.

     

    그럴 때는 위와 같이 empty() 메서드를 이용하면 된다.

Designed by Tistory.