Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

일상기록

Java - 스트림(Stream) 만들기 본문

Java

Java - 스트림(Stream) 만들기

너 구 나 2023. 4. 19. 18:16

스트림(Stream) 만들기

스트림으로 작업을 하려면, 스트림이 필요하다 스트림의 소스가 될 수 있는 대상은 배열, 컬렉션, 임의의 수 등 다양하며, 이 다양한 소스들로 부터 스트림을 생성할 수 있다.

컬렉션

컬렉션의 최상위클래스인 Collection에 stream()이 정의되어 있다. 따라서 Collection의 하위클래스인 List와 Set을 구현한 컬렉션 클래스들은 모두 이 메소드로 스트림을 생성할 수 있다. stream()은 해당 컬렉션을 소스(source)로 하는 스트림을 반환한다.

Stream<T> Collection.stream()

List로 예를들면 아래와 같다

List<Integer> list = Arrays.asList(1,2,3,4,5);	// 가변인자
Stream<Integer> intStream = list.stream();	// list를 소스로 하는 컬렉션 생성

배열

배열을 소스로 하는 스트림을 생성하는데 메소드는 다음과 같이 Stream과 Arrays에 static메소드로 정의되어 있다.

Stream<T> Stream.of(T... values)    //  가변 인자
Stream<T> Stream.of(T[])
Stream<T> Arrays.stream(T[])
Stream<T> Arrays.stream(T[] array, int startInclusive, int endExclusive)

int, long, double과 같은 기본형 배열을 소스로 하는 스트림을 생성하는 메소드도 있다.

IntStream IntStream.of(int.. values)
IntStream IntStream.of(int[])
IntStream Arrays.stream(int[])
IntStream Arrays.stream(int[] array, int startInclusive, int endExclusive)

특정 범위의 정수

IntStream과 LongStream은 다음과 같이 지정된 범위의 연속된 정수를 스트림으로 생성해서 반환하는 range()와 rangeClosed()를 가지고 있다.

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

 range()의 경우 경계의 끝인 end가 범위에 포함되지 않고,  rangeClosed()의 경우 포함된다.

IntStream intStream = IntStream.range(1, 5)		// 1, 2, 3, 4
IntStream intStream = IntStream.rangeClosed(1, 5)	// 1, 2, 3, 4, 5

int보다 큰 범위의 스트림을 생성하려면 LongStream에 있는 동일한 이름의 메소드를 사용하면 된다.

임의의 수

난수를 생성하는데 사용하는 Random클래스에는 아래와 같은 인스턴스 메소드들이 포함되어 있다. 이 메소드들은 해당 타입의 난수들로 이루어진 스트림을 반환한다.

IntStream               ints()
LongStream           longs()
DoubleStream       double()

이 메소드들이 반환하는 스트림은 크기가 정해지지 않는 '무한 스트림(infinite stream)'이므로 limit()도 같이 사용해서 스트림의 크기를 제한해 주어야 한다. limit()는 스트림의 개수를 지정하는데 사용하며, 무한 스트림을 유한 스트림으로 만들어 준다.

IntStream               ints(long streamSize)
LongStream           longs(long streamSize)
DoubleStream       double(long streamSize)
IntStream intStream = new Random().ints(5);	// 크기가 5인 난수 스트림을 반환

위 메소드들에 의해 생성된 스트림의 난수는 아래의 범위를 갖는다.

Integer.MIN_VALUE        <=         ints()            <=         Integer.MAX_VALUE
   Long.MIN_VALUE        <=         longs()         <=         Long.MAX_VALUE
                            0.0        <=        double()       <=         1.0

지정된 범위(begin~end)의 난수를 발생시키는 스트림을 얻는 메소드는 아래와 같다. 단, end는 범위에 포함되지 않는다.

IntStream               ints(int begin, int end)
LongStream           longs(long begin, long end)
DoubleStream       double(double begin, double end)
IntStream               ints(long streamSize, int begin, int end)
LongStream           longs(long streamSize, long begin, long end)
DoubleStream       double(long streamSize, double begin, double end)

