티스토리 뷰

Java

Java8 New features #3 (Stream)

§무명소졸§ 2020. 8. 19. 16:24

랜덤한 int를 원소로 같는 리스트가 있다. 이 리스트에 원소들의 합을 구하기 위해서는 보통은 아래와 같은 코드로 작성할 것이다.

List<Integer> numbers = Lists.newArrayList(11, 21, 45, 69, 10, 24, 12, 51, 22, 31, 20);

int sum = 0;
for (Integer number : numbers) {
	sum += number;
}

하지만 자바 8+ 에서는 아래와 같은 코드 작성이 가능하다.

 int sum = numbers.stream()
 		.mapToInt(Integer::intValue)
		.sum();

 

어쩌면 더 복잡하게 느껴질 수도 있는 코드이다. 뭐 어쨌든 위와 같은 코드 형태가 자바 8에서 소개된 Stream API이다. Collection처럼 연속된 데이터 객체를 쉽게 처리할 수 있고 원하는 결과를 만들기 위해 파이프라인 형태의 다양한 API를 제공한다. 이제 몇가지 예제를 통해 사용법을 알아보자.

filter

메서드 이름에서 충분히 유추할 수 있다. Collection 에 원소중에 조건에 만족하는 filtering한다. 조건은 filter 람다 함수를 이용해 인수로 전달한다.

 List<String> colors = Lists.newArrayList("BLACK", "RED", "BLUE", "RED", "BLUE", "GREEN", "RED", "RED", "BLACK");

 List<String> filterColors = colors.stream()
                .filter(color -> {
                    if ("BLACK".equals(color)) {
                        return true;
                    }
                    return false;
                }).collect(Collectors.toList());

 System.out.print(filterColors);  //[BLACK, BLACK]

 

map

map 은 각 원소들의 형태를 변경해준다. Integer -> String 으로 같은 간단한 type 변환 뿐만 아니라 Map 함수에 람다 인수에서 여러가지 작업을 통해 아에 새로운 값의 형태로 변환이 가능하다. 아래 코드는 Collection 의 문자열 원소를 가문자열의 길이로 변환한다.

List<String> colors = Lists.newArrayList("BLACK", "RED", "BLUE", "RED", "BLUE", "GREEN", "RED", "RED", "BLACK");

final List<Integer> collect = colors.stream()
                .map(color -> {
                    return color.length();
                }).collect(Collectors.toList());

System.out.println(collect); //[5, 3, 4, 3, 4, 5, 3, 3, 5]

 

위에서 람다식 부분은 메서드레퍼런스를 이용하면 아래와 같이 간결하게 만들 수 있다.

.map(String::length)
.collect(Collectors.toList());

 

sorted

collection의 원소들을 정렬하는 메서드이다. 아래는 map 예제의 Collection 원소들의 문자열 길이순으로 정렬하는 예제 코드이다.

 List<String> colors = Lists.newArrayList("BLACK", "RED", "BLUE", "RED", "BLUE", "GREEN", "RED", "RED", "BLACK");

        final List<String> collect = colors.stream()
                .sorted((before, current) -> {
                    if (before.length() > current.length()) {
                        return 1;
                    }
                    return -1;
                })
                .collect(Collectors.toList());

       System.out.println(collect); //[RED, RED, RED, RED, BLUE, BLUE, BLACK, GREEN, BLACK]

 

sorted 메서드의 인수값은 Comparator 인터페이스의 람다 표현식인데 before 는 앞의 원소이도 current는 현재 원소이다. 앞에 원소의 길이가 현재 원소의 길이보다 클 경우 1을 반환한다. 여기서 음수 반환은 꼭 -1이 아닌 어떠한 음수도 반환할 수 있다. 아래와 같이 변경할 수 있다.

.sorted((before, next) -> {
	return before.length() - next.length();
})

여기서 더 Comparator 의 static 메서드를 이용하면 한줄로 표현이  가능하다.

 .sorted(Comparator.comparingInt(String::length))

 

Chaining

위 예제로 작성한 몇 가지 메서드는 체이닝 형태의 코드로 작성할 수 있다.  아래는 그 예시이다.

     final List<Integer> collect = colors.stream()
                .filter("BLACK"::equals)
                .sorted(Comparator.comparingInt(String::length))
                .mapToInt(String::length)
                .boxed()
                .collect(Collectors.toList());

 

boxed() 는 원시 타입인 int 를 Wrapper 클래스인 Integer 로 변경한다. 위에 코드를 보면 필터링 부터 소팅 타입 변경까지 꽤 많은 작업을 하고 있다. 그러나 Stream 을 이용해서 몇 라인의 간결한 코드 작성이 가능하다. 또한 Stream 에는 parallel 라는 메서드도 제공하는 동시성 문제에 대한 고민 없이 멀티 쓰레딩 작업을 할수 있다. 외에  많은 기능의 메서드를 제공하고 있는데  이를 이용해 가독성있고 간결한 코드를 작성할 수 있을 것이다. 물론 성능 최적화나 제대로 사용하기 위해서 많은 연습이 필요할 것이다.

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크