시작이 반

[Java] Stream 본문

Programming/Java

[Java] Stream

G_Gi 2021. 1. 20. 21:08
SMALL

Spring공부중 자바 문법을 몰라서 일단 정리한다... 필요한 것만 우선 정리..

 

Stream 생성

  1. 컬렉션
  2. 배열
  3. 가변 매개변수
  4. 지정된 범위의 연속된 정수
  5. 특정 타입의 난수들
  6. 람다 표현식
  7. 파일
  8. 빈스트림

 

Collection

자바에서 제공하는 모든 컬렉션의 최고 상위 조상인 Collection 인터페이스에는 stream()메소드가 정의되어 있다.

즉, Collection 인터페이스를 구현한 모든 List와 Set 컬렉션 클래스에서도 stream()메소드로 스트림을 생성할 수 있다.

또한, parallelStream() 메소드를 사용하면 병렬 처리가 가능한 스트림을 생성할 수 있다.

ArrayList<Integer> list = new ArrayList<Integer>();

list.add(4);
list.add(2);
list.add(3);
list.add(1);

// 컬렉션에서 스트림 생성
Stream<Integer> stream = list.stream();

// forEach() 메소드를 이용한 스트림 요소의 순차 접근
stream.forEach(System.out::println); // 4 2 3 1

 

 

배열

배열에 관한 스트림을 생성하기 위해 Arrays 클래스에는 다양한 형태의 stream()메소드가 정의되어 있다.

또한, 기본타입인 int, long, double 형을 저장할 수 있는 배열에 관한 스트림이 별도로 정의되어 있다.

이러한 스트림은 java.util.stream 패키지의 IntStream, LongStream, DoubleStream 인터페이스로 각각 제공된다.

String[] arr = new String[]{"넷", "둘", "셋", "하나"};

// 배열에서 스트림 생성
Stream<String> stream1 = Arrays.stream(arr);
stream1.forEach(e -> System.out.print(e + " ")); //넷 둘 셋 하나
System.out.println();

// 배열의 특정 부분만을 이용한 스트림 생성
Stream<String> stream2 = Arrays.stream(arr, 1, 3);
stream2.forEach(e -> System.out.print(e + " ")); //둘 셋

Arrays 클래스의 stream() 메소드는 전체 배열뿐만 아니라 배열의 특정 부분만을 이용하여 스트림을 생성할 수도 있다.

 

 

가변 매개변수

Stream 클래스의 of() 메소드를 사용하면 가변 매개변수(variable parameter)를 전달받아 스트림을 생성할 수 있다.

Stream<Double> stream = Stream.of(4.2, 2.5, 3.1, 1.9);

stream.forEach(System.out::println); //4.2 2.5 3.1 1.9

 

 

지정된 범위의 연속된 정수

지정된 범위의 연속된 정수를 스트림으로 생성하기 위해 IntStream나 LongStream 인터페이스에는 range()와 rangeClosed() 메소드가 정의되어 있다.

range() 메소드는 명시된 시작 정수를 포함하지만, 명시된 마지막 정수는 포함하지 않는 스트림을 생성한다.

rangeClosed() 메소드는 명시된 시작 정수뿐만 아니라 명시된 마지막 정수까지도 포함하는 스트림을 생성한다.

// 지정된 범위의 연속된 정수에서 스트림 생성
IntStream stream1 = IntStream.range(1, 4);
stream1.forEach(e -> System.out.print(e + " ")); //1 2 3
System.out.println();

IntStream stream2 = IntStream.rangeClosed(1, 4); 
stream2.forEach(e -> System.out.print(e + " ")); //1 2 3 4

이외에

특정 타입의 난수, 람다, 파일, 빈스트림으로 Stream을 생성 할 수있다.

나머지는 참고주소 확인

 


스트림의 중개 연산(intermediate operation)

 

스트림API에 의해 생성된 초기 스트림은 중개 ㅇ녀산을 통해 또 다른 스트림으로 변환된다.

이러한 중개 연산은 스트림을 전달받아 스트림으로 반환함으로, 중개연산은 연속으로 연결해서 사용할 수 있다.

스트림의 중개 연산은 필터-맵(filter-map) 기반의 API를 사용함으로 지연(lazy) 연산을 통해 성능을 최적화할 수 있다.

 

  1. 스트림 필터링 : filter(), distinct()
  2. 스트림 변환 : map(), flatMap()
  3. 스트림 제한 : limit(), skip()
  4. 스트림 정렬 : sorted()
  5. 스트림 연산 결과 확인 : peek()

스트림 필터링

filter() 메소드는 해당 스트림에서 주어진 조건에 맞는 요소만으로 구성된 새로운 스트림을 반환한다.

