[JAVA] 람다와 스트림 ( Lambda & Stream ) ⑦

DongGyu Jung·2022년 5월 1일
0

자바(JAVA)

목록 보기
55/60
post-thumbnail

🏃‍♂️ 들어가기 앞서..

본 게시물은 스터디 활동 중에 작성한 게시물로 자바의 정석-기초편 교재를 학습하여 정리하는 글입니다.
※ 스터디 Page : 〔투 비 마스터 : 자바〕

*해당 교재의 목차 순서와 구성을 참고하여 작성하며
각 내용마다 부족할 수 있는 내용이나 개인적으로 궁금한 점은
추가적인 검색을 통해 채워나갈 예정입니다.



🎊 스트림(Stream) 만들기

스트림(Stream)으로 작업을 하기 위해서는
당연히 무엇보다도 스트림을 생성하는 방법을 알아야 하는데

이 스트림의 데이터 소스가 될 수 있는 대상은
배열 / 컬렉션 / 임의의 난수 / 람다식 등등 다양하고
각자 만드는 방법이 있다.

① 컬렉션 (Collection)

List, Set 등의 조상,
컬렉션의 최고 조상인 Collection에는 stream()메서드가 정의되어 있다.

Stream<E> stream() // Collection 인터페이스에 정의되어 있는 메서드

그렇기 때문에
자연스레 자손인 List나 Set를 구현한 컬렉션 클래스들은
모두 이 stream()메서드를 통해 스트림을 생성할 수 있다.

List<Integer> list = Arrays.asList(1,2,3,4,5) ; // 가변인자 ( 배열의 길이를 정하지 않고 ) 로
//List 만들기 위한 .asList

Stream<Integer> intStream = list.stream() ; //  List -> Stream


② 배열 (Arrays)

배열을 데이터 소스로 하는 스트림을 생성하는 메서드는
StreamArrays
static메서드로 정의되어 있다.

/* Stream */
Stream<T> Stream.of(T...values) // 가변
Stream<T> Stream.of(T[])

/* Arrays */
Stream<T> Arrays.stream(T[])
// index로 원하는 구간의 값들만으로 stream 생성 가능
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive) 

Integer[] intArr = {1,2,3,4,5}; //int[] 는 불가능 --> 쓰고싶으면 IntStream 사용해야함.
Stream<Integer> intStream = Arrays.stream(intArr) ; // intArr가 참조형(Integer)이여야 가능하다.

/* String(문자열) Stream 만들기 */
Stream<String> strStream=Stream.of("a", "b", "c")
Stream<String> strStream=Stream.of(new String[]{"a", "b", "c"});
Stream<String> strStream=Arrays.stream(new String[]{"a", "b", "c"});
Stream<String> strStream=Arrays.stream(new String[]{"a", "b", "c"}, 0, 3);

이전 게시물에서도 알아봤던

" 기본형 배열로부터 생성된 스트림 "
즉, 참조형이 아닌 기본형 Stream에서 또한
기본형 배열을 소스로 하는 스트림을 생성하는 메서드도 있다.

/* 기본형 IntStream */
IntStream IntStream.of(int...values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive) 

// IntStream 뿐만 아니라 LongStream과 DoubleStream도 위 방법과 동일하다.


③ 난수 (Random) & 범위 (Range)

▣ 난수

난수를 생성하는데 사용되는 Random클래스에는

각 타입별 난수로 이루어진 기본형 스트림을 반환하는 메서드를 포함하고 있다.

IntStream ints()
LongStream longs()
DoubleStream doubles()

이 메서드들은 기본적으로
크기가 정해지지 않은 무한 스트림을 반환하기 때문에
limit()라는 " 스트림 개수 지정(크기 제한) 메서드 "를 사용해서 유한 스트림으로 만들어줘야 한다.

IntStream intStream = new Random().ints(); // 매개변수 값 없기 때문에 " 무한 스트림 "
intStream.limit(5).forEach(System.out::println) ; // 임의의 난수 "5개" 요소만 출력
  • 유한 스트림 : 크기가 정해져있는 스트림

  • 무한 스트림 : 무한한 크기로 값을 가지는 ( 매개변수 값 없는 ints(), longs(), doubles() 등 ) 스트림

limit()말고 유한 스트림으로 만들기 위한 방법으로는
난수 스트림 생성 메서드에 매개변수크기를 지정해주면 된다.

IntStream ints(long streamSize)
LongStream longs(long streamSize)
DoubleStream doubles(long streamSize)

// 매개변수값 활용 유한 스트림 만들기
IntStream intStream = new Random().ints(5); // 매개변수 값 5 : " 5개의 요소를 가지는 유한 스트림 "
intStream.forEach(System.out::println) ; // 임의의 난수 "5개" 요소만 출력

