Java 8 Stream API

Minsu Kang·2020년 10월 22일
0

Java

목록 보기
1/5

Stream 이란?

Strem은 Java 8 의 기능 중 하나로, 리눅스에서 사용되는 파이프 라인 처럼 한번에 한 개 씩 만들어지는 연속적인 데이터 항목들의 모임이다.

Stream은 배열, List 등의 요소들의 처리를 담당한다.

기존 배열처리를 간단하게 해주며 Functional style로 처리할 수 있도록 해준다.


Stream의 특징

  • 람다식으로 요소 처리코드를 제공한다.
 List<String> list = Arrays.asList("홍길동", "신용권", "김남준");
 list.stream().forEach(name -> System.out.println(name));
  • 스트림 연산은 원본을 변경하지 않는다. 즉, Immutable 하다.

  • 내부 반복자(Internal iteration)를 사용한다.

    • 외부 반복자 : 개발자가 코드로 직접컬렉션의 요소를 반복해서 가져오는 것을 말한다.

    • 내부 반복자 : 컬렉션 내부에서 요소들을 반복시키고 개발자는 각 요소당 처리해야하는 코드만 제공하는 것을 말한다.

  • 많은 Stream의 기능들은 Stream 자기 자신을 리턴한다. 이 방식은 처리 작업이 체인처럼 연결되어 큰 파이프라인처럼 작동 하도록할 수 있다.

 List<Integer> list = Arrays.asList(3, 5, 2, 4);
 list.stream().sorted().filter(e -> e < 5).forEach(System.out::println);
  • 멀티쓰레드 코드를 직접 구현할 필요 없이 데이터를 병렬로 처리할 수 있다.

    🤚 병렬 처리?
    병렬처리란, 한 가지 작업을 서브 작업으로 나누고, 서브 작업들을 분리된 스레드에서 병렬적으로 처리하는 것을 말한다.

  • 스트림은 중간 처리와 최종 처리를 할 수 있다. 중간 처리에서는매핑, 필터링, 정렬 등을 수행하고 최종 처리에서는 반복, 카운팅, 평균, 총합 등의 집계 처리를 수행한다.

  • 스트림은 딱 한번만 소비할 수 있다. 한 번 탐색된 스트림의 요소는 소비되고, 다시 탐색하려면 초기 데이터 소스에서 새로운 스트림을 다시 만들어야 한다.

Stream 사용법


Stream 객체 얻기

Stream 중간 처리 메서드

대표적인 메서드들

  • distinct()

    어떤 스트림에서 중복되는 아이템들을 모두 제거해주고 새로운 스트림을 반환한다.

 List<Integer> list = Arrays.asList(1, 2, 3, 3);
 list.stream().distinct().forEach(System.out::println);

 결과
 1
 2
 3

  • filter()

    특정 조건으로 스트림의 요소를 필터링 하고 필터링된 새로운 스트림을 반환한다.

 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
 list.stream().filter(e -> e > 3).forEach(System.out::println);

 결과
 4
 5

  • map()

    각각의 요소들을 변경하여 새로운 스트림을 생성한다.

 List<Integer> list = Arrays.asList(1, 2, 3);
 list.stream().map(e -> e * 2).forEach(System.out::println);

 결과
 2
 4
 6

  • flatMap()

    배열로 감싸져 있는 모든 원소를 단일 원소 스트림으로 반환한다.

 Integer arr[][] = { { 1, 2 }, { 2, 4 }, { 1, 5 } };
 Arrays.stream(arr).flatMap(array -> Arrays.stream(array)).forEach(System.out::println);

 결과
 1
 2
 2
 4
 1
 5

 // 똑같은 결과를 map으로 하려면? 차이점을 확인해보자!
 Arrays.stream(arr)
     .map(array -> Arrays.stream(array))
     .forEach(numbers -> numbers.forEach(System.out::println));

  • MaptoInt()

    map과 똑같은 기능을 하지만 IntStream을 반환한다.
    mapToLong, mapToDouble 또한 특화 스트림으로 변환해 반환한다.

 class Student {
     String name;
     int num;
     Student (String name, int num) {
	     this.name = name;
	     this.num = num;
     }	
 }

 List<Student> students = new ArrayList<>();
 students.add(new Student("James", 1));
 students.add(new Student("Alice", 2));
 students.add(new Student("Tom", 3));
		
 int sum = students.stream().mapToInt(e -> e.num).sum();
 System.out.println(sum);
		
 결과
 6
 // 오류발생
 int[] arr = students.stream().map(e -> e.num).toArray(); 

 // 정상 작동
 int[] arr = students.stream().mapToInt(e -> e.num).toArray(); 
	

  • asDoubleStream()

    IntStream의 요소 또는 LongStream의 요소를 double 요소로 타입 변환해서 DoubleStream을 생성한다.

  • asLongStream()

    IntStream의 int 요소를 long 요소로 타입 변환해서 LongStream을 생성한다.

  • boxed()

    int 요소, long요소, double요소를 Integer, Long, Double 요소로 박싱해서 Stream을 생성한다.


