스트림 생성에 필요한 데이터를 직접 전달
스트림 생성과 관련하여 Stream 인터페이스에 정의되어 있는 static 메소드가 들어있다.
static stream of(T t)
static stream of(T …. values)
스트림 생성이 필요한 데이터를 인자로 직접 전달할 수 있다.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Main1 {
public static void main(String[] args) {
Stream.of(11,22,33,44)
.forEach(n -> System.out.println(n));
System.out.println();
Stream.of("So Simple")
.forEach(s -> System.out.println(s));
System.out.println();
List<String> s1 = Arrays.asList("Toy", "Robot", "Box");
Stream.of(s1)
.forEach(w -> System.out.println(w));
System.out.println();
}
}
Integer 형을 오토 언박싱. 생성된 스트림에는 하나의 인스턴스만 존재. 그리고 그 인스턴스는 참조변수 s1이 참조하는 컬랙션 인스턴스
Stream.of 메소드의 배열을 전달하는 경우
하나의 배열로 이뤄진 스트림이 생성되는 것이 아니라, 배열에 저장된 요소로 이뤄진 스트림 생성
DoublStream, IntStream, LongStream
static Int/Double/LongStream of -> of 메소드가 위의 인터페이스에 정의
기본 자료형 데이터로 이뤄진 스트림을 생성하면, 불필요한 오토 박싱과 언박싱을 피할 수 있다.
static Int/long range/rangeClosed -> 범위 나타냄
double이 존재하지 않는 이유는 두 실수 사이에 존재하는 값이 수없이 많기 때문이다.
import java.util.stream.IntStream;
public class Main2 {
public static void showIntStream(IntStream is) {
is.forEach(n -> System.out.println(n));
System.out.println();
}
public static void main(String[] args) {
IntStream is3 = IntStream.of(7, 5, 3);
showIntStream(is3);
IntStream is4 = IntStream.range(5,8);
showIntStream(is4);
IntStream is5 = IntStream.rangeClosed(5,8);
showIntStream(is5);
}
}
병렬 스트림으로 변경
병렬 스트림을 생성하기 원한다면, Stream parallel. 참고로 parallel 메소드는 Stream, DoubleStream과 같이 스트림을 참조할 수 있는 형 인터페이스를 상속 -> BaseStream 인터페이스의 추상 메소드
import java.util.Arrays;
import java.util.List;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
public class Main3 {
public static void main(String[] args) {
List is = Arrays.asList("Box", "Simple", "Complex", "Robot");
Stream ss = is.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);
}
}
스트림의 연결
두개의 스트림을 연결하여, 하나의 스트림을 생성
static Stream concat
두 문자열 스트림을 하나의 스트림으로 묶는 방법
import java.util.stream.Stream;
public class Main4 {
public static void main(String[] args) {
Stream s1 = Stream.of("Cake", "Milk");
Stream s2 = Stream.of("Lemon", "jelly");
Stream.concat(s1, s2)
.forEach(s -> System.out.println(s));
}
}
스트림의 중간연산
Mapping
map, mapToInt, mapToLong, mapToDouble(Function mapper)
flatMap series
stream flatMap(Function<T. Stream> mapper. Function <T, R>의 추상 메소드는 R apply(T t) 이므로, 호출 시 전달해야 할 람다식 -> Stream apply(T t)
즉 flatMap 전달할 람다식에서는 스트림을 생성하고 이를 반환. 반면 map에 전달할 람다식에서는 스트림을 구성할 데이터만 반환
import java.util.Arrays;
import java.util.stream.Stream;
public class Main5 {
public static void main(String[] args) {
Stream s1 = Stream.of("My_Age", "Your_Life");
Stream<String> s2 = s1.flatMap(s -> Arrays.stream(s.split("_")));
s2.forEach(s -> System.out.println(s));
System.out.println();
}
}
String 클래스의 다음 메소드는 인자로 전달된 기준을 근거로 문자열을 나눈고 문자열을 배열에 담아서 반환.
public String[] split(String regex)
인자로 전달된 구분자 정보를 기준으로 문자열로 나누고, 배열에 담아서 반환
Stream s2 = s1.flatMap(s -> Arrays.stream(s.split("_")));
이때 두개의 스트림을 하나로 묶은 스트림을 flatMap이 경우 다대 일로 메핑한다.
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;
class ReportCard {
private int kor;
private int Eng;
private int math;
public ReportCard(int kor, int eng, int math) {
this.kor = kor;
Eng = eng;
this.math = math;
}
public int getKor() {
return kor;
}
public int getEng() {
return Eng;
}
public int getMath() {
return math;
}
}
public class Main6 {
public static void main(String[] args) {
ReportCard[] cards = {
new ReportCard(70, 80, 90),
new ReportCard(80, 90, 100),
new ReportCard(80, 80, 80)
};
Stream<ReportCard> sr = Arrays.stream(cards);
IntStream s1 = sr.flatMapToInt(
r -> IntStream.of(r.getKor(), r.getEng(), r.getMath())
);
double arg = s1.average().getAsDouble();
System.out.println(arg);
}
}
IntStream s1 = sr.flatMapToInt(
r -> IntStream.of(r.getKor(), r.getEng(), r.getMath())
);
double arg = s1.average().getAsDouble();
System.out.println(arg);
}
IntStream형 참조변수로 참조하는 스트림을 대상으로 다음 메소드를 호출할 수 있지만, Stream 형 참조 변수로 참조하는 스트림을 대상으로는 메소드 호출 X
import java.util.Arrays;
import java.util.stream.IntStream;
class ReportCard1{
}
public class Main7 {
public static void main(String[] args) {
ReportCard[] card = {
new ReportCard(70,80,90),
new ReportCard(90,80,70),
new ReportCard(80,80,80)
};
Arrays.stream(card)
.flatMapToInt(r -> IntStream.of(r.getKor(), r.getEng(), r.getMath()))
.average()
.ifPresent(avg -> System.out.println(avg));
}
}
정렬
정렬 기능을 제공하는 중간 연산 메소드들
Stream, IntStream, DoubleStream, LongStream sorted()
import java.util.stream.Stream;
public class Main8 {
public static void main(String[] args) {
Stream.of("Box", "Apple", "Robot")
.sorted()
.forEach(s -> System.out.println(s));
System.out.println();
Stream.of("Box", "Apple", "Rabbit")
.sorted((s1, s2) -> s1.length() - s2.length())
.forEach(s -> System.out.println(s));
System.out.println();
}
}
컬랙션 인스턴스의 정렬과 같다. 스트림을 구성하는 인스턴스가 Comparable 인터페이스를 구현하고 있다.
Stream sorted
즉 스트림을 구성하는 인스턴스는 Comparable compareTo를 구현 하고 있어야 한다. 그리고 이 조건은 Collections 클래스의 sort 메소드 호출을 위한 조건과 동일. compare 메소드 구현에 해당하는 람다식을 전달.
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
public class Main9 {
public static void main(String[] args) {
IntStream.of(3, 9, 4, 2)
.sorted()
.forEach(s -> System.out.println(s));
System.out.println();
DoubleStream.of(3.3, 6.6, 7.7, 8.8)
.sorted()
.forEach(s -> System.out.println(s));
System.out.println();
}
}
루핑
모든 데이터를 각각 대상으로 특정 연산을 진행하는 것을 루핑. 루핑 연선으로 forEach
최종 연산에 하는 것과 중간 연산 forEach 차이가 있다.
최종 연산 생략
IntStream.of(1,3,5)
.peek(s -> System.out.println(s));
System.out.println();
이 스트림의 파이프라인에는 최종 연산 존재하지 않는다. 즉 중간연산은 진행되지 않는다.
최종연산 존재
IntStream.of(5,3,1)
.peek(d -> System.out.println(d))
.sum();
System.out.println();
}
sum은 스트림에 저장된 값을 모두 더하여 결과를 저장
스트림의 최종 연산
sum, count, average, min, max
import java.util.stream.IntStream;
public class Main11 {
public static void main(String[] args) {
int sum = IntStream.of(1, 3, 5, 7, 9)
.sum();
System.out.println(sum);
long count = IntStream.of(1, 3, 5, 7, 9)
.count();
System.out.println(count);
IntStream.of(1, 3, 5, 7, 9)
.average().ifPresent(av -> System.out.println(av));
IntStream.of(1, 3, 5, 7, 9)
.min()
.ifPresent(m -> System.out.println(m));
IntStream.of(1,3,5,7,9)
.max()
.ifPresent(m -> System.out.println(m));
}
}
최종연산을 하는 순간 파이프라인 마지막을 통과한다. 얻고자 하는 것이 있다면 매번 스트림을 생성해야 한다.
ForEach
foreach와 peek은 각각 최종연산과 중간연산이라는 부분에서의 차이. foreach는 최종연산이기 때문에 void, peek는 중간 연산이기 때문에 반환형이 존재.
allMatch, anyMatch, noneMatch
allMatch -> 스트림의 모든 데이터가 조건을 만족?
anyMatch -> 조건 하나라도 만족?
noneMatch -> 조건을 하나도 만족 X ?
import java.util.stream.IntStream;
public class Main12 {
public static void main(String[] args) {
boolean b = IntStream.of(1, 2, 3, 4, 5)
.allMatch(n -> n % 2 == 0);
System.out.println("짝수" + b);
boolean c = IntStream.of(1, 2, 3, 4, 5)
.anyMatch(n -> n % 2 != 0);
System.out.println("홀수" + c);
boolean d = IntStream.of(1, 2, 3, 4, 5)
.noneMatch(n -> n % 2 == 0);
System.out.println("짝수" + d);
}
}
import java.util.Arrays;
class ReportCards1 {
private int kor;
private int eng;
private int math;
public ReportCards1(int kor, int eng, int math) {
this.kor = kor;
this.eng = eng;
this.math = math;
}
public int getKor() {
return kor;
}
public int getEng() {
return eng;
}
public int getMath() {
return math;
}
}
public class Main13 {
public static void main(String[] args) {
ReportCards1[] cards = {
new ReportCards1(90, 70, 80),
new ReportCards1(70, 70, 60),
new ReportCards1(90, 70, 50)
};
boolean b1 = Arrays.stream(cards)
.mapToDouble(r -> (r.getKor() + r.getEng() + r.getMath()) / 3.0)
.anyMatch(avg -> avg >= 90);
System.out.println(b1);
if (b1 == true) {
boolean b2 = Arrays.stream(cards)
.mapToDouble(r -> (r.getEng() + r.getMath() + r.getKor()) / 3.0)
.allMatch(avg -> avg > 90);
System.out.println(b2);
}
}
}
collect
한번 파이프라인에 흘려보낸 스트림을 되돌리거나 다른 파이프라인에 다시 흘려보낼 수 없다. 필요하다면 파이프라인을 통해서 가공되고, 걸러진 데이터를 최종연산 과정에서 별도로 저장
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Main14 {
public static void main(String[] args) {
String[] words = {"Hello", "Box", "Robot", "Toy"};
Stream ss = Arrays.stream(words);
List<String> s1 = ss.filter(s -> s.length() < 5).collect(() -> new ArrayList<>(),
(c, s) -> c.add(s),
(lst1, lst2) -> lst1.addAll(lst2));
System.out.println(s1);
}
}
List s1 = ss.filter(s -> s.length() < 5).collect(() -> new ArrayList<>(),
람다식을 기반으로 데이터를 저장할 저장소를 생성
(c, s) -> c.add(s),
두번째 매개변수로 전달된 람다식
c는 collect 의 첫 인자를 통해서 생성된 컬랙션 인스턴스, s는 스트림을 이루는 데이터.
즉 컬랙션 인스턴스에 스트림 데이터가 저장
병렬 스트림에서의 collect
병렬 스트림을 대상으로 메소드 호출시, 첫 인자로 람다식을 기반으로 다수 저장소 생성. 두번째로 람다식을 기반으로 다수의 저장소에 데이터가 나뉘어 저정된다. 마지막으로 하나로 묶는다.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Main15 {
public static void main(String[] args) {
String[] words = {"Hello", "Box", "Robot", "Toy"};
Stream ss = Arrays.stream(words);
List<String> ls = ss.parallel()
.filter(s -> s.length() < 5)
.collect(() -> new ArrayList<>(),
(c,s) -> c.add(s),
(lst1, lst2) -> lst1.addAll(lst2)
);
}
}
주의 할 점은 병렬처리를 한다고 무조건 좋은 것은 아니다. 전후 과정이 더 소모적일 경우, 병렬처리가 방해가 된다. 따라서 테스트를 통해 적합성을 따져보는게 좋다.