스트림 Stream
- 컬렉션 형태로 구성된 데이터를 람다를 이용해 간결하고 직관적인 프로세스를 가능하게 한다.
- 스트림을 쓰면 사실 for, while을 안쓰게 됨
- 손쉽게 병렬 처리도 가능
String Stream을 만드는 예시
Stream<String> nameStream = Stream.of("Alice", "Bob", "Charlie");
스트림에 담긴 names를 List로 담기
List<String> collect = nameStream.collect(Collectors.toList());
배열을 스트림으로 만들 수도 있다.
String[] cityArray = new String[]{"Seoul", "tokyo", "busan"};
Stream<String> cityStream = Arrays.stream(cityArray);
컬렉션을 stream으로 만들기
Set<Integer> numberSet = new HashSet<>(Arrays.asList(3, 5, 7));
Stream<Integer> stream = numberSet.stream();
Stream Filter
만족하는 데이터만 걸러내는데 사용
Predicate true를 반환하는 데이터만 존재하는 stream을 리턴
Stream<T> filter(Predicate<? super T> predicate);
양수를 필터링하는 스트림 필터를 만들어보자
Stream<Integer> numberStream = Stream.of(3, -5, 7, 10, -3);
Stream<Integer> integerStream = numberStream.filter(x -> x > 0);
User 검증 여부를 체크하는 스트림을 만들어보자.
User user1 = new User().setId(101).setName("Alice").setVerified(true).setEmailAddress("123@123.com");
User user2 = new User().setId(102).setName("Bob").setVerified(false).setEmailAddress("123@123.com");
User user3 = new User().setId(103).setName("Chalie").setVerified(false).setEmailAddress("123@123.com");
List<User> users = Arrays.asList(user1, user2, user3);
List<User> verifiedUsers = users.stream().filter(User::isVerified).collect(Collectors.toList());
public class User {
private int id;
private String name;
private String emailAddress;
private boolean isVerified;
private List<Integer> friendUserIds;
.
.
.
public boolean isVerified() {
return isVerified;
}
}
Stream Map
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
T 나 T 타입의 상위 타입을 받아서 R이나 R 하위 타입을 리턴해주는 Function을 파라미터로 받는다. 즉, T라는 데이터가 흘러나오는 스트림에서 데이터마다 Function을 적용해서 R타입으로 바꿔주고 그 결과물을 흘려보낸다.
리턴되는 스트림은 R 타입의 데이터 스트림
이와 같은 맵함수가 있을 때 input 타입으로 function을 받는데, 해당 function의 input이 Integer.
Stream<Integer> integerStream = integers.stream().map(x -> x * 2);
이를 리스트로 만들어서 출력해보자.
Stream<Integer> integerStream = integers.stream().map(x -> x * 2);
List<Integer> collect = integerStream.collect(Collectors.toList());
System.out.println(collect);
곱하기가 된 숫자 리스트 출력
이번에는 Integer를 받아서 다른 타입으로 리턴해주는 케이스를 살펴보자.
Stream<String> stringStream = integers.stream().map(x -> "nuumber is " + x);
Integer를 받아서 String으로 변환해서 리턴한다.
실제 사용 예시
List<String> emailList = Stream.of(user1, user2, user3)
.map(User::getEmailAddress)
.toList();
유저를 통해 이메일 리스트를 추출한다.
Stream Pipeline
- 스트림은 3가지 요소로 나눌 수 있음
- Source (소스) > Intermediate Operations (중간 처리) : filter, map 등 > Terminal Operation (Collect, reduce 등)
중간 처리 과정에서 여러 가지의 메서드들을 이어붙일 수 있다!
filter와 map을 함께 사용해보기
List<String> verifiedUsersEmailList = Stream.of(user3, user2, user1)
.filter(User::isVerified)
.map(User::getEmailAddress)
.collect(Collectors.toList());
Stream Sorted
- 데이터가 순서대로 정렬된 stream 리턴
- 데이터의 종류에 따라 Comparator가 필요할 수도 있음
List<Integer> numbers = Arrays.asList(1, 3, -5, 77, 4, 5, 9, 12, -3, -20);
Stream<Integer> sorted = numbers.stream().sorted();
정렬된 상태로 스트림을 만든다.
이를 리스트로 만들어서 출력하면
System.out.println(numbers.stream().sorted().collect(Collectors.toList()));
User를 Sort 해보자.
List<User> users = Arrays.asList(user1, user2, user3, user4, user5)
.stream()
.sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
.collect(Collectors.toList());
System.out.println(users);
메서드 레퍼런스 방식으로 다음과 같이 축약할 수도 있다.
.sorted(Comparator.comparing(User::getName))
Stream 중복제거
Stream<T> distinct();
중복된 요소들을 제거
List<Integer> numbers = Arrays.asList(3, -5, 4, -5, 2,3 );
numbers.stream().distinct();
출력해보자.
System.out.println(distinct.collect(Collectors.toList()));
Stream FlatMap
- Map + Flatten
- 데이터에 함수를 적용한 후 중첩된 stream을 연결하여 하나의 stream으로 리턴
스트림 안에 스트림이 들어가는 경우가 있다. 일반적으로는 스트림 안에 데이터 형태로 들어가길 바란다. 맵을 통해 변형할 때 스트림이 된다면 해당 스트림을 flat 시켜 하나의 스트림으로 통일시켜 주도록 해주는 메서드.
맵의 일종이기 때문에 function을 받아서 input 파라미터로 T나 T의 슈퍼타입으로 받고 리턴 타입이 R이다. 즉, T를 받아서 R 타입의 데이터가 나오는 스트림을 반환한다.
<R> Stream <R> flatMap(Function<? super T, ? extends Stream<? extend R>> mapper);
예를 들어 다음과 같은 2차원 배열이 있다고 치자.
String[][] cities = new String[][]{
{"Seoul", "Busan"},
{"San Francisco", "New York"},
{"Madrid", "Barcelona"}
};
Stream<String[]> stream = Arrays.stream(cities);
이 스트림은 안에 String 배열을 갖는다. 즉, 이 스트림은 city 배열이 덩어리로 들어있다.
이 상태에서 그냥 map을 사용하면 어떨까?
Stream<Stream<String>> streamStream = stream.map(x -> Arrays.stream(x));
Arrays.stream이 스트림을 리턴하기 때문에 각각의 stream을 갖는 stream이 된다.
이대로 리스트를 만든다면
List<Stream<String>> collect = streamStream.collect(Collectors.toList());
다음과 같은 List가 나온다.
이것을 원한 것이 아니기 때문에 이때 필요한 것이 flatMap이라고 할 수 있다.
Stream<String[]> cityStream = Arrays.stream(cities);
Stream<String> stringStream = cityStream.flatMap(x -> Arrays.stream(x));
List<String> cityList = stringStream.collect(Collectors.toList());
reference. https://fastcampus.co.kr/dev_red_lsh