[JAVA] 3. Stream 기초와 Optional

초보개발자·2024년 1월 23일
0

Java

목록 보기
3/5
post-thumbnail

⭐스트림이란?

  • 데이터의 흐름
  • 컬렉션(Collection) 형태로 구성된 데이터를 람다를 이용해 간결하고 직관적으로 프로세스하게 해줌
  • For, while 등을 이용하던 기존 loop을 대체
  • 손쉽게 병렬 처리를 할 수 있게 해줌

☀️Stream의 구성요소

  • 여러가지의 중간 처리를 이어붙이는 것이 가능
// 유요하지 않은 유저 필터링 후 이메일 추출
List<String> emails2 = users.stream()
            .filter(user -> !user.isVerified())
            .map(User::getEmailAddress)
            .collect(Collectors.toList());
            
// error과 24시간 이내 생성된 것 필터링
List<Order> ex2 = orders.stream()
            .filter(order -> order.getStatus() == OrderStatus.ERROR)
            .filter(order -> order.getCreatedAt().isAfter(now.minusHours(24)))
            .collect(Collectors.toList());

📑Stream의 다양한 중간 처리

1. Filter

  • 인스타그램 필터 말고 거름종이같은 필터
  • 만족하는 데이터만 걸러내는데 사용
  • Predicate에 true를 반환하는 데이터만 존재하는 stream을 리턴
Stream<T> filter(Predicate<? super T> predicate);
Stream<Integer> numberStream = Stream.of(3, -5, 7, 10, -3);
Stream<Integer> filteredNumberStream = numberStream.filter(x -> x > 0);
List<Integer> filteredNumbers = filteredNumberStream.collect(Collectors.toList());

// filter와 collect를 한번에
List<Integer> newFilteredNumbers = Stream.of(3, -5, 7, 10, -3)
        .filter(x -> x > 0)
        .collect(Collectors.toList());

// user가 유효한지 필터링
List<User> users = Arrays.asList(user1, user2, user3);
List<User> verifiedUsers = users.stream()
        .filter(User::isVerified)
        .collect(Collectors.toList());
        
// order의 status를 필터링
List<Order> errorOrder = orders.stream()
        .filter(o -> o.getStatus() == OrderStatus.ERROR)
        .collect(Collectors.toList());

2. Map

  • 데이터를 변형하는데 사용
  • 데이터에 해당 함수가 적용된 결과물을 제공하는 stream을 리턴
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
// 리스트 전부 곱하기 2
var numberList = Arrays.asList(3, 6, -4);
        List<Integer> numberListX2 = numberList.stream()
                .map(x -> x * 2)
                .collect(Collectors.toList());
                
// 유저의 이메일만 추출
List<String> emailAddresses = users.stream()
        .map(User::getEmailAddress)
        .collect(Collectors.toList());

3. Sorted

  • 데이터가 순서대로 정렬된 stream을 리턴
  • 데이터의 종류에 따라 Comparator가 필요할 수 있음
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
// 오름차순 정렬
List<Integer> numbers = Arrays.asList(3, -5, 7, 4);
List<Integer> sortedNumbers = numbers.stream()
        .sorted()
        .collect(Collectors.toList());

// 유저 이름 내림차순
List<User> users = Arrays.asList(user1, user2, user3);
List<User> sortedUsers = users.stream()
        .sorted((u1, u2) -> u2.getName().compareTo(u1.getName()))
        .collect(Collectors.toList());

4. Distinct

  • 중복되는 데이터가 제거된 stream을 리턴
Stream<T> distinct();
// userid 리스트로 추출하고 중복제거 후 정렬
List<Long> ex = orders.stream()
        .map(Order::getCreatedByUserId)
        .distinct()
        .sorted()
        .collect(Collectors.toList());

5. FlatMap

  • Map + Flatten
    • 데이터에 함수를 적용한 후 중첩된 stream을 연결하여 하나의 stream으로 리턴
<R> Stream<R> flatMap(
Function<? super T, ? extends Stream<? extends R>> mapper);

