자바 Stream과 Optional

자바 Stream과 Optional

Stream

데이터를 담고 있는 것은 컬렉션. 스트림은 이러한 컬렉션에 들어있는 데이터들을 가지고 연산을 실행하는 것.

컬렉션에 담겨있는 데이터값이 변경되는 것은 아니다.

중개 operation과 종료 operation로 나뉜다.

중개 operation은 LAZY 하다.

중개 operation은 LAZY 하기 때문에 종료operation이 들어와야 스트림의 연산이 끝난다. 그래서 여러개의 중개 연산과 하나의 종료 연산으로 구성되어있다.

List names = new ArrayList<>(); names.add("hello"); names.add("man"); names.add("iii"); names.add("aaa"); names.add("bbb"); // 중개 연산. 연산이 끝나지 않았으므로 리턴값은 스트링 Stream stringStream = names.stream().map(String::toUpperCase); // 종료 연산 List res = stringStream.collect(Collectors.toList());

스트림 연산을 통해 names 컬렉션의 데이터들 값 자체가 변경되는 것은 아니기때문에 collect 함수를 통해서 따로 res라는 변수에 결과값을 담아준다.

병렬 처리

스트림을 사용하면 병렬처리가 편해진다. parallelStream을 사용하면된다. 간단하게나마 위의 예제에 그대로 적용해봤다.

List collect1 = names.stream().map(s -> { System.out.println(s); return s.toUpperCase(Locale.ROOT); }).collect(Collectors.toList()); List collect1 = names.parallelStream().map(s -> { System.out.println(s); return s.toUpperCase(Locale.ROOT); }).collect(Collectors.toList());

실행결과

병렬적으로 처리하기 때문에 parallelStream을 사용한 연산은 컬렉션에 담긴 데이터의 순서가 보장되지 않는 것을 확인할 수 있었다. 쓰레드 이름과 함께 로그를 남겨봤는데 다음과 같았다.

활용해보기

List classes = new ArrayList(); classes.add(new MyClass(1, "class1", true)); classes.add(new MyClass(2, "class3", true)); classes.add(new MyClass(4, "class14", false)); classes.add(new MyClass(8, "class15", true)); classes.add(new MyClass(6, "class16", true)); classes.add(new MyClass(9, "class17", false)); Optional reduce = classes.stream().filter(s -> s.isClosed).map(s -> s.id).reduce((acc, a) -> acc + a); System.out.println(reduce.get());

map filter reduce를 사용한 stream 실습 코드

flatMap

List> classList = new ArrayList<>(); classList.add(classes); classList.add(classes2); classList.stream().flatMap(Collection::stream).forEach(System.out::println);

리스트를 가지고 있는 리스트의 경우 flat을 사용해서 하나의 리스트로 펼치는 것도 가능하다. (2차원 -> 1차원)

Optional

등장배경

public class App { public static void main(String[] args) { Object obj = App.getObj(); if(obj != null) { } } private static Object getObj() { return null; } }

Optional 등장 이전에 자바에서는 null 체크는 흔히 볼 수 있는 코드였다. 하지만 사람이기에 저런 null 체크는 깜빡하고 빼먹어서 에러가 날 가능성이 다분했다. 그래서 Optional은 매번 null 체크를 하지않기 위해 혹은 특정 함수에서 리턴값이 null이 리턴되는 상황 등을 해결하기 위해 Optional 이 등장했다.

private static Optional getObj() { App mayBeNull = null; return Optional.ofNullable(mayBeNull); }

만약 null 값이 될 수 있는 변수일 경우에는 Optional로 한번 감싸서 전달하는 것이다. Optional을 사용할 때에는 예시처럼 반환값으로만 사용해야한다. 나머지의 경우에는 Optional을 사용하는 것이 무의미해질 수도 있다.

주의점 간략 정리

Optional은 반환값으로만 사용하자

Optional로 감싸는 대상은 null인지 아닌지 체크할 수 없는 객체를 대상으로 할 것

사용해보기

optional 만드는 함수 3개

of() : null이 아닌 객체를 담고있는 Optional 생성

ofNullable() : null이든 아니든 다 받아서 Optional 생성

empty() : null 을 담고있는 Optional 생성

get() ; optional로 감싼 대상 꺼내오기.

ifPresent(() -> {}) : optional로 감싼 대상이 null이 아니면 전달받은 콜백함수 실행

isPresent() : optional로 감싼 대상이 null인지 체크 (사용 권장 x)

orElse(), orElseGet() : 값이 있으면 가져오고 없으면 ~ 를 리턴

orElseThrow() : null일 때 대안이 없으면 에러 던짐

orElse, orElseGet 예시

public static void main(String[] args) { Optional obj = App.getObj(); App app = obj.orElse(createNew()); } private static App createNew() { return new App(); } private static Optional getObj() { App mayBeNull = null; return Optional.ofNullable(mayBeNull); }

orElse는 createNew() 함수가 항상 실행된다. 그래서 만약 obj에 값이 null이 들어있으면 createNew의 결과값을 반환하고 null이 아니면 해당 값을 그대로 반환한다. null이든 아니든 createNew 가 실행되어서 조금 비효율적인데 이럴때는 orElseGet을 쓰면 된다. 만약 위의 예제에서 orElseGet을 썼다면 Optional의 값이 null 이 아닐때는 createNew가 실행되지 않을 것이다.

App app = obj.orElseGet(() -> createNew()); App app2 = obj.orElseGet(App::createNew);

orElse : 이미 만들어져있는 값일때 사용

orElseGet : 동적으로 함수 호출을 통해 값을 만들어야할 때 사용

Optional의 map과 filter

Optional obj = App.getObj(); Optional s1 = obj.map(s -> s.toString());

Optional에서 map과 filter를 바로 사용할 수 있도록 제공해준다. 두 메소드의 결과값은 당연히 Optional이다. 만약 map 함수의 결과가 Optional이라면 Optional 안에 Optional이 중첩되는 상황이 발생할 수 있다. 그럴때 사용하는 것이 flatMap이다.

public static void main(String[] args) { Optional obj = App.getObj(); //map Optional> app = obj.map(App::makeApp); Optional app2 = app.orElse(Optional.empty()); //flatMap Optional appWithFlatMap = obj.flatMap(App::makeApp); } public Optional makeApp() { return Optional.of(new App()); } private static Optional getObj() { App mayBeNull = null; return Optional.ofNullable(mayBeNull); }

마무리

한번쯤 정리가 필요했다 싶었던 내용들이었는데 강의와 함께 여러 레퍼런스를 찾아보며 정리하니 도움이 많이 된 시간이었다.

관련글

https://www.inflearn.com/course/the-java-java8/dashboard

위 강의를 공부하며 정리한 글입니다.

참고하면 좋은 레퍼런스

https://www.daleseo.com/java8-optional-after/

https://www.daleseo.com/java8-optional-effective/

https://jeong-pro.tistory.com/165

https://futurecreator.github.io/2018/08/26/java-8-streams/

from http://mvmvm.tistory.com/166 by ccl(A) rewrite - 2021-09-28 08:27:40