또한, distinct() 메소드는 해당 스트림에서 중복된 요소가 제거된 새로운 스트림을 반환한다.

distinct() 메소드는 내부적으로 Object 클래스의 equals() 메소드를 사용하여 요소의 중복을 비교합니다.

IntStream stream1 = IntStream.of(7, 5, 5, 2, 1, 2, 3, 5, 4, 6);
IntStream stream2 = IntStream.of(7, 5, 5, 2, 1, 2, 3, 5, 4, 6);

// 스트림에서 중복된 요소를 제거함.
stream1.distinct().forEach(e -> System.out.print(e + " ")); //7 5 2 1 3 4 6
System.out.println();

// 스트림에서 홀수만을 골라냄.
stream2.filter(n -> n % 2 != 0).forEach(e -> System.out.print(e + " ")); //7 5 5 1 3 5

 

나머지는 참고 주소 확인하자...

 


스트림의 최종 연산(terminal operation)

스트림 API에서 중개 연산을 통해 변환된 스트림은 마지막으로 최종 연산을 통해 각 요소를 소모하여 결과를 표시한다.

즉, 지연(lazy)되었던 모든 중개 연산들이 최종 연산 시에 모두 수행되는 것입니다.

이렇게 최종 연산 시에 모든 요소를 소모한 해당 스트림은 더는 사용할 수 없게 됩니다.

 

  1. 요소의 출력 : forEach()
  2. 요소의 소모 : reduce()
  3. 요소의 검색 : findFirst(), findAny()
  4. 요소의 검사 : anyMatch(), allMatch(), noneMatch()
  5. 요소의 통계 : count(), min(), max()
  6. 요소의 연산 : sum(), average()
  7. 요소의 수집 : collect()

요소의 출력

앞선 수업에서 자주 사용한 forEach() 메소드는 스트림의 각 요소를 소모하여 명시된 동작을 수행한다.

반환 타입이 void이므로 보통 스트림의 모든 요소를 출력하는 용도로 많이 사용합니다.

Stream<String> stream = Stream.of("넷", "둘", "셋", "하나");

stream.forEach(System.out::println); //넷 둘 셋 하나

 

요소의 소모

스트림의 최종 연산은 모두 스트림의 각 요소를 소모하여 연산을 수행하게 된다.

하지만 reduce() 메소드는 첫 번째와 두 번째 요소를 가지고 연산을 수행한 뒤, 그 결과와 세 번째 요소를 가지고 또다시 연산을 수행한다.

이런 식으로 해당 스트림의 모든 요소를 소모하여 연산을 수행하고, 그 결과를 반환하게 된다.

또한, 인수로 초깃값을 전달하면 초깃값과 해당 스트림의 첫 번째 요소와 연산을 시작하며, 그 결과와 두 번째 요소를 가지고 계속해서 연산을 수행하게 된다.

 

ex)스트림의 각 문자열 요소를 "++" 기호로 연결하여 출력하는 예제

Stream<String> stream1 = Stream.of("넷", "둘", "셋", "하나");
Stream<String> stream2 = Stream.of("넷", "둘", "셋", "하나");

Optional<String> result1 = stream1.reduce((s1, s2) -> s1 + "++" + s2); 
result1.ifPresent(System.out::println); //넷++둘++셋++하나

String result2 = stream2.reduce("시작", (s1, s2) -> s1 + "++" + s2); 
System.out.println(result2);  //시작++넷++둘++셋++하나

위의 예제처럼 인수로 초깃값을 전달하는 reduce() 메소드의 반환 타입은 Optional<T>가 아닌 T 타입

그 이유는 비어 있는 스트림과 reduce 연산을 할 경우 전달받은 초깃값을 그대로 반환해야 하기 때문

 

 

요소의 검색

findFirst()와 findAny() 메소드는 해당 스트림에서 첫 번째 요소를 참조하는 Optional 객체를 반환한다.

두 메소드 모두 비어 있는 스트림에서는 비어있는 Optional 객체를 반환한다.

IntStream stream1 = IntStream.of(4, 2, 7, 3, 5, 1, 6);
IntStream stream2 = IntStream.of(4, 2, 7, 3, 5, 1, 6);

OptionalInt result1 = stream1.sorted().findFirst();
System.out.println(result1.getAsInt()); //1

OptionalInt result2 = stream2.sorted().findAny();
System.out.println(result2.getAsInt()); //1

나머지는 참고 주소 확인하자...

 

 

참고주소

 

LIST

'Programming > Java' 카테고리의 다른 글

[Java] 코테 준비 ( 알아야할 것들)  (2) 2021.08.13
[Java] SOLID  (1) 2021.05.10
[Java] Lombok  (0) 2021.01.25
[Java] 람다식(Lambda)  (0) 2021.01.20
[Java] Optional  (0) 2021.01.20