람다식 - iterate(), generate()

Stream클래스의 interate()와 generate()는 람다식을 매개변수로 받아서, 이 람다식에 의해 계산되는 값들을 요소로 하는 무한 스트림을 생성한다.

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

iterate()는 씨앗값(seed)으로 지정된 값부터 시작해서, 람다식 f에 의해 계산된 결과를 다시 seed값으로 해서 계산을 반복한다. 아래의 evenStream은 0부터 시작해서 값이 2씩 계속 증가한다.

Stream<Integer> evenStream = Stream.iterate(0, n->n+2);	// 0, 2, 4, 6, ...

generate()도 iterate()처럼, 람다식에 의해 계산되는 값을 요소로 하는 무한 스트림을 생성해서 반환하지만, iterate()와 달리, 이전 결과를 이용해서 다음 요소를 계산하지 않는다.

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

generate()에 정의된 매개변수의 타입은 Supplier<T>이므로 매개변수가 없는 람다식만 허용된다. 한 가지 주의할 점은 iterate()와 generate()에 의해 생성된 스트림을 아래와 같이 기본형 스트림 타입의 참조변수로 다룰 수 없다는 것이다.

DoubleStream randomStream = Stream.generate(Math::random);	// 에러
IntStream evenStream = Stream.generate(0, n->n+2);		// 에러

굳이 필요하다면 아래와 같이 mapToInt()와 같은 메소드로 변환을 해야 한다.

IntStream evenStream = Stream.iterate(0, n->n+2).mapToInt(Integer::valueOf);
Stream<Integer> stream = evenStream.boxed();	// IntStream -> Stream<Integer>

반대로 IntStream타입의 스트림을 Stream<Integer>타입으로 변환하려면, boxed()를 사용하면 된다.

파일

java.nio.Files는 파일을 다루는데 필요한 유용한 메소드들을 제공하는데, list()는 지정된 디렉토리(dir)에 있는 파일의 목록을 소스로 하는 스트림을 생성해서 반환한다.

Stream<Path>             Files.list(Path dir)

이 외에도 Files클래스에는 Path를 요소로 하는 스트림을 생성하는 메소드가 더 있다. 

파일의 한 행(line)을 요소로 하는 스트림을 생성하는 메소드도 있다. 아래의 세번째 메소드는 BufferedReader클래스에 속한 것인데, 파일 뿐만 아니라 다른 입력대상으로부터도 데이터를 행단위로 읽어올 수 있다.

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

빈 스트림

요소가 하나도 없는 비어있는 스트림을 생성할 수도 있다. 스트림에 연산을 수행한 결과가 하나도 없을 때, null보다 빈 스트림을 반환하는 것이 낫다.

Stream emptyStream = Stream.empty();	// empty()는 빈 스트림을 생성해서 반환한다.
long count = emptyStream.count();	// count의 값은 0

count()는 스트림의 요소의 개수를 반환하는 메소드이다.

두 스트림의 연결

Stream의 static메소드인 concat()을 사용하면, 두 스트림을 하나로 연결할 수 있다. 물론 연결하려는 두 스트림의 요소는 같은 타입이어야 한다.

String[] str1 = {"123", "456", "789"};
String[] str2 = {"ABC", "abc", "DEF"};

Stream<String> strs1 = Stream.of(str1);
Stream<String> strs2 = Stream.of(str2);
Stream<String> strs3 = Stream.concat(strs1, strs2);	// 두 스트림을 하나로 연결

'Java' 카테고리의 다른 글

가비지 컬렉션(Garbage Collection)  (0) 2023.04.22
Java - 스레드(thread)  (1) 2023.04.20
Java - 스트림(stream)  (0) 2023.04.19
Java - 메소드 참조  (0) 2023.04.19
Java - Function의 합성, Predicate의 결합  (0) 2023.04.19