본문 바로가기

Java

Stream(1)

Stream(1)

  • 람다를 활용하여 데이터 처리 연산을 지원하도록 컬렉션, 배열, I/O 자원 등에 대해 저장되어 있는 요소들을 추출하여 반복적인 처리를 가능하게 하는 기능

Collection vs Stream 

  • 스트림은 컬렉션과 마찬가지로 연속된 값 집합의 인터페이스를 제공한다.

① Collection

 

  • 컬렉션은 현재 자료구조가 포함하는 모든 값을 메모리에 저장한 후 계산이 가능하다.
    Ex) 다운로드된 동영상
  • 컬렉션은 외부 반복(ex.for-each문 등)을 통해 반복을 제어한다. 

② Stream

 

  • 스트림은 게으른 연산(Lazy)을 진행하여 요청할 때만 값을 계산한다.
    Ex) 전체 중 일정 구간만 제공되는 스트리밍 동영상
  • 스트림은 한 번 사용 후 소멸되며 재사용도 불가능하다.
  • 스트림은 내부 반복을 사용한다.
    - 내부에서 자동으로 반복을 처리하므로 반복자를 사용할 필요가 없다.
    - 동기화 처리 없이 병렬성 처리가 가능하다.

특징

  1. 원본 데이터를 변경하지 않는다.
    - 데이터를 읽기만 할 뿐 변경하지 않는다.
    - 필요한 경우 연산 결과를 컬렉션이나 배열에 담아 반환할 수 있다.
  2. 일회용이다.
    - 요소들을 모두 읽은 후 객체가 닫혀서 다시 사용할 수 없으며 필요한 경우 스트림을 다시 생성해야 한다.
  3. 작업을 내부 반복으로 처리한다.
  4. 병렬처리가 가능하다.
  • 장점
    - Stream API에서 제공되는 함수들을 이용하여 코드가 간결해지고 가독성이 좋아진다.
  • 단점
    - 단순 반복문(for-each)의 경우에는 for문 또는 컬렉션의 forEach 함수를 사용하는 것이 속도가 빠르다.

연산

  • 중간 연산
    - 단말 연산을 스트림 파이프라인에 실행하기 전까지 아무 연산도 수행하지 않는다.
    - 리턴값이 없다.
    Ex) filter, map, limit, sorted, distinct
  • 최종 연산
    - 스트림 파이프라인에서 결과를 도출한다.
    Ex) forEach, collect, count
  • 게으른 연산(Lazy)
    - 스트림은 filter-map 기반의 API를 사용하여 지연 연산을하여 성능을 최적화한다.
    - 이로 인해 최종 연산이 없으면 중간 연산이 실행되지 않는다.

예제

① 스트림 API는 다양한 데이터 소스에서 생성할 수 있다.

 

// 베열 스트림
String[] stringArr = new String[]{"hello", "world", "!"};
Stream<String> streamArrString = Arrays.stream(stringArr);
Stream<String> streamArrStringPart = Arrays.stream(stringArr, 1, 3);

// 컬렉션 스트림
List<String> listString = new ArrayList<>();
Stream<String> streamListString = listString.stream();
Stream<String> parallelStreamListString = listString.parallelStream(); // 병렬 처리 스트림

// 비어있는 스트림

// 메소드 이용 스트림
Stream<String> streamBuilder = Stream.<String>builder().add("A").add("B").build();
Stream<String> streamGenerate = Stream.generate(() -> "wow").limit(5);
Stream<Integer> streamIterate = Stream.iterate(50, n -> n + 5).limit(5);

List<String> streamBuilderList = streamBuilder.collect(Collectors.toList());

// 기본 타입형 스트림
IntStream intStream = IntStream.range(1, 6);
LongStream longStream = LongStream.rangeClosed(1, 5);
Stream<Integer> boxedStreamInt = IntStream.range(1, 6).boxed();

List<Integer> intStreamList = intStream.boxed().collect(Collectors.toList());
List<Integer> boxedStreamIntList = boxedStreamInt.collect(Collectors.toList());

// 난수 스트림
DoubleStream doubleStream = new Random().doubles(3);

List<Double> doubleStreamList = doubleStream.boxed().collect(Collectors.toList());

// 문자열 스트림
IntStream charStream = "Hello".chars();
Stream<String> stringStream = Pattern.compile(", ").splitAsStream("A, B, C");

List<Integer> charStreamList = charStream.boxed().collect(Collectors.toList());
List<String> stringStreamList = stringStream.collect(Collectors.toList());

