JAVA 8 (3) Stream / Optional의 등장

신복호·2020년 8월 30일
0

java

목록 보기
3/3
post-thumbnail

Stream?

Stream에 대한 정의는 다음과 같습니다.

A sequence of elements supporting sequential and parallel aggregate operations.

출처 : https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

Stream은 선언형으로 Collection, Array, I/O 리소스등 저장된 데이터에 대해서 filtering,matching,mapping등과 같은 복잡한 데이터 처리 작업을 수행하는데 사용됩니다.

그러나 이쯤에서 드는 의문이 한가지 있습니다.
기존 collection 데이터 등을 잘 사용하고 있었는데 굳이 스트림이라는 새로운 것을 왜 만드는 것일까?

Stream vs Collection(Iterator)

무슨 차이가 있는 것일까? 해당 내용을 구글링하다보니 나온 내용이 있습니다.

  • 공통점

    • 연속된 요소의 형식의 값을 저장하는 자료구조의 인터페이스를 제공합니다.
    • 순차적으로 요소에 접근한다.
  • 차이점

    • Collection
      • 각 계산식을 만날때 마다 데이터가 계산된다.
      • 데이터의 접근,읽기,변경, 저장이 주요 기능이다.
      • Iterator로 모든요소를 순환해야한다.
      • 메모리에 모든 요소가 올라가져 있는 상태에서 요소들을 누적하며 결과를 계산한다.
      • 메모리 사용량이 늘어난다.
    • Stream
      • 계산식(람다)을 표현하는 것이 주요 관심사이다.
      • 데이터에 접근하는 방법이 추상화되어있다.
      • 계산식을 미리 적어두고 계산시에 람다식으로 JVM에 넘긴다.
      • 메모리 사용량이 줄어든다.
      • 고정된 자료구조
      • 최종 연산이 실행 될 때에 데이터가 계산된다.

    출처 : https://shlee0882.tistory.com/196

코드 비교


       List<Integer> list = Arrays.asList(1,2,3,4);

       //Iterator
       Iterator<Integer> iter = list.iterator();
       while (iter.hasNext()){
           int number = iter.next();
           System.out.println("값 ::: " + number);
       }

       //Stream
       Stream<Integer> stream = list.stream();
       stream.forEach(number -> System.out.println("값 ::: " + number));

Stream의 특징

  • Stream은 데이터 소스를 변경하지 않는다.
    • Stream은 데이터 소스로 부터 데이터를 읽어드리기만 할뿐 소스를 변경하지 않습니다.
  • Stream은 일회용이다.
    -> Iterator처럼 Stream 또한 한번 사용하고 닫히면 다시는 사용할수 없으며, 필요할 경우 다시 생성을 해야 합니다.
  • 작업을 내부반복합니다.
    -> 내부반복을 이용해서 작업이 간결하게 할수 있습니다.

Stream 사용법

기본 타입

  • IntStream, LongStream, DoubleStream

필터링

  • filter : 요소들에 대하여 특정조건에 따라 걸러내는 작업을 진행한다.
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

       //Stream
       Stream<Integer> stream = list.stream();
       stream.filter(i -> i %2 ==0)
                .forEach(number -> System.out.println("값 ::: " + number));

매핑

  • map :특정조건에 해당되는 값으로 변환
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

        //Stream
        Stream<Integer> stream = list.stream();
        stream.filter(i -> i %2 ==0)
                .map(i-> i*2)
                .forEach(number -> System.out.println("값 ::: " + number));
 

  • flatMap : 중첩구조를 한단계 제거하고 단일 컬랙션으로 만들어주는 역할
        int arr [][] = {{1, 2, 3}, {4, 5}, {6, 7, 8}, {9, 10}};
        Stream.of(arr)
                .flatMapToInt(IntStream::of)
                .forEach(i-> System.out.println("값 ::: " + i));

정렬

  • 요소들을 정리하는 역할
        List<Integer> list = Arrays.asList(1,2,3,4,5,1,2,3,4,5);
        
        //Stream
        Stream<Integer> stream = list.stream();
        stream.sorted()
                .forEach(number -> System.out.println("값 ::: " + number));

