본문 바로가기
SPRING

[JAVA] stream을 활용해보자

by 킹명주 2023. 7. 14.

취직하고 4개월 만에 다시 기술 블로그를 오픈해보고자 한다..!! 입사하자마자 신규 프로젝트에 투입되어서 할 일이 너무 많아서 잠시 자기계발에 손을 놨던 것 같다. 지금 회사로 만족할 수 없는 킹명주의 여정이 다시 시작된다 후,,,

 

이번에 신규 프로젝트를 하면서 for문을 많이 사용했는데, for 문에 대한 가독성 문제나 스타일 등에 대해 지적을 많이 받았다 ㅠㅠ,, 그래서 찾아보았던 것이 stream이였다.

 

그래서 이번에는 stream 활용에 대해 소개하고 예전에 진행했던 프로젝트에도 stream을 활용하고자 한다.

 


 Stream 너 뭐세요?

 

사실 내 블로그가 인기가 원래 없긴 했는데 GPT의 급 성장으로 인해 더 인기가 없어졌다 ㅠㅠ,, 그래서 GPT에게는 없는 정보가 무엇일까? 생각 해봤는데 없다 ㅎㅎ,, 그래서 재미있는 글 솜씨로 승부보기로 했다.

 

stream이란 무엇일까?

JAVA 8에서 도입된 새로운 기능인데 사실 언제 도입됐는지는 알 필요가 없다. ㅎㅎㅎㅎㅎ 

한 줄로 정의 하면 Collection, Array 같은 시퀀스 요소를 간결하고 가독성 있게 도와주는 기능이라고 생각하면 된다.

 


Stream 연산

 

Stream을 사용하기 전에 어떤 연산을 가지고 있는지 알아보자!!

 

Filtering

필터는 시퀀스 요소 하나씩 filter 처리하는 연산이다. 예제를 본다면 금방 이해갈 수 있을 것이다.

List<String> names = Arrays.asList("김명주", "이브이", "추사랑", "김네카라쿠배");
List<String> kim_names = new ArrayList<>();

names.stream()
     .filter(name -> name.contains("김"))
     .forEach(kim_names::add);

filter안의 구문을 해석해보면 이름에 "김" 이 들어가 있는 경우 filter 처리하는 것을 알 수 있다.

만약 이것을 for문으로 나타낸다면?

List<String> names = Arrays.asList("김명주", "이브이", "추사랑", "김네카라쿠배");
List<String> kim_names = new Array
for(String name : names){
	if(name.contains("김")){
		kim_names.add(name);
    }
}

 

Mapping

map은 스트림 요소 하나씩 처리하는 연산이다. 예제를 통해 살펴보자.

List<String> names = Arrays.asList("Kim myeong ju", "test", "TEST2", "Oh no");

List<String> upperCaseNames = names.stream()
                            	   .map(String::toUpperCase)
                            	   .collect(Collectors.toList());

name이 들어있는 리스트를 순회하면서 모든 문자를 대문자로 바꿔주는 코드이다.

만약이 이것을 for문으로 나타낸다면?!

List<String> names = Arrays.asList("Kim myeong ju", "test", "TEST2", "Oh no");

List<String> upperCaseNames = new ArrayList<>();
for (String name : names) {
    upperCaseNames.add(name.toUpperCase());
}

둘 중에 가독성이 뭐가 좋은가?! 라고 물어보면 해당 코드만 가지고는 알 수 없다!!!

그런데 현업에서 계속해서 for문을 써서 사용하다보니,, 이 코드가 뭐였더라?? 이런 경우가 많았다. 이 측면에서는 stream이 효율적이였다.

 

(flatMap도 있는데 갠적으로 많이 사용하지 않아서 이번 글에서  skip~~)

 

Sort

sort는 말 그대로!! 정렬해주는 함수이다. 이때 default 값은 오름차순이다.

 List<String> names = Arrays.asList("Kim myeong ju", "test", "TEST2", "Oh no");


// 오름차순
 List<String> sortedNames = names.stream()
                                 .sorted()
                                 .collect(Collectors.toList());
 
 // 내림차순
 List<String> sortedNames = names.stream()
                	    	 .sorted(Comparator.reverseOrder())
                	    	 .collect(Collectors.toList());

해당 코드의 결과는 [Kim myeong ju, Oh no, TEST2, test] 이런식으로 출력될 것이다.

해당 코드는 뭔가 딱 명확하다는 느낌이 든다. 해석해보면 names를 오름차순으로 정렬하여 sortedNames list로 새로 저장하는 것이다.

 

Distinct

SQL을 자주 만지신 분이라면 바로 어떤 함수인지 감이 올 것이다. 바로 중복 값을 제거해주는 것이다. 

따로 사용법은 소개하지 않고 설명만으로 넘어가겠다!

 

forEach

forEach를 요즘 많이 쓰는데, 해당 구문은 그냥 for문과 동일하다고 생각하면 된다.

 List<String> names = Arrays.asList("Kim myeong ju", "test", "TEST2", "Oh no");


 names.stream()
      .map(String::toLowerCase)  // 소문자로 변환
      .forEach(System.out::println);  // 출력

해당 구문은 모든 문자열을 소문자로 변환하고 forEach문을 통해 print 하는 구문이다

이것을 for문으로 나타낸다면

 List<String> names = Arrays.asList("Kim myeong ju", "test", "TEST2", "Oh no");

 for (String name : names) {
      String lowerCaseName = name.toLowerCase(); // 소문자로 변환
      System.out.println(lowerCaseName); // 출력
 }

이런식으로 될것이다. 개인적으로  forEach를 알고난 후 for문을 많이 사용하지 않고있다 ㅎㅎ,,,

 

anyMatch / allMatch / noneMatch

잘 사용하지는 않지만, 값이 특정 조건을 만족하는가를 판정한다. 조건에 따라 true / false 값으로 반환된다.

any는 하나라도 만족하면 true, all은 모두 만족하면 true, none은 하나도 만족하지 않는다면 true를 반환한다.

List<String> names = Arrays.asList("Kim myeong ju", "test", "TEST2", "Oh no");

boolean anyTest = names.stream()
                       .anyMatch(name -> name.equals("test")); // true
                       
boolean allTest = names.stream()
                       .allMatch(name -> name.equals("test")); // false

boolean noneTest = names.stream()
                       .noneMatch(name -> name.equals("test")); // false

 

 

toArray

toArray는 stream 처리 결과를 배열로 변환하는 것이다. (많이 사용하지는 않음 -> .collect() 참고)

 

소개한 함수 외에도 min/max, count 등이 있지만, 필자가 현업에서 사용하거나 유용한 것을 우선순위로 설명을 했다.


이번에는 refactoring, clean code 관점으로 stream에 대해 알아보았다. 꼭 stream 기능을 도입해보고 괜찮다면 코드에 바로 적용해보시길 권장한다.

 

 

 

 

 

'SPRING' 카테고리의 다른 글

[Spring] Static Class, Custom Builder 소개  (4) 2023.07.22
[Spring] FeignClient 란 무엇일까?  (2) 2023.07.17
[Spring] CORS 쉽게 처리하기  (4) 2023.02.22
[Spring] AOP 맛보기  (7) 2023.02.21
[Spring] @Setter vs @Builder  (2) 2023.02.20