위 메서드들에 의해 생성된 스트림의 난수는

  • Integer.MIN_VALUE <= ints() <= Integer.MAX_VALUE

  • Long.MIN_VALUE <= longs() <= Long.MAX_VALUE

  • 0.0 <= doubles() < 1.0

범위를 갖는다.

※ Random 클래스 _ 지정된 범위의 난수를 요소로 갖는 스트림을 생성하는 메서드
단, end는 범위에 포함되지 않는다.

/* 무한 스트림 */
IntStream ints(int begin, int end)
LongStream longs(long begin, long end)
DoubleStream doubles(double begin, double end)

/* 유한 스트림 */
IntStream ints(long streamSize, int begin, int end)
LongStream longs(long streamSize, long begin, long end)
DoubleStream doubles(long streamSize, double begin, double end)

▣ 특정 범위

흔히 범위를 지정해서 사용할 때 range()를 주로 사용한다.
이는 특정 범위의 정수 요소를 반환하는데

Stream에서도 range 키워드를 통해 특정 범위의 정수 요소를 가지며
당연히 정수 요소이기 때문에
IntStreamLongStream 이 해당 메서드를 가지고 있다.

IntStream IntStream.range(int begin, int end)
IntStream IntStream.rangeClosed(int begin, int end)

두 메서드의 차이는
end 값 포함 여부 이다.

  • range : end 값 포함 X
  • rangeClosed : end 값 포함 O
IntStream intS = IntStream.range(1, 5) ; // 1,2,3,4
IntStream intS = IntStream.rangeClosed(1, 5) ; // 1,2,3,4,5


④ 람다식 (lambda)

Stream 클래스에서 람다식을 매개변수를 받는 메서드로는
크게 iterate()generate() 가 있다.

이 두 메서드는
매개변수로 받은 람다식에 의해 계산되는 값들을 요소로 하는
" 무한 스트림 "을 생성한다.

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stream<T> generate(Supplier<T> s)

딱 보기에도 두 함수 사이의 차이점이 확연히 보인다.
바로 seed값 이다.

iterator()
씨앗값부터 " 시작 "해서
" 람다식 f에 의해 계산된 결과 "를
다시 씨앗값(seed)으로 지정해서 계산을 반복한다.

Stream<Integer> evenStream = Stream.iterate(0, n -> n+2) ; 
/*
0 으로 2
2 로 4
4 로 6
.....(무한 스트림)
*/

generate()
람다식에 의해 계산되는 값을 요소로하는 무한스트림을 생성 및 반환하는 것은 맞지만
iterator()와는 달리
이전 결과를 통해 다음 요소를 계산하지 않는다.

또한
위에서 눈치 챘다시피
매개변수인 람다식의 타입이 매개변수가 없는 람다식 인터페이스인 Supplier<T> s이다.

Stream<Double> randStream = Stream.generate(Math::random) ; 
Stream<Integer> oneStream = Stream.generate(() -> 1) ;

❗ *주의점* ❗
iterate()generate() 에 의해 생성된 스트림을 기본형 스트림 타입 참조변수로 다룰 수 없다.

  • 기본형 스트림으로 사용하려면 mapToInt() mapToLong() , mapToDouble() 등을 사용해야 함.
  • 기본형 스트림을 일반적인 제네릭 타입 지정 스트림으로 사용할 땐, 오토박싱 해주는 boxed()를 사용한다.

⑤파일 (Files) & 빈 (empty) Stream

▣ 파일

java.nio.file.Files 에서는
파일을 다루는데 필요한
유용한 메서드들을 제공한다.

Files.list()
지정 디렉토리(dir)에 있는
파일 목록을 소스로 하는 스트림을 생성해 반환한다.

Stream<Path> Files.list(Path dir)

파일 목록에서 더 들어가
한 파일의 한 행(line)을 요소로 하는 스트림을 생성하는 메서드도 있다.
(보통 로그(Log)파일을 읽을 때 사용한다. )

파일뿐만 아니라
다른 입력대상으로부터도 단위 Read가 가능하다.

Stream<String> Files.lines(Path path)
Stream<String> Files.lines(Path path, Charset cs)
Stream<String> lines() // BufferedReader 클래스의 메서드

▣ 비어있는 스트림

" 요소가 하나도 없는 말 그대로 비어있는 스트림 "

" 연산 수행 결과가 없을 땐 ", null보단 빈 스트림을 반환하는 것이 낫다.

Stream emptyStream = Stream.empty(); // 비어있는 스트림 생성 및 반환

long cnt = emptyStream.count(); // 비어있기 때문에 당연히 0

0개의 댓글