Stream 최종 처리 메서드

주요 메서드들

  • allMatch()

    Stream의 모든 요소가 조건을 충족하는지 확인한다.

 List<Integer> list = new ArrayList<>();
 IntStream.range(1, 5).forEach(e -> list.add(e));
		
 boolean result1 = list.stream().allMatch(e -> e >= 1);
 boolean result2 = list.stream().allMatch(e -> e >= 2);	
		
 System.out.println(result1 + " " + result2);

 결과
 true false

  • allMatch()

    Stream의 요소가 1개라도 충족하는지 확인한다.

  • noneMatch()

    Stream의 모든 요소가 충족하지 않는지 확인한다.

  • count()

    스트림의 요소의 수를 반환한다.

  • findFirst(), findAny()

    스트림에서 첫 번째 요소를 참조하는 Optional 객체를 반환한다. findAny()는 병렬스트림에서 주로 사용한다.

    🤚 Optional?
    Optional 클래스는 Integer나 Double 클래스처럼 'T'타입의 객체를 포장해 주는 래퍼 클래스이다.

    따라서 Optional 인스턴스는 모든 타입의 참조 변수를 저장할 수 있다.

    아직 Optional에 대해서는 공부를 충분히 하지 않았기 때문에 추후에 공부를 더 하고 설명에 대한 글을 써봐야 겠다..

 List<Integer> list = new ArrayList<>();
 IntStream.range(1, 5).forEach(e -> list.add(e));
 Optional<Integer> a = list.stream().filter(e -> e > 1).findFirst();

 System.out.println(a.get());

 결과
 2

  • min(), max()

    스트림의 요소 중에서 가장 큰 값과 가장 작은 값을 가지는 요소를 참조하는 Optional 객체를 반환한다.

  • sum()

    스트림의 모든 요소의 합을 반환한다.

  • average()

    스트림의 모든 요소의 평균을 Optional 객체로 반환한다.

 List<Integer> list = new ArrayList<>();
 IntStream.range(1, 5).forEach(e -> list.add(e));
 OptionalDouble avg = list.stream().filter(e -> e > 1).mapToInt(e -> e).average();

 System.out.println(avg.getAsDouble());

 결과
 3.0

  • reduce()

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

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

    초기값이 있는 경우 반환 타입은 Optional<T>가 아닌 T 타입이다. 전달 받은 초기값과 동일한 타입을 반환해야 하기 때문이다.

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

 Optional<String> result1 = stream1.reduce((s1, s2) -> s1 + "++" + s2);
 result1.ifPresent(System.out::println);
		
 // 초기값이 있는 reduce
 String result2  = stream2.reduce("시작", (s1, s2) -> s1 + "++" + s2);
 System.out.println(result2);

 결과
 넷++++++하나
 시작++++++++하나

  • collect()

    인수로 전달되는 Collectors 객체에 구현된 방법대로 스트림의 요소를 수집한다.

    기본적으로 toCollection(), toList(), toSet(), toMap() 등이 있다.

    그 외에 통계와 연산, 그룹화와 분할 등을 위한 메서드가 존재하는데, 이 부분에 대해서는 추후에 더 공부하여 포스팅 하는걸로..

 // List로 변환
 Stream<String> stream = Stream.of("a", "b", "c", "d");
 List<String> list = stream.collect(Collectors.toList());
		
 // Set으로 변환
 stream = Stream.of("a", "b", "c", "d");
 Set<String> set = stream.collect(Collectors.toSet());

참고한 곳
https://palpit.tistory.com/647
https://ict-nroo.tistory.com/43
http://tcpschool.com/java/java_stream_concept

profile
백엔드 개발자

0개의 댓글