Language/JAVA

스트림의 중간, 최종연산

YoonJong 2023. 2. 15. 12:44
728x90

스트림의 연산에 대해 알아보겠습니다.

프로젝트를 진행할 때, for 문과 같은 코드를 Stream 으로 리팩토링을 했는데, 자바의 정석 강의를 통해 다시 한번 정리해보고자 합니다.

 

자바8 이후 Stream 을 사용할 수 있게 되면서, 길고 복잡했던 코드를 단순하고 가독성 좋게 사용할 수 있기 때문에,

낯설더라도 프로젝트에 적용하면 빠르게 배울 수 있을 거 같습니다.

 

먼저 어떠한 종류가 있는지 확인해보겠습니다. 기본적인 메서드만 정리했으며, 많은 메서드가 있습니다.

중간 연산은 0~n 개를 사용할 수 있습니다.

중간 연산 설명
Stream<T> distinct() 종복제거
Stream<T> filter( 조건식 ) 조건에 안맞는 요소는 제외
Stream<T> limit() 스트림의 일부를 잘라내기
Stream<T> skip() 스트림의 일부를 건너뛰기
Stream<T> sortd() 스트림의 요소 정렬
Stream<T> map() 스트림의 요소를 변환

 

최종연산의 메서드를 알아보겠습니다. 최종연산은 1개만 사용가능합니다.

최종연산 설명
void forEach() 각 요소에 지정된 작업 수행
long count() 스트림의 요소의 개수 반환
Optional<T> max()
Optional<T> min()
스트림의 최댓값 반환
스트림의 최솟값 반환
Optional<T> findAny()
Optional<T> findFirst()
스트림 요소를 하나 반환 (아무거나 하나)
스트림 요소를 하나 반환 (첫번째 요소)
boolean allMatch(Predicate<T> p)
boolean anyMatch(Predicate<T> p)
boolean noneMatch(Predicate<T> p)
요소를 만족시키는지 확인 (모두 만족)
요소를 만족시키는지 확인 (하나라도 만족)
요소를 만족시키는지 확인 (모두 만족X)
Object[ ] toArray() 스트림의 모든 요소를 배열로 반환
Optional<T> reduce 스트림의 요소를 하나씩 줄여가면서 계산
R collect 스트림의 요소 수집

 


skip() 과 limit() 의 예제입니다.

3개를 건너뛰고 5개를 출력합니다.

IntStream intStream = IntStream.rangeClosed(1, 10); // 1 ~ 10
intStream.skip(3).limit(5).forEach(System.out::print);

// 결과값
45678

 

distinct() 와 filter() 의 예제입니다.

distinct() 는 중복을 제거하고 filter() 는 괄호 안에 있는 조건에 맞는 요소를 필터링합니다.

IntStream intStream = IntStream.of(1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7);
intStream.distinct().forEach(System.out::print);
// 결과값
1 2 3 4 5 6 7 

IntStream intStream = IntStream.rangeClosed(1, 10); // 1 ~ 10
intStream.filter(i -> i % 2 == 0).forEach(System.out::print);
// 결과값 
2 4 6 8 10

IntStream intStream = IntStream.rangeClosed(1, 10); // 1 ~ 10
intStream.filter(i -> i % 2 == 0).filter(i -> i % 3 == 0).forEach(System.out::print);
// 결과값
6

sorted() 의 예제입니다. 괄호안에 정렬기준을 설정할 수 있습니다.

Stream<String> strStream = Stream.of("dd", "aa", "bc", "bb");
strStream.sorted((o1, o2) -> o1.compareTo(o2)).forEach(System.out::print);
// 결과값
aabbbcdd

Stream<String> strStream = Stream.of("dd", "aa", "bc", "bb");
strStream.sorted((o1, o2) -> o2.compareTo(o1)).forEach(System.out::print);
//strStream.sorted(Comparator.reverseOrder()).forEach(System.out::print);
// 결과값
ddbcbbaa

map() 을 사용하는 예제입니다. File 타입의 객체를 String 타입으로 변환할 수 있습니다.

