이번에는 스트림의 생성과 연결
, 중간 연산
, 최종 연산
에 대해 정리 해보자.
기존에 스트림을 생성하던 두 가지 방식
위와 같은 스트림 생성 방법은 기존에 존재하는 데이터를 기반으로 하여 스트림을 생성한다.
// 배열 기반
String [] arr = {"Box", "Toy", "Rogo"};
Arrays.stream(arr)
.filter(a -> a.length > 2)
.forEach(a -> System.out.println(a + "\t"));
System.out.println();
// 컬렉션 인스턴스 기반
List<String> arr2 = Arrays.asList("Box", "Boy", "Gir");
arr2.stream()
.filter(a -> a.length > 2)
.forEach(a -> System.out.println(a + "\t"));
System.out.println();
기존에 존재하는 데이터를 대상으로 스트림을 생성하는 것이 아닌, of() 메서드를 통해 새로운 스트림을 만들어보자.
public static void main(String[] args) {
Stream.of(11, 22, 33, 44) // 네 개의 값으로 이뤄진 스트림 생성
.forEach(n -> System.out.println(n + "\t"));
System.out.println();
Stream.of("So Simple") // 하나의 String 인스턴스로 이뤄진 스트림 생성
.forEach(s -> System.out.println(s + "\t"));
System.out.println();
List<String> s1 = Arrays.asList("Toy", "Robot", "Box");
Stream.of(s1) // 하나의 컬렉션 인스턴스로 이뤄진 스트림 생성
.forEach(w -> System.out.println(w + "\t"));
System.out.println();
}
기존에 존재하는 데이터를 기반으로 하여 스트림을 생성하지 않는다?
데이터를 of()에 넣어
초기화가 가능하다.대상
을 가지고 스트림을 만든다.컬렉션 인스턴스
(1개)를 대상으로 스트림을 형성 했다.11 22 33 44
So Simple
[Toy, Robot, Box]
// 병령 스트림 메서드 예제
-----------------------------------------------------------------------
Stream<T> parallel() // Stream<T>의 메소드
DoubleStream parallel() // DoubleStream의 메소드
IntStream paralle() // IntStream의 메소드
LongStream paralle() // Longstream의 메소드
-----------------------------------------------------------------------
public static void main(String[] args) {
List<String> ls = Arrays.asList("Box", "Simple", "Complex", "Robot");
Stream<String> ss = ls.Stream(); // 스트림 생성
BinaryOperator<String> lc = (s1, s2) -> {
if(s1.length() > s2.length())
return s1;
else
return s2;
}
String str = ss.parallel() // 병렬 스트림 생성
.reduce("", lc); // 리덕션
System.out.println(str);
}
- 스트림이 병렬 스트림이냐, 아니다가 중요한 것이 아니다.
- 이후에 진행이 되는 중간, 최종 연산이 병렬로 처리 되는것.
Complex
public static void main(String[] args) {
Stream<String> ss1 = Stream.of("Cake", "Milk");
Stream<String> ss2 = Stream.of("Lemon", "Jelly");
// 스트림을 하나로 묶은 후 출력(즉 연결 함수 -> concat을 통해)
Stream.concat(ss1, ss2)
.forEach(s -> System.out.println(s));
}
concat()
를 통해 두 인자를 연결 한다.하나의 스트림으로 생성 해준다
.[Stream<T>의 map 시리즈 메소드들] 1:1 맵핑
mapToInt
(ToIntFunction<T> mapper)mapToLong
(ToLongFunction<T> mapper)mapToDouble
(ToDoubleFunction<T> mapper)[Stream<T>의 flaMap 시리즈 메소드들] 1:* 맵핑
Stream<R>
> mapper)mapToInt
(ToIntFunction<T> mapper)mapToLong
(ToLongFunction<T> mapper)mapToDouble
(ToDoubleFunction<T> mapper)
- Stream<T>의 map, flatMap 메서드를 통해 새로운 값을 만든다 봐도 무방하다.
- 또한 위 메서드를 통해 불필요한 박싱(Boxing), 언박싱(UnBoxing)을 피할 수 있다.
public static void main(String[] args) {
Stream<String> ss1 = Stream.of("MY_AGE", "YOUR_LIFE");
// 아래 람다식에서 스트림을 생성
Stream<String> ss2 = ss1.flatMap(s -> Arrays.stream(s.split("_"))); // 중간 연산
ss2.forEach(s -> System.out.println(s + "\t"))
System.out.println();
}
위 부분 설명해주실때 이해가 잘 되지 않았음 → 제대로 이해할 필요가 있음
// 성적표 클래스 정의
class ReportCard {
private int kor;
private int eng;
private int math;
public ReportCard(int k, int e, int m) {
kor = k;
eng = e;
math = m;
}
public int getKor() { return kor; }
public int getEng() { return eng; }
public int getMath() { return math; }
}
public static void main(String[] args) {
// 배열에 3개의 성적표 인스턴스를 저장
ReportCard[] cards = {
new ReportCard(70, 80, 90),
new ReportCard(90, 80, 70),
new ReportCard(80, 80, 80)
};
// Reportcard 인스턴스로 이루어진 스트림 생성
Stream<String> sr = Arrays.stream(cards);
// 위 배열을 통해 스트림 생성
// 1 : 3의 맵핑을 수행 한다.
// 학생들의 점수 정보로 이루어진 스트림 생성
IntStream si = sr.flatMapToInt(r -> IntSream.of(r.getKor(), r.getEnf(), r.getMath()));
// 평균을 구하기 위한 최종 연산 average 진행
double avg = si.average().getAsDouble();
System.out.println("avg. " + avg);
}
// 정렬 메서드 구분
-----------------------------------------------------------------------
Stream<T> sorted(Comparator<? super T> comparator) // Stream<T>의 메서드
Stream<T> sorted() // Stream<T>의 메서드
IntStream sorted() // IntStream의 메서드
LongStream sorted() // LongStream의 메서드
DoubleStream sorted() // DoubleStream의 메서드
-----------------------------------------------------------------------
public static void main(String[] args) {
// 사전 편찬순으로 정렬을 수행 한다
Steeam.of("Box", "Apple", "Robot")
.sorted() // String 인스턴스는 Comparable<String> 인터페이스를 구현! 이를 기반으로 한 정렬 수행
.forEach(s -> System.out.println(s + "\t"));
System.out.println();
Stream.of("Box", "Apple", "Rabit")
.sorted((s1, s2) -> s1.length() - s2.length())
.forEach(s -> System.out.println(s + "\t"));
System.out.println();
}
String 클래스
는 내부적
으로 Compartor 인터페이스
를 구현
하고 있다(복습)Apple Box Robot
Box Apple Rabit
Stream의 forEach
같은 경우 최종 연산
인 반면, 이번에는 중간 연산
인 peek() 메서드
에 대해 알아보자.
public static void main(String[] args) {
// 최종 연산이 생략된 스트림의 파이프라인
IntSteam.of(1, 3, 5)
.peek(d -> System.out.println(d + "\t"));
System.out.println();
// 최종 연산이 존재하는 스트림의 파이프 라인
int a = IntSteam.of(1, 3, 5)
.peek(d -> System.out.println(d + "\t"))
.sum();
System.out.println("a : " + a);
}
중간 연산
이라는 차이점이 있다.최종 연산
이 진행되지 않은 중간 연산
은 아무 의미가 없다, 즉 → 실행 결과를 얻을 수 없다
.최종 연산인 forEach()
는 위 같은 실행이 불가능하기에 peek()를 사용
한다.스트림의 중간 연산을 사용한 후 반드시 최종 연산 구문을 넣어줘야 실행을 할 수 있다.
스트림의 최종 연산
을 알아보기에 앞서, IntStream에 존재하는 메서드에 대해 정리 해보자.
public static void main(String[] args) {
// 스트림에 존재하는 정수의 합 구하기
int sum = IntStream.of(1, 3, 5, 7, 9)
.sum();
System.out.println("sum : " + sum);
// 스트림에 존재하는 정수의 개수 구하기
long cnt = IntStream.of(1, 3, 5, 7, 9)
.count();
System.out.println("count : " + count);
// 평균
IntStream.of(1, 3, 5, 7, 9)
.average() // 반환형이 Optional이니까 -> 이어서 ifPresent() 사용 가능하다.
.ifPresent(av -> System.out.println("avg : " + av));
// 최소값
IntStream.of(1, 3, 5, 7, 9)
.min()
.ifPresent(mn -> System.out.println("min : " + mn));
// 최대값
IntStream.of(1, 3, 5, 7, 9)
.max()
.ifPresent(mx -> System.out.println("max : " + mx));
}
True, false 반환
.람다식 수행
.null
을 허용하지 않는다
.null
을 허용
한다.class StringOptional2 {
public static void main(String[] args) {
Optional<String> os1 = Optional.of(new String("Toy1"));
Optional<String> os2 = Optional.ofNullable(new String("Toy2"));
os1.ifPresent(s -> System.out.println(s)); // 람다식 버전
os2.ifPresent(System.out::println); // 메소드 참조 버전
}
}
public static void main(String[] args) {
boolean b = IntStream.of(1, 2, 3, 4, 5)
.allMatch(n -> n % 2 == 0);
System.out.println("모두 짝수이다. " + b);
b = IntStream.of(1, 2, 3, 4, 5)
.anyMatch(n -> n % 2 == 0);
System.out.ptinln("짝수가 하나는 있다. " + b);
b = IntStream.of(1, 2, 3, 4, 5)
.noneMatch(n -> n % 2 == 0);
System.out.ptinln("짝수가 하나도 없다. " + b);
}
모든 인자
가 조건에 맞아야 True
를 반환.하나의 인자
라도 조건에 맞으면 True
를 반환.모든 인자
가 조건에 맞지 않는다면 True
를 반환.<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner)
// Stream의 collect 메서드 예제
public static void main(String[] args) {
String [] words = {"Hello", "Box", "Robot", "Toy"};
Stream<String> ss = Arrays.stream(words);
List<String> ls =
ss.filter(s -> s.length() < 5)
.collect(() -> new ArrayList<>(), // 저장소 생성
(c, s) -> c.add(s), // 첫 번째 인자 통해 생성된 인스턴스 c, 스트림의 데이터 s
(lst1, lst2) -> lst1.addAll(lst2)); // 순차 스트림에서는 의미 없음
System.out.println(ls);
}
collect()
메서드를 사용하면 filter()
메서드를 통해 살아남은 데이터
, 즉 걸러진 데이터
를 따로 저장할 수 있다.Box
, Toy
만 걸러진다.new ArrayList<>()
를 통해 저장소 생성
.c
, s) → c
: 저장소 (new ArrayList<>())를 의미.s
) → s
: 스트림에 저장된 데이터를 의미(Box, Toy).IntStream it = IntStream.of(1,3, 4, 5, 6);
int a = it.sum();
long b = it.count(); // 여기서 아래와 같은 컴파일 에러 발생
System.out.println(a);
System.out.println(b);
Exception in thread "main" java.lang.IllegalStateException: stream has already
been operated upon or closed
at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
at java.util.stream.LongPipeline.<init>(LongPipeline.java:91)
at java.util.stream.LongPipeline$StatelessOp.<init>(LongPipeline.java:574)
at java.util.stream.IntPipeline$5.<init>(IntPipeline.java:261)
at java.util.stream.IntPipeline.mapToLong(IntPipeline.java:260)
at java.util.stream.IntPipeline.count(IntPipeline.java:430)
at com.java.study.stream.StreamOf.main(StreamOf.java:72)
Stream
형 참조 변수
로 스트림
을 참조하고 있지만, 중간
, 최종 연산
이 수행된 스트림은 소멸
이 된다.class CollectParallelStringStream {
public static void main(String[] args) {
String [] words = {"Hello", "Box", "Robot", "Toy"};
Stream<String> ss = Arrays.stream(words);
List<String> ls = ss.parallel()
.filter(s -> s.length() < 5)
.collect(() -> new ArraytList<>(),
(c, s) -> c.add(s),
(lst1, lst2) -> lst1.addAll(lst2));
}
}