본문 바로가기
Programming/Java, Spring

Java Stream, filter, map, sorted, distinct, flatmap

by Renechoi 2023. 6. 19.

스트림 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

 

 

반응형