Colletors

  • 스트림을 하나의 결과로 요약시킬때 사용
  • 스트림을 여러 그룹으로 분할할때 사용

Optional?

Optional의 등장

Stream과 같이 java8에서 등장한 개념이 Optional이다.
java 공식문서에 따르면 다음과 같이 정의 되어 있습니다.

A container object which may or may not contain a non-null value.
널이 아닌 값을 포함하거나 포함하지 않을 수있는 컨테이너 객체입니다.

해당 설명을 보아하니 null 관련 체크를 하는 기능을 하는거 같다.
근데 if-else를 이용해서 null을 체크 해도 될텐데 Optional을 사용할 필요가 있을까?

API Note:
Optional is primarily intended for use as a method return type where there is a clear need to represent “no result,” and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.
-> Optional은 주로 "결과 없음"을 나타내야하는 명확한 필요성이 있고 null을 사용하면 오류가 발생할 가능성이있는 메서드 반환 유형으로 사용하기위한 것입니다. 유형이 Optional 인 변수는 자체적으로 null이 아니어야합니다. 항상 Optional 인스턴스를 가리켜야합니다

출처 : https://docs.oracle.com/javase/9/docs/api/java/util/Optional.html

예측하지 못하는 null로 부터 개발자들에게 분리를 시키기 위해서 새로 만들어 진거 같다.

  • 예시
        Map<Integer,String> name = new HashMap<>();
        name.put(1,"철수");
        name.put(2,"영희");
        name.put(3, "길동");
        
	//Optional X
        String searchName = name.get(4); // null 발생
        if(searchName == null){
            new Exception("값이 없습니다.");
        }else{
            System.out.println("이름 ::: " + searchName);
        }
        
	// Optional O
 	Optional<String> nameOptaionl = Optional.ofNullable(name.get(4));
        nameOptaionl.orElseThrow(() -> new Exception("값이 없습니다."));
        System.out.println("이름 ::: " + nameOptaionl.get());

Optional 사용법

  • of : Optaional에 값이 반드시 있어야 하는 객체인 경우에 값을 넣어주는 역할을 한다.
       Optional<Integer> optional = Optional.of(1);
       System.out.println(optional.get());
  • ofNullable : null인경우 비어있는 Optional을 반환
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8);
        Optional<Integer> optional =  Optional.ofNullable(list.get(9));
  • orElse: 해당 값이 null 이던 말던 항상
    -> public T orElse(T other)
        List<String> list = Arrays.asList("김범수","나얼","박효신","이수", null);
        System.out.println("단어 ::: " + Optional.ofNullable(list.get(4)).orElse("없음"));
  • orElseGet: 존재하는 경우 값을 반환하고, 그렇지 않으면 other를 호출 하고 해당 호출의 결과를 반환
    -> public T orElseGet(Supplier<? extends T> other)
	List<String> list = Arrays.asList("김범수","나얼","박효신","이수", null);
	System.out.println("단어 ::: " + Optional.ofNullable(list.get(4)).orElseGet(() -> "데이터 없음"));

※ orElse vs orElseGet
orElse 나 orElseGet나 같은 것들이 리턴된다.
그럼 orElse 나 orElseGet이나 같은 기능을 하는 것이 아닌가?
정답은 "같지 않다".

  • orElse -> 값을 리턴
  • orElseGet -> 객체를 리턴

즉 위의 orElseGet을 람다식을 없애면 이해하기 쉽다.

    List<String> list = Arrays.asList("김범수","나얼","박효신","이수", null);
        System.out.println("단어 ::: " + Optional.ofNullable(list.get(1)).orElseGet( new Supplier<String>() {
        @Override
        public String get() {
            return "없음";
        }
    }));
  • orElseThrow : 포함된 값이 있으면 반환하고 아닌경우 예외 발생
  • IsPresent : 내부 객체가 null인지 아닌지를 판단 (boolean)
  • fillter : 조건에 따른 요소들을 필터하는 역할
  • map : 요소들의 값 형태 변환하기
  • get : Optional 안에 존재하는 값을 가져옴

참조 : java8 공식문서

profile
한참 열정이 가득한 백엔드 개발자입니다.

0개의 댓글