스트림의 이해
내부가 비어있는 긴 파이프의 한쪽 끝으로 물을 흘려보내면 그 물이 다른 한쪽을 흘러 나온다.
이때 이러한 물의 흐름을 가리켜 '스트림'이라 한다. 이와 유사하게 자바에서도 '데이터의 흐름'을
생성할수 있으며 이러한 데이터의 흐름을 가리켜 '스트림'이라 한다.
그리고 데이터를 흘려보낼 연산의 종류는 두 가지로 나뉜다.
1. 중간연산 - 마지막이 아닌 위치에서 진행이 되어야 하는 연산.
2. 최종연산 - 마지막에 진행이 되어야 하는 연산.
스트림을 생성하고 이를 대상으로 '중간 연산'과 '최종 연산'을 진행하면 원하는 기주능로 데이터를
필터링하고 필터링 된 데이터의 가공된 결과를 매우 적은 양의 코드로 얻을 수 있다.
class MyFirstStream{
public static void main(String[] args){
int[] ar = {1,2,3,4,5};
IntStream stm1 = Arrays.stream(ar);
IntStream stm2 = stm1.filter(n -> n%2 ==1);
int sum = stm2.sum();
System.out.println(sum);
}
}
----------------------------------------------------------------------
9
스트림 생성하기 : 배열
배열에 저장된 데이터를 대상으로 스트림을 생성할때 호출하는 대표 메소드는 다음과 같다.
public static<T> Stream<T> stream(T[] array)
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));
}
}
---------------------------------------------------------------------
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)
위 메소드의 매개변수에 선언에서 보이듯 매개변수 형이 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)
.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형 스트림에 저장된 값의 총합을 계산하여 반환하였다.