☠️NPE – NullPointerException

  • Null 상태인 오브젝트를 레퍼런스 할 때 발생
  • Runtime error이기 때문에 실행 전 까지는 발생 여부를 알기 쉽지 않음

😊Optional

1. Optional이란?

  • Null일수도, 아닐 수도 있는 오브젝트를 담은 상자
java.util.Optional<T>

Optional<String> maybeString = Optional.of("Hello world");
String string = maybeString.get();

2. Optional 만드는 법

public static <T> Optional<T> of(T value)
public static <T> Optional<T> empty()
public static <T> Optional<T> ofNullable(T value)
String someEmail = "some@email.com";
String nullEmail = null;

Optional<String> maybeEmail = Optional.of(someEmail);
Optional<String> maybeEmail2 = Optional.empty();
Optional<String> maybeEmail3 = Optional.ofNullable(someEmail);
Optional<String> maybeEmail4 = Optional.ofNullable(nullEmail);
  • of – Null이 아닌 오브젝트를 이용해 Optional을 만들 때
  • Empty – 빈 Optional을 만들 때
  • ofNullable – Null인지 아닌지 알 지 못하는 오브젝트로 Optional을 만들 때

3. 안에 있는 값을 확인하고 꺼내는 법

public boolean isPresent()
public T get()
public T orElse(T other)
public T orElseGet(Supplier<? extends T> supplier)
public <X extends Throwable> T orElseThrow(
	Supplier<? extends X> exceptionSupplier) throws X
String email = maybeEmail.get();
System.out.println(email);

if (maybeEmail2.isPresent()) {
    System.out.println(maybeEmail2.get());
}

String defaultEmail = "default@email.com";
String email3 = maybeEmail2.orElse(defaultEmail);
System.out.println(email3);
String email4 = maybeEmail2.orElseGet(() -> defaultEmail);
System.out.println(email4);
String email5 = maybeEmail2.orElseThrow(() -> new RuntimeException("email not present"));
  • isPresent – 안의 오브젝트가 null인지 아닌지 체크. Null이 아닐 시 true
  • get – Optional 안의 값을 추출. Null이라면 에러
  • orElse – Optional이 null이 아니라면 Optional 안의 값을, null이라면 other로 공급된 값을 리턴
  • orElseGet – Optional이 null이 아니라면 Optional 안의 값을, null이라면 supplier로 공급되는 값을 리턴
  • orElseThrow – Optional이 null이 아니라면 Optional 안의 값을, null이라면 exceptionSupplier로 공급되는 exception을 던짐

4. 응용을 위해 알아야 할 것들

public void ifPresent(Consumer<? super T> action)
public <U> Optional<U> map(Function<? super T, ? extends U> mapper)
public <U> Optional<U> flatMap(
Function<? super T, ? extends Optional<? extends U>> mapper)
Optional<User> maybeUser = Optional.ofNullable(maybeGetUser(true));
// 존재하면 println
maybeUser.ifPresent(user -> System.out.println(user));

// user id 추출
Optional<Integer> maybeId = Optional.ofNullable(maybeGetUser(true))
        .map(user -> user.getId());
maybeId.ifPresent(System.out::println);

// user name 추출 후 있으면 map 없으면 orElse
String userName = Optional.ofNullable(maybeGetUser(false))
        .map(User::getName)
        .map(name -> "The name is " + name)
        .orElse("Name is empty");
System.out.println(userName);

// optional이 두번 겹쳐질때 flatMap 사용
Optional<String> maybeEmail = Optional.ofNullable(maybeGetUser(true))
        .flatMap(User::getEmailAddress);
maybeEmail.ifPresent(System.out::println);
  • ifPresent – Optional이 null이 아니라면 action을 실행
  • map – Optional이 null이 mapper를 적용
  • flatMap – mapper의 리턴 값이 또 다른 Optional이라면 한 단계의 Optional이 되도록 납작하게 해줌
profile
꾸준히 빠르게

0개의 댓글