File[] fileArr = {new File("Ex1.java"),
        new File("Ex1.bak"),
        new File("Ex2.java"),
        new File("Ex1"),
        new File("Ex1.txt")};

Stream<File> fileStream = Stream.of(fileArr);

// map() 으로 Stream<File> 을 Stream<String> 으로 변환
Stream<String> filenameStream = fileStream.map(f -> f.getName());
filenameStream.forEach(System.out::println);

//결과값
Ex1.java
Ex1.bak
Ex2.java
Ex1
Ex1.txt

최종연산 (1번만 사용가능) 에 대해 알아보겠습니다.

 

먼저 가장 기본적인 forEach() 입니다. 루프를 들면서 출력합니다.

IntStream.range(1,10).forEach(System.out::print);
IntStream.range(1,10).parallel().forEach(System.out::print);    // 병렬 처리 -> 순서보장X
IntStream.range(1,10).parallel().forEachOrdered(System.out::print); // 병렬 처리 시 순서보장

//결과값
123456789
657948213
123456789

 

anyMatch() 입니다. 괄호안에는 조건식이 들어가며, 아래는 100 점 이하의 학생이 있는지 확인하는 예제입니다.

boolean hasFailedStu = studentStream.anyMatch( s-> s.getTotalScore() <= 100);

// 결과값
존재하면 true

 

findFirst() 와 findAny() 의 사용예제입니다. filter 조건이랑 같이 사용됩니다.

findFirst 는 앞에서부터 하나씩 조회하며 필터조건에 의해 찾으면 그 값을 반환(첫번째 요소) 합니다.

값이 없을 수도 있기 때문에 Optional 입니다.

 

findAny() 는 병렬처리 시 사용합니다. 

필터조건에 맞는 값을 반환하며, 병렬 처리 시 나누어 찾기 때문에 Any를 사용합니다.

값은 먼저 찾는 값을 반환합니다.

Optional<Student> result = studentStream.filter( s-> s.getTotalScore() <= 100).findFirst();
Optional<Student> result = paralleStream.filter( s-> s.getTotalScore() <= 100).findAny();

 

reduce() 는 요소를 하나씩 줄여가며 누적연산을 수행합니다.

Optional<T> reduce (BinaryOperator<T> accumulator) : 초기값이 없으니 null 이 반환 될 수 있습니다.

T reduce(T identity, BinaryOperator<T> accumulator) : 초기값을 설정하며, 실행되지 않으면 초기값을 반환합니다.

초기값을 설정해주고, 실행할 연산을 적어줍니다.

int count = intStream.reduce(0, (a,b) -> a+1);	//count
int sum = intStream.reduce(0, (a,b) -> a+b);	//sum
int max = intStream.reduce(Integer.MIN_VALUE, (a,b) -> a > b ? a : b); //max
int min = intStream.reduce(Integer.MAX_VALUE, (a,b) -> a < b ? a : b); //min

sum 의 코드는 풀어서 작성하면 아래와 같습니다.

int a = identity;
for(int b : stream)
	a = a + b;

 

최종연산 reduce() 의 예제를 총 예제로 알아보겠습니다.

String[] strArr = {"aaa", "bbbb", "cc", "dd", "e"};

IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream2 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream3 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream4 = Stream.of(strArr).mapToInt(String::length);

int count = intStream1.reduce(0, (a, b) -> a + 1);
int sum = intStream2.reduce(0, (a, b) -> a + b);
OptionalInt max = intStream3.reduce(Math::max);
OptionalInt min = intStream4.reduce(Math::min);


System.out.println("count = " + count);
System.out.println("sum = " + sum);
System.out.println("max = " + max.getAsInt());
System.out.println("min = " + min.getAsInt());

//결과값
count = 5
sum = 12
max = 4
min = 1

 

collect() 는 그룹별로 리듀싱할때 사용합니다.

Object collect(Collerctor collector)

List<String> names = studentStream.map(Student::getName).collect(Collectors.toList());
ArrayList<String> list = names.stream().collect(Collectors.toCollection(ArrayList::new));
Map<String,Person> map = personStream.collect(Collectors.toMap(p->p.getRegId(), p->p));

 

728x90