스트림 기초

gustjtmd·2022년 1월 16일
0

Java

목록 보기
35/40

스트림의 이해

내부가 비어있는 긴 파이프의 한쪽 끝으로 물을 흘려보내면 그 물이 다른 한쪽을 흘러 나온다.
이때 이러한 물의 흐름을 가리켜 '스트림'이라 한다. 이와 유사하게 자바에서도 '데이터의 흐름'을
생성할수 있으며 이러한 데이터의 흐름을 가리켜 '스트림'이라 한다.

그리고 데이터를 흘려보낼 연산의 종류는 두 가지로 나뉜다.

1. 중간연산 - 마지막이 아닌 위치에서 진행이 되어야 하는 연산.
2. 최종연산 - 마지막에 진행이 되어야 하는 연산.

스트림을 생성하고 이를 대상으로 '중간 연산''최종 연산'을 진행하면 원하는 기주능로 데이터를
필터링하고 필터링 된 데이터의 가공된 결과를 매우 적은 양의 코드로 얻을 수 있다. 
class MyFirstStream{
	public static void main(String[] args){
    	int[] ar = {1,2,3,4,5};
        IntStream stm1 = Arrays.stream(ar); //배열 ar로부터 스트림 생성
        IntStream stm2 = stm1.filter(n -> n%2 ==1); //중간 연산 진행
        int sum = stm2.sum(); //최종 연산 진행.
        System.out.println(sum);
        /*
        	int sum = Arrays.stream(ar) 스트림 생성하고
                .filter(n -> n%2 == 1) filter 통과시키고
                .sum(); sum을 통과시켜 그 결과 반환
        	System.out.println(sum);
        */
    }
}
----------------------------------------------------------------------
9

스트림 생성하기 : 배열

배열에 저장된 데이터를 대상으로 스트림을 생성할때 호출하는 대표 메소드는 다음과 같다.

public static<T> Stream<T> stream(T[] array) //Arrays 클래스에 정의

public class StringStream {
    public static void main(String[] args) {
        String[] names = {"Seo", "Seoung", "Hyun"};
        Stream<String> stm = Arrays.stream(names);  //스트림 생성
        stm.forEach(s -> System.out.println(s));
        /*
        Arrays.stream(names).foreach(s-> System.out.println(s)
        */
    }
}
---------------------------------------------------------------------
Seo
Seoung
Hyun

forEach는 '최종 연산'이다. 그래서 스트림 생성 이후에 메소드를 통해서 스트림을 이루고 있는
문자열들을 출력하였다('중간 연산'없이 바로 '최종 연산'을 진행할수도 있다.

스트림 생성하기: 컬렉션 인스턴스

컬렉션 인스턴스 대상으로 스트림을 생성하고 싶다면 마찬가지로 stream 메소드를 찾으면 된다.
그리고 이를 목적으로 stream 메소드는 java.util.Collection<E>에 디폴트 메소드로
다음과 같이 정의되어 있다.
default Stream<E> stream()

즉 컬렉션 인스턴스를 대상으로 stream 메소드를 호출하여 스트림을 생성할수 있다.
public class ListStream {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Toy", "Robot", "Box");
        list.stream().forEach(s -> System.out.println(s));
    }
}
Toy
Robot
Box

중간연산 필터링과 맵핑

스트림이 배열을 대상으로 생성되었건 컬렉션 인스턴스를 대상으로 생성되었건 이에 상관없이 동일한
방법으로 '중간 연산''최종 연산'을 진행할 수 있다. 
즉 지금부터 설명하는 순간 중간 연산인 '필터링''맵핑'은 모든 스트림에 적용 가능하다.

필터링

필터링은 그 이름처럼 스트림을 구성하는 데이터 중 일부를 조건에 따라 걸러내는 행위를 의미함.
필터링에 사용되는 메소드는 다음과 같다.

Stream<T> filter(Predicate<? super T> predicate) //Stream<T>에 존재

위 메소드의 매개변수에 선언에서 보이듯 매개변수 형이 Predicate이다.
따라서 Predicate의 다음 추상 메소드의 구현에 해당하는 람다식을 인자로 전달해야 한다.

