자바의 스트림

27178 단어 JavaJava

스트림

스트림= 한번에 한개씩 만들어지는 연속적인 데이터 항목들의 모임
자바8부터 추가된 컬렉션의 저장요소를 하나씩 참조해서 람다식으로 처리할수있도록 해주는 반복자이다.

자바 7이전의 코드

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class ex {
    public static void main(String[] args) {

    List<String> list = Arrays.asList("이영진", "김영진");
    Iterator<String> iterator = list.iterator();

    while(iterator.hasNext()){
        String name= iterator.next();
        System.out.println(name);
    }

}
}

자바 8이후 코드

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class ex {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("이영진", "김영진");
        Stream<String> stream = list.stream();
        stream.forEach(name -> System.out.println(name));
        // 컬렉션의 요소를 하나씩 콘솔에 출력 
}
}
  • 람다식으로 요소 처리 코드를 제공한다. (Stream 이 제공하는 대부분의 요소처리 메소드에는 함수적 인터페이스 매개타입을 가짐)

외부반복자, 내부반복자

외부반복자(external iterator)
=> 개발자가 직접 컬렉션의 요소를 반복
ex) for문, Iterator 이용하는 while문 등

내부반복자(internal iterator)
=> 컬렉션내부에서 요소들을 반복시킴

내부반복자는 컬렉션요소들의 반복을 컬렉션에게 맡겨두고, 개발자는 다른코드에 집중할수있다.
병렬작업을 할수있게 도와주기때문에 효율적으로 요소를 반복시킬수있다고 할수있다.

Iterator 는 개발자가 직접 요소를 처리하는것까지 하는것에 비해
Stream은 람다식으로 요소처리 내용만 전달하고, 반복은 컬렉션 내부에서일어난다. 그뿐만아니라 요소의 병렬처리가 일어나므로 장점이다.

스트림의 중간처리와 최종처리

그렇지만 스트림에게 처리한다고 해서 한번에 간단하게 처리되는것이아니라.

데이터의 필터링,매핑,정렬,그룹핑등 의 중간처리
합계,평균,카운팅,최대값,최소값등의 최종처리인 파이프라인으로 해결한다.

중간스트림이 형설될때 요소들이 바로 중간처리(필터링,매핑,정렬등)되는것이아니라 최정처리가 시작되기전까지 중간처리는 lazy(지연)된다.

Stream 인터페이스에는 필터링,매핑,정렬등 많은 중간처리 메소드가 있다.
이 메소드들은 중간처리된 스트림을 리턴한다.

ex) 회원컬렉션에서 남자만 필터링하는 중간스트림을 연결하고, 다시 남자의 나이로 매핑하는 스트림을 연결한후, 최종남자 평균나이를 집계한다면
그림과 코드는 다음과 같다.

Member 클래스

public class Member {

    public static int MALE =0;
    public static int FEMALE = 1;
    private String name;
    private int sex;
    private int age;

    public Member(String name, int sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    public int getSex() {
        return sex;
    }

    public int getAge() {
        return age;
    }
}

스트림 파이프라인


List<Member> list = Arrays.asList(
                new Member("이영진", Member.MALE, 30),
                new Member("김영근", Member.MALE, 29),
                new Member("이예지", Member.FEMALE, 24)
        );

        //람다이전의 코드 
//        Stream<Member> maleFemaleStream = list.stream();
//        Stream<Member> maleStream = maleFemaleStream.filter(m -> m.getSex() == Member.MALE);
//        IntStream ageStream = maleStream.mapToInt(Member::getAge);
//        OptionalDouble optionalDouble = ageStream.average();
//        double ageAvg = optionalDouble.getAsDouble();
//
        
        //자바8이후의 코드 
        double ageAvg = list.stream()
                .filter(m-> m.getSex() == Member.MALE)
                .mapToInt(Member::getAge)
                .average()
                .getAsDouble();

필터링(distinct(), filter() )

distinct() =(중복제거)와
filter()= (조건 필터링) 는 모든 스트림이 가지고있는 공통메소드임

flatMapxxx(), mapxxx(), asxxxStream(), boxed() ...

flatMapxxx()
=> A라는 요소가있으면 A1,A2 요소로 대체 됨

mapXXX() 메소드
=> A라는 요소가있으면 C or D 로 대체됨

루핑 (peek(), forEach())

peek() : 중간처리 메소드
forEach() : 최종처리 메소드

    intStream
          .filter(a-> a%2 == 0)
          .peek( a-> System.out.println(a))

다음과같이 짝수로 필터링하고 나머지를 보기위한 상황에서 스트림은 전혀 동작하지않는다. peek()는 중간 처리단계 메소드이기때문이다

    intStream
          .filter(a-> a%2 == 0)
          .peek( a-> System.out.println(a))
          .sum() 

다음과같이해야 peek()가 정상적으로 동작한다.

예시코드

  public static void main(String[] args) {
        int[] intArr = {1, 2, 3, 4, 5};

        System.out.println("[peek()를 마지막에 호출한 경우]");
        Arrays.stream(intArr)
                .filter(a -> a%2 ==0) // 짝수만 필터링
                .peek(n-> System.out.println(n)); // 동작하지않음 peek()는 중간처리메소드이기때문에

        System.out.println("[최종 처리 메소드를 마지막에 호출한 경우]");
        int total = Arrays.stream(intArr)
                .filter(a -> a%2 ==0) // 짝수만 필터링
                .peek(n-> System.out.println(n)) //  동작한다.
                .sum(); // 최종메소드
        System.out.println("총합 = " + total);

        System.out.println("[forEach()를 마지막에 호출한 경우]");
        Arrays.stream(intArr)
                .filter(a-> a%2 ==0)
                .forEach(n -> System.out.println(n)); // forEach()는 최종메소드, 최종메소드로 동작함

    }

실행결과

allMatch(),anyMatch(), noneMatch()

특정조건에 만족하는지 조사하는 매칭 메소드들이다.

allMatch() 는 모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하는지,
anyMatch()는 최소한 한개의요소들이 매개값으로 주어진 Predicate의 조건을 만족하는지,
noneMatch()는 모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하지 않는지 조사한다.

Arrays.stream(intArr)
	.allMatch(a -> a%2 ==0); // 모든요소가 2의배수인지
    
    Arrays.stream(intArr)
	.anyMatch(a -> a%3 ==0); // 하나라도 3의배수가 있는지
    
    Arrays.stream(intArr)
	.noneMatch(a -> a%3 ==0); // 3의배수가 없는지 
   

기본집계 sum(), count(), average(), max(), min()

대량의 데이터를 가공해서 축소하는 리덕션이라고 볼수있다
카운팅, 합계, 평균값, 최대값, 최소값등을 산출해준다.

TakeWhile

자바9에서 지원하는 새로운 메서드
filter와 비슷한역할을 하지만 스트림을 슬라이스 할수있는 기능을 가지고있다.
filter는 전체 스트림을 반복하며 각요소에 predicate를 적용하게된다면
TakeWhile은 predicate에서 조건을 통과하지못한 녀석이 나타나면 즉시 멈춘다.

DROPWHILE

자바9에서 지원하는메서드
TAKEWHILE 과 정반대의 작업을 수행한다. predicate가 처음으로 거짓이되는지점을 발견하면 그지점에서 작업을 중단하고 요소를 모두 반환한다.

출처 : 이것이 자바다 http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788968481475

좋은 웹페이지 즐겨찾기