Stream (2) - 자주 사용하는 메소드(중간 연산 메소드)
업데이트:
이전 포스트에서 Stream의 기본적인 내용에 대해 알아봤습니다.
이번에는 자주 사용하는 Stream의 메소드에 대해서 알아보겠습니다.
💡참고로 Stream에서 사용하는 메소드는
Lambda
나Method Reference
,Functional Interface
를 사용합니다. 이 중에서도 주로Lambda
를 사용합니다. 이는 다른 포스팅에서 함께 다뤄보도록 하겠습니다~
중간 연산 메소드 (Intermediate Operation Method)
이전 포스트에서 Stream은 총 2가지 메소드로 나눠지는 것을 알려드렸었는데요, 먼저 자주 사용하는 중간 메소드에는 무엇이 있는지 알아보겠습니다.
filter()
filter()
는 조건에 맞는 요소를 걸러주는 역할을 합니다.
중간 연산 메소드인 만큼 Stream
을 반환하기 때문에 이후에 다른 중간 연산, 최종 연산이 가능합니다.
다음은 5명의 이름에서 5글자 이상인 사람의 이름을 필터링하는 코드입니다.
@Test
void filter() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.filter(name -> name.length() > 5)
.forEach(System.out::println);
}
결과는 위와 같이 jordan, iverson, curry가 출력된 것을 확인할 수 있습니다.
map()
map()
의 생김새는 다음과 같습니다.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
map()
은 요소의 기존 타입을 다른 타입으로 변환 시켜주는 메소드입니다.
물론 원래 타입을 그대로 유지해도 됩니다. 타입을 그대로 유지한다?
즉, 타입은 유지하되 원래 요소의 무언가 작업을 진행한다는 뜻이겠죠?
다음은 5명의 이름 글자 수로 변환하여 출력하는 코드입니다.
@Test
void map() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.map(name -> name.length())
.forEach(System.out::println);
players.stream()
.map(name -> name + name.length())
.forEach(System.out::println);
}
다음은 이름과 글자 수를 합쳐 출력하는 코드입니다.
@Test
void map2() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.map(name -> name + name.length())
.forEach(System.out::println);
}
flatMap()
제가 Stream을 공부하면서 제일 헷갈렸던 메소드입니다 🤣
“요소를 단일 Stream으로 변환해준다.”라는 어려운 설명이 있지만 flatMap()
은 결국 2차원 배열, 혹은 2중 컬렉션을 반복하여 작업하는 행위를 한다고 생각해주시면 쉽게 이해가 되실겁니다.
flatMap()
의 생김새는 다음과 같습니다.
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
위의 map()
과 차이점을 확인해보면, 2번째 인자로 Stream의 하위 타입이 와야됨을 확인 할 수 있습니다.
다음은 2중 컬렉션을 하나의 컬렉션으로 취합하는 코드입니다.
@Test
void flatMap() {
List<List<String>> allPlayers = new ArrayList<>();
List<String> players1 = List.of("jordan", "kobe");
List<String> players2 = List.of("iverson", "wade");
List<String> players3 = List.of("curry", "lebron");
allPlayers.add(players1);
allPlayers.add(players2);
allPlayers.add(players3);
List<String> collect = allPlayers.stream()
.flatMap(players -> players.stream())
.collect(Collectors.toList());
collect.stream()
.forEach(System.out::println);
}
이 밖에도 여러 과목의 시험을 친 학생들의 총 평균을 구하는 좋은 예제가 있는 포스트가 있어 소개해드리겠습니다.
[Java] Stream API의 활용 및 사용법 - 고급 (4/5)
💡
map()
과flatMap()
은 각각 타입을 정해놓은mapToInt()
,flatMapToInt()
같은 메소드가 있습니다. 이를 활용하면 더 확실하게 타입 변환을 할 수 있습니다.
peek()
peek()
은 최종 연산 메소드인 forEach()
같이 반환하지 않는(void
)인 작업을 하는 메소드입니다.
대표적인 예로 System.out.println()
이 있습니다.
주로 다른 여러 작업 사이의 요소 값을 확인하기 위해 사용합니다.
❗주의할 점은 forEach()
와 달리 중간 연산이기 때문에 최종 연산이 없으면 실행되지 않습니다.
@Test
void peek() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.peek(System.out::println);
}
위와 같이 최종 연산을 하지 않은 상태에서 실행하면 아무런 값도 출력되지 않습니다.
이번엔 5명 중 이름의 길이가 5글자 이상인 사람들을 peek()
으로 확인하고 필터링된 사람의 수를 구하는 코드를 작성해봤습니다.
@Test
void peek() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
long count = players.stream()
.filter(name -> name.length() >= 5)
.peek(System.out::println)
.count();
System.out.println("count = " + count);
}
결과를 보면 peek()
이 제대로 사용된 것을 확인할 수 있습니다.
sorted()
sorted()
는 이름에서 바로 알 수 있듯이 요소를 정렬해주는 메소드입니다.
sorted()
에서 제공하는 건 2개가 있습니다.
하나는 인자가 없는 메소드드와 Comparator
를 인자로 받는 sotred()
가 있습니다.
먼저 sorted()
의 설명을 보면
natural order, 즉 자연순에 따라 정렬된다고 나와있습니다.
보통 우리가 사용하는 java.util.*
의 Arrays
나 Collections
의 natural order는 Ascending(오름차순)입니다.
문자열 정렬
@Test
void string_sorted() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream().sorted()
.forEach(System.out::println);
}
숫자 정렬
@Test
void integer_sorted() {
List<Integer> numbers = List.of(4, 5, 1, 3, 2);
numbers.stream().sorted()
.forEach(System.out::println);
}
결과에서 확인할 수 있듯이 오름차순 정렬된 것을 확인할 수 있습니다.
다음은 Comparator
를 인자로 받는 Stream<T> sorted(Comparator<? super T> comparator)
입니다.
정렬 방법을 Comparator
를 정의하여 넘겨주는 것인데, 간단하게 Comparator.reverseOrder()
를 사용하여 역순으로 정렬해보도록 하겠습니다.
@Test
void reverse_sorted() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);
}
아까와는 반대로 정렬된 것을 확인할 수 있습니다.
distinct()
distinct()
역시 이름에서 알수 있듯이 중복제거 입니다.
같은 이름을 2번 사용했을때, 중복을 제거하고 결과를 출력하는 것을 확인할 수 있습니다.
@Test
void distinct() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry", "jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.distinct()
.forEach(System.out::println);
}
limit()
limit()
는 결과 중 몇개만 출력하겠다는 메소드입니다.
@Test
void limit() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.limit(3)
.forEach(System.out::println);
}
결과를 보면 무작위로 3개를 출력하는 것이 아니라 최종 결과물에서 3개까지만 출력하는 것입니다.
skip()
skip()
은 limit()
와 달리 말 그대로 몇개를 건너뛰고 최종 결과물을 출력하겠다는 뜻입니다.
바로 코드를 보겠습니다.
@Test
void skip() {
List<String> players = List.of("jordan", "kobe", "iverson", "wade", "curry");
players.stream()
.skip(3)
.forEach(System.out::println);
}
아까 limit()
코드와 똑같지만 limit()
→ skip()
으로 바꼈습니다.
결과물은?
이번엔 limit()
과 달리 3개를 건너뛰고 남은 2개 결과만 출력되었습니다.
마무리
오늘은 Java Stream에서 자주 사용하는 메소드 중에서도 중간 연산 메소드를 확인해봤습니다.
원래는 최종 연산 메소드까지 한번에 하려했는데 생각보다 양이 꽤 돼네요. 😢
한 포스트에서 다뤄보기엔 보기에도 지루하실꺼고 저도 힘들고해서 포스트를 나누게 되었습니다.
다음 포스트에서 최종 연산 메소드를 다뤄보도록 하겠습니다
오늘도 제 포스트를 봐주셔서 감사합니다.😄
댓글남기기