📝Stream
Java8에서 나온 것으로 매개변수들이 함수형 인터페이스 기반으로 만들어져 Stream에서 쓰는 내부 함수들은 Predicate, Function처럼 함수형 인터페이스를 사용한다
// Stream 인터페이스 내부 함수
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Stream 장점으로는 Lazy Evaluation으로 이 의미는 즉, 불필요한 연산을 피한다는 의미이다.
배열의 값을 하나씩 읽어서 순차적으로 진행시킨다. 밑에 코드에서 보는 것 처럼 i < 6이라는 조건절이 있는 경우 1 ~ 10까지 i < 6처리한 후에 남은 애들이 2번째 조건 절인 i % 2 == 0을 거치는게 아니라 "1"이 i < 6을 거친 후 i % 2 == 0을 거치는 순차적인 Iterator로 돌아가게 된다.
Stream 동작 트래킹
@Test
public void test(){
final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(
list.stream()
.filter(i -> {
System.out.println(i + " : i < 6");
return i<6;
})
.filter(i -> {
System.out.println(i + " : i % 2 == 0");
return i%2==0;
})
.collect(Collectors.toList())
);
/**
* 1 : i < 6
* 1 : i % 2 == 0
* 2 : i < 6
* 2 : i % 2 == 0
* 3 : i < 6
* 3 : i % 2 == 0
* 4 : i < 6
* 4 : i % 2 == 0
* 5 : i < 6
* 5 : i % 2 == 0
* 6 : i < 6
* 7 : i < 6
* 8 : i < 6
* 9 : i < 6
* 10 : i < 6
* [2, 4]
*/
}
여기서 예제를 보면 1 ~ 10을 1번 필터를 다 거친 후 남은 걸 2번 필터를 거치거나 각 Iterator들을 개별로 1번 → 2번을 거치거나 별 차이는 없어보인다 하지만 밑에 같은 예제의 경우 이야기가 달라진다. findFirst로 1개의 정상 값만 나오면 바로 종료시켜버린다 물론 IF로직같은 걸로 1개 일 때 종료하던가 로직 처리가 가능하지만 가독성 및 효율성이 떨어진다.
Stream 동작 트래킹
@Test
public void test(){
final List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(
list.stream()
.filter(i -> {
System.out.println(i + " : i < 6");
return i<6;
})
.filter(i -> {
System.out.println(i + " : i % 2 == 0");
return i%2==0;
})
.findFirst()
);
/**
* 1 : i < 6
* 1 : i % 2 == 0
* 2 : i < 6
* 2 : i % 2 == 0
*/
}
동작 방식을 이제 알아봤으니 이걸 이용해 익숙해져보자
Stream 사용해보기
package hello.exception;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static java.util.stream.Collectors.*;
public class StreamTest {
@Test
public void intStreamTest(){
/** ---- 연속적인 값 할당 ---- **/
IntStream.range(0, 10).forEach(i -> System.out.println("range : " + i)); // range : 0 range : 1 .... range : 9
// IntStream.iterate(1, i -> i + 1).forEach(i -> System.out.println("iterate : " + i)); // iterate : 1 ... iterate : 21
IntStream.rangeClosed(0, 10).forEach(i -> System.out.println("rangeClose : " + i)); // rangeClose : 0 rangeClose : 1 ... rangeClose : 10
/** ---- Sum ---- **/
long sum = IntStream.of(1, 2, 3, 4, 5)
.sum();
System.out.println("sum : " + sum); // sum : 15
}
@Test
public void streamTest(){
/** ---- of, forEach ---- **/
Stream.of(1, 2, 3, 4, 5)
.forEach(i -> System.out.println("stream of : " + 1)); // stream of : 1 ... stream of : 5
}
@Test
public void streamChaningTest(){
/** ---- stream chaning ---- **/
final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Optional<Integer> result =
numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
.filter(number -> number > 3) // 4, 5, 6, 7, 8, 9, 10
.filter(number -> number < 9) // 4, 5, 6, 7, 8
.map(number -> number * 2) // 8, 10, 12, 14, 16
.filter(number -> number < 15) // 8, 10, 12, 14
.findAny(); // 8
System.out.println("stream result : " + result.get());
// Intermediate Operation Method [Stream 리턴 -> chaning 가능]
// Terminated Operation Method [Stream 외 리턴]
}
@Test
public void streamChaningTest2(){
final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/** ---- collection(toList()) ---- **/
List<String> result = numbers.stream()
.filter(number -> number > 8) // 9, 10
.map(number -> "#" + number)
.collect(toList());
System.out.println("result : " + result); // result : [#9, #10]
/** ---- collection(joining) ---- **/
String result2 = numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
.filter(number -> number > 8) // 4, 5, 6, 7, 8, 9, 10
.map(number -> "#" + number)
.collect(joining(",","[","]"));
System.out.println("result2 : " + result2); // result2 : [#9,#10]
/** ---- collection(toSet()) ---- **/
Set<String> result3 = numbers.stream() // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
.filter(number -> number > 8) // 4, 5, 6, 7, 8, 9, 10
.map(number -> "#" + number)
.collect(toSet());
System.out.println("result3 : " + result3); // result3 : [#10, #9]
/** ---- distinct ---- **/
List<String> result4 =
Stream.of(1, 3, 3, 5, 5)
.filter(number -> number > 2)
.map(number -> number * 2)
.map (number -> "#" + number) // [#6, #6, #10, #10]
.distinct() // [#6, #10]
.collect(toList());
System.out.println("result4 : " + result4); // result4 : [#6, #10]
/** —— count —— **/
long result5 =
Stream.of(1, 3, 3, 5, 5)
.filter(number -> number > 2)
.map(number -> number * 2)
.map (number -> "#" + number) // [#6, #6, #10, #10]
.distinct() // [#6, #10]
.count();
System.out.println("result5 : " + result5); // result5 : 2
}
@Test
public void reduceTest(){
/** —— reduce —— **/
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer result =
numbers.stream()
.reduce(100, (x, y) -> {
System.out.println("x : " + x + ", y :" + y);
return x+y;
});
// reduce : 반복된 패턴을 추상화 할 수 있다.
System.out.println("result : " + result);
/**
* x : 100, y :1
* x : 101, y :2
* x : 103, y :3
* x : 106, y :4
* x : 110, y :5
* x : 115, y :6
* x : 121, y :7
* x : 128, y :8
* x : 136, y :9
* x : 145, y :10
* result : 155
*/
}
}
🔗 참고 및 출처
https://dororongju.tistory.com/137