스트림(Stream)은 여러 해 전 도입되었지만, Java 개발자들은 여전히 이 강력한 도구를 완전히 활용하지 못하고 있습니다. 이 글에서는 다음 프로젝트에 참고할 수 있는 유용한 스트림 활용 팁을 소개합니다.
아래 예제에서는 다음 클래스들을 사용할 것입니다.

| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | @Getterclass Company {
 private String name;
 private Address address;
 private List<Person> personList;
 }
 
 @Getter
 class Person {
 private Long id;
 private String name;
 }
 
 @Getter
 class Address {
 private String street;
 private City city;
 }
 
 @Getter
 class City {
 private String name;
 private State state;
 }
 
 @Getter
 class State {
 private String name;
 }
 
 | 
1. 메서드 참조를 사용하여 map 단순화하기
다음 코드는 회사들의 주소에서 도시 이름을 가져옵니다.
| 12
 3
 4
 5
 
 | public List<String> getCityNames(List<Company> companyList){return companyList.stream()
 .map(company -> company.getAddress().getCity().getName())
 .toList();
 }
 
 | 
이를 더 가독성 있게 다음과 같이 변경할 수 있습니다.
| 12
 3
 4
 5
 6
 7
 
 | public List<String> getCityNames(List<Company> companyList){return companyList.stream()
 .map(Company::getAddress)
 .map(Address::getCity)
 .map(City::getName)
 .toList();
 }
 
 | 
2. Null 체크하기
위 코드를 null 체크와 함께 작성하면 다음과 같습니다.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | public List<String> getCityNames(List<Company> companyList){return companyList.stream()
 .map(Company::getAddress)
 .filter(Objects::nonNull)
 .map(Address::getCity)
 .filter(Objects::nonNull)
 .map(City::getName)
 .filter(Objects::nonNull)
 .toList();
 }
 
 | 
3. 스트림을 단일 스트림으로 변환하기
다음 코드는 모든 회사로부터 사람 목록을 가져옵니다.
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | public List<Person> getAllPerson(List<Company> companyList){
 List<List<Person>> partialResult = companyList.stream()
 .map(Company::getPersonList)
 .toList();
 
 
 List<Person> result = new ArrayList<>();
 partialResult.forEach(result::addAll);
 
 return result;
 }
 
 | 
위와 동일한 작업을 다음과 같이 할 수 있습니다.
| 12
 3
 4
 5
 6
 
 | public List<Person> getAllPerson(List<Company> companyList){return companyList.stream()
 .map(Company::getPersonList)
 .flatMap(List::stream)
 .toList();
 }
 
 | 
4. 속성별 그룹화하기
다음 코드는 각 도시에 있는 회사 목록을 Map으로 반환합니다
| 12
 3
 4
 
 | public Map<City, List<Company>> getCompaniesByCity(List<Company> companyList){return companyList.stream()
 .collect(Collectors.groupingBy(company -> company.getAddress().getCity()));
 }
 
 | 
5. 스트림에 특정 항목이 있는지 확인하기
다음 코드는 특정 도시에 회사가 있는지 확인합니다.
| 12
 3
 4
 5
 6
 7
 
 | public boolean hasCompanyInCity(List<Company> companyList, String cityName){return companyList.stream()
 .map(Company::getAddress)
 .map(Address::getCity)
 .map(City::getName)
 .anyMatch(cityName::equals);
 }
 
 | 
특정 도시에 회사가 없는지를 확인하려면 noneMatch를 사용할 수 있습니다.
| 12
 3
 4
 5
 6
 7
 
 | public boolean hasNoCompanyInCity(List<Company> companyList, String cityName){return companyList.stream()
 .map(Company::getAddress)
 .map(Address::getCity)
 .map(City::getName)
 .noneMatch(cityName::equals);
 }
 
 | 
6. 로깅하기
각 도시 이름을 반환할 때 로그를 기록하려면 peek 메서드를 사용할 수 있습니다.
| 12
 3
 4
 5
 6
 7
 8
 
 | public List<String> getCityNames(List<Company> companyList){return companyList.stream()
 .map(Company::getAddress)
 .map(Address::getCity)
 .map(City::getName)
 .peek(cityName -> log.info(cityName))
 .toList();
 }
 
 | 
7. 고유한 도시 이름 가져오기
distinct를 사용해 스트림에서 중복된 도시 이름을 제거할 수 있습니다.
| 12
 3
 4
 5
 6
 7
 8
 
 | public List<String> getUniqueCityNames(List<Company> companyList){return companyList.stream()
 .map(Company::getAddress)
 .map(Address::getCity)
 .map(City::getName)
 .distinct()
 .toList();
 }
 
 | 
결론
이 방법들을 통해 스트림을 보다 효과적으로 사용하고 코드의 가독성과 성능을 높일 수 있습니다.