PredicateT>	boolean test(T t)

그러면 filter 메소드는 내부적으로 스트림의 데이터를 하나씩 인잘 전달하면서 test를 호출한다.
그결과 true, false 반환.
public class FilterStream {
    public static void main(String[] args) {
        int[] ar = {1,2,3,4,5};
        Arrays.stream(ar)   //배열 기반 스트림 생성
                .filter(n -> n%2 == 1) //홀수만 통과
                .forEach(n -> System.out.printf(n +"\t"));
        System.out.println();

        List<String> sl = Arrays.asList("Toy", "Robot", "Box");
        sl.stream() //컬렉션 인스턴스 기반 스트림 생성
                .filter(s -> s.length() == 3)   //길이가 3이면 통과시킨다.
                .forEach(s -> System.out.printf(s +"\t"));
    }
}
--------------------------------------------------------------------------
1	3	5	
Toy	Box	

맵핑

맵핑도 필터링과 마찬가지로 중간 연산이다.
맵핑에 사용되는 대표적인 메소드는 다음과 같으며 이는 보이는 바와 같이 제네릭 메소드이다.

<R> Stream<R> map(Funciont<? super T, ? extends R> mapper) 

위 메소드의 매개변수 형이 Function이다. 따라서 다음 메소드의 구현에 해당하는 람다식을
인자로 전달해야 한다.

Function<T, R> R apply(T t)

그러면 map은 내부적으로 스트림의 데이터를 하나씩 인자로 전달하며 apply 메소드를 호출한다.
그리고 그 결과로 반환되는 값을 모아 새로운 스트림을 생성한다.
public class MapToInt {
    public static void main(String[] args) {
        List<String> ls = Arrays.asList("Box", "Robot", "Simple");

        ls.stream()
                .map(s -> s.length())
                .forEach(s -> System.out.print(s+"\t"));
    }
}
----------------------------------------------------------------------
3	5	6

이 예제의 경우 apply 메소드에 문자열이 전달되고 그 문자열의 길이가 반환되도록 하는 것이
목적이니 다음 메소드의 구현에 해당하는 람다식을 인자로 전달하면 된다.

필터링과 맵핑 합쳐서 사용해보기.

class ToyPriceInfo{ //장난감 모델 별 가격 정보
    private String model;   //모델 명
    private int price;  //가격

    public ToyPriceInfo(String m, int p){
        model = m;
        price = p;
    }
    public int getPrice(){
        return price;
    }
}
public class ToyStream {
    public static void main(String[] args) {
        List<ToyPriceInfo> ls = new ArrayList<>();
        ls.add(new ToyPriceInfo("Gun_LR_45",200));
        ls.add(new ToyPriceInfo("TEDDY_BEAR",350));
        ls.add(new ToyPriceInfo("CAR_TRASFORM",550));

        int sum = ls.stream()
                .filter(p -> p.getPrice() <500)
                .mapToInt(s -> s.getPrice())
                .sum();
        System.out.println(sum);
    }
}
------------------------------------------------------------------
sum = 550;


위 예제에서 다음 문장을 통해 얻은 결과는 '정가 500원 미만인 장난감 가격의 총합'이다.
int sum = ls.stream()
                .filter(p -> p.getPrice() <500)
                .mapToInt(s -> s.getPrice())
                .sum();
                
먼저 다음 연산을 통해 가격이 500원 미만인 장난감의 가격 정보만을 모아서 스트림을 생성하였다.

.filter(p -> p.getPrice() < 500);

이렇게 얻은 스트림의 가격이 500원 미만인 TOyPriceInfo 인스턴스의 스트림인데
다음 연산을 통해서 인스턴스에 저장되어 있는 가격 정보를 꺼내서 int형 스트림을 생성하였다.

.mapToInt(t -> t.getPrice())
끝으로 sum을 통해서 int형 스트림에 저장된 값의 총합을 계산하여 반환하였다.
profile
반갑습니다

0개의 댓글