개발그래머
'filter()' and 'map()' can be swapped 본문
발견
- 코드를 리팩터링하며 보인 문구이며 왜 이 문구가 발생했는지 궁금하여 찾아보게되었다.
과정
List<String> emails = communityUserRepository.findByCommunityId(1L).stream()
.filter(communityUser -> communityUser.getCreatedUser().getEmail() != null)
.map(communityUser -> communityUser.getCreatedUser().getEmail()).collect(Collectors.toList());
- 해당 커뮤니티에 해당하는 유저들을 가져오는 로직에서 'filter()' and 'map()' can be swapped 라는 문구가 인텔리제이에서 발생한것이다
List<String> emails = communityUserRepository.findByCommunityId(1L).stream()
.map(communityUser -> communityUser.getCreatedUser().getEmail())
.filter(email -> email != null).collect(Collectors.toList());
- 인텔리제이가 권장하는 방법으로 수정하면 위와 같은 로직이 나오게된다.
- 먼 차이가 있길래 인텔리제이에서는 위와 같은 방법을 권장하는지 궁금하게 되어 검색을 하게 되었다.
해결
`communityUser.getCreatedUser().getEmail()` is invoked twice - once inside the filter and it is also the mapping function, so instead of doing this twice, it would be better to first map it using `getCreatedUser().getEmail()` and then filter it out
`communityUser.getCreatedUser().getEmail()`은 두 번 호출됩니다. 한 번은 필터 내부에 있고 매핑 함수이기도 하므로 이 작업을 두 번 수행하는 대신 먼저 `getCreatedUser().getEmail( )`을 사용하여 매핑한 다음 필터링하는 것이 좋습니다.
https://stackoverflow.com/questions/66979712/filter-and-map-can-be-swapped
- 위와 같은 문구가 올라와 있는 스택오버플로어 글을 발견하였고 객체의 메서드를 여러번 호출하는 것보다는 미리 한번만 선언하여 매핑하고 나머지 후에 로직을 처리하는 것이 성능면에서 유리하다고 나와있었다.
- 물론 엄청나게 많은 데이터가 아니라 두개의 순서가 바뀐다고 해서 크게 영향을 받을 것같진 않았지만 궁금해서 찾아보게 되었다.
검증
public class FilterTest {
List<User> users = new ArrayList<>();
@BeforeEach
void setUp() {
for (int i = 0; i < 2000000; i++) {
if(i % 3 == 0) {
users.add(new User("요환"+i, null));
} else {
users.add(new User("요환"+i, "dyghks7102@naver.com"));
}
}
}
@Test
void filterMapTest() {
long beforeTime = System.currentTimeMillis();
List<String> emails = users.stream()
.filter(user -> user.getEmail() != null)
.map(user -> user.getEmail())
.collect(Collectors.toList());
System.out.println(emails.size());
long afterTime = System.currentTimeMillis();
long diffTime = afterTime - beforeTime;
System.out.println("실행 시간(ms): " + diffTime);
}
@Test
void mapFilterTest() {
long beforeTime = System.currentTimeMillis();
List<String> emails = users.stream()
.map(user -> user.getEmail())
.filter(email -> email != null)
.collect(Collectors.toList());
System.out.println(emails.size());
long afterTime = System.currentTimeMillis();
long diffTime = afterTime - beforeTime;
System.out.println("실행 시간(ms): " + diffTime);
}
class User {
String name;
String email;
public User(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
}
- 간략하게 테스트 코드를 짜서 성능차이가 얼마나 있나 검증을 해보았다.
- 몇천건 정도에는 의미있는 차이가 없었지만 백만건이 넘어가는 부분에서는 의미있는 차이를 보여주기 시작했다.
- 평균적으로 map을 하고 난 후 filter를 하는게 10ms정도 더 빠르게 종료되었다.
- 성능이 중요하면 메서드의 호출을 얼마나 하나도 신경써야하는 부분 중 하나일 것 같다.
'Java' 카테고리의 다른 글
Java WatchService를 이용하여 파일 감지하기 (0) | 2024.04.28 |
---|---|
[자바스터디 번외] 문자열, 콜렉션, 스트림 (0) | 2023.09.24 |
[자바스터디 15주차] 람다식 (0) | 2023.07.24 |
[자바스터디 14주차] Generic (0) | 2023.07.18 |
[자바스터디 13주차] I/O (0) | 2023.07.10 |