// 파일 스트림
Stream<String> lineStream = Files.lines(Paths.get("D:/test.txt"), Charset.forName("UTF-8"));

List<String> lineStreamList = lineStream.collect(Collectors.toList());

// 병렬 스트림
Stream<String> parallelStringStream = Stream.<String>builder().add("A").add("B").build().parallel();

List<Integer> listInteger = new ArrayList<>();
Stream<Integer> parallelStreamlistInteger = listInteger.parallelStream();

IntStream parallelIntStream = IntStream.range(1, 6).parallel();
IntStream sequentialIntStream = parallelIntStream.sequential(); // 시퀀셜 모드로 변경

// 스트림 연결
Stream<String> concatStream = Stream.concat(stringStream, lineStream);

List<String> concatStreamList = concatStream.collect(Collectors.toList());
System.out.println(concatStreamList);

 

② 중개 연산

    - 스트림을 전달받아 스트림으로 반환하므로 연속으로 사용할 수 있다.

 

  1. Stream 필터링 : filter(), distinct()
  2. Stream 변환 : map(), flatMap()
  3. Stream 제한 : limit(), skip()
  4. Stream 정렬 : sorted()
  5. Stream 연산 결과 확인 : peek()
List<String> cars = Arrays.asList("Jeep", "BMW", "Kia", "Hyundai", "Bentley");

// Filtering
Stream<String> filteredCars = cars.stream().filter(car -> car.startsWith("B"));
List<String> filteredCarsList = filteredCars.collect(Collectors.toList());

// Mapping
Stream<String> mappedStream = cars.stream().map(car -> car.toUpperCase());
List<String> mappedCarsList = mappedStream.collect(Collectors.toList());

List<List<String>> flatStream = Arrays.asList(Arrays.asList("A"), Arrays.asList("B"));
List<String> flatList = flatStream.stream().flatMap(Collection::stream).collect(Collectors.toList());

// Sorting
Stream<String> sortedStream = cars.stream().sorted(); // 오름차순
List<String> sortedCarsList = sortedStream.collect(Collectors.toList());

Stream<String> sortedReverseStream = cars.stream().sorted(Comparator.reverseOrder()); // 내림차순
List<String> sortedReverseCarsList = sortedReverseStream.collect(Collectors.toList());

Stream<String> sortedLengthStream = cars.stream().sorted(Comparator.comparingInt(String::length)); // 문자열 개수 오름차순
List<String> sortedLengthCarsList = sortedLengthStream.collect(Collectors.toList());

// Iterating
int sum = IntStream.of(1, 2, 3, 4, 5).peek(System.out::println).sum();

 

③ 최종 연산
    - 앞서 중개 연산을 통해 만들어진 스트림에 있는 요소들에 대해 이들을 소모하여 결과를 표시한다.
      즉, 지연(Lazy)되었던 모든 중개 연산들이 최종 연산 시에 모두 수행된다.
      따라서 최종 연산 시 모든 요소를 소모한 스트림은 더 이상 사용 불가능하다.

 

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

// Reduction
Integer reducedParams = Stream.of(1, 2, 3, 4, 5).reduce(10, Integer::sum, (a, b) -> { System.out.println("called"); return a + b; });

// Collecting
List<String> collectingList = cars.stream().collect(Collectors.toList());
String joiningString = cars.stream().collect(Collectors.joining(", ", "[", "]"));
Double averageDouble = Stream.of(1, 2, 3, 4, 5).collect(Collectors.averagingInt(n -> n));
int sumDouble = Stream.of(1, 2, 3, 4, 5).collect(Collectors.summingInt(n -> n));
DoubleSummaryStatistics statistics = Stream.of(1, 2, 3, 4, 5).collect(Collectors.summarizingDouble(n -> n));

// Matching
boolean isExist = cars.stream().anyMatch(car -> car.startsWith("B"));

// Iterating
cars.stream().forEach(System.out::println);

 

▷ 출처

https://innovationm.co/concept-of-stream-api-java1-8/ 
https://velog.io/@mooh2jj/Java-stream-Collector-%EC%99%9C-%EC%93%B0%EB%8A%94-%EA%B1%B0%EC%95%BC

https://futurecreator.github.io/2018/08/26/java-8-streams/

728x90

'Java' 카테고리의 다른 글

Spring Framework의 주요 특징  (1) 2024.07.23
Stream(2)  (0) 2024.07.19
[Spring] @RequestBody vs @RequestParam  (0) 2024.07.10
[Spring] @Controller VS @RestController  (0) 2024.07.09
재귀 알고리즘  (0) 2024.07.04