자바-20(Stream-2)

학습할것
동작 순서
성능 향상
스트림 재사용
지연 처리(Lazy Invocation)
Null-safe 스트림 생성하기
줄여쓰기(Simplified)
동작 순서
              List<String> list = Arrays.asList("l","l","java");
        Stream<String> stringStream = list.stream();
        stringStream.filter(el -> {
            System.out.println("filter() was called");
            return el.contains("a");
        })
                .map(el -> {
            System.out.println("map() was called");
            return el.toUpperCase();
        })
                .findFirst();
              List<String> list = Arrays.asList("l","l","java");
        Stream<String> stringStream = list.stream();
        stringStream.filter(el -> {
            System.out.println("filter() was called");
            return el.contains("a");
        })
                .map(el -> {
            System.out.println("map() was called");
            return el.toUpperCase();
        })
                .findFirst();output
filter() was called
filter() was called
filter() was called
map() was called- 
여기서 스트림이 동작하는 순서를 알아낼 수 있다. 
- 
모든 요소가 첫 번째 중간 연산을 수행하고 남은 결과가 다음 연산으로 넘어가는 것이 아니라, 한 요소가 모든 파이프라인을 거쳐서 결과를 만들어내고, 다음 요소로 넘어가는 순서이다. 
- 
좀 더 자세히 살펴보면, "l"은 문자열 "a"를 포함하고 있지 않기 때문에 다음 요소로 넘어간다. 
- 
이 때 "filter() was called" 가 출력된다. 
- 
다음 요소인"l" 역시 문자열 "a"를 포함하고 있지 않기 때문에 다음 요소로 넘어간다. 이 때 "filter() was called" 가 출력된다. 
- 
마지막 요소인 "java"는 "a"를 포함하고 있기 때문에 다음 연산으로 넘어갈수 습니다. 
- 
마지막 연산인 findFirst는 첫번쩨 요소만을 반환하는 연산이다. 따라서 최종결과인 "JAVA"이고 다음연산은 수행할 필요가 없어 종료된다.
- 
위와 같은 과정을 통해서 수행된다. 
성능 향상
- 위에서 봤듯이 스트림은 한 요소씩 수직적으로 실행된다.
- 여기에서 스트림의 성능을 개선할 수 있는 힌트가 숨겨져있다.
        List<String> list = Arrays.asList("Eric","Elena","java");
        List<String> stringList = list.stream()
                .map(el->{
                    System.out.println("map() was called");
                    return el.substring(0,3);
                })
                .skip(2)
                .collect(Collectors.toList());
        System.out.println(stringList);
        List<String> list = Arrays.asList("Eric","Elena","java");
        List<String> stringList = list.stream()
                .map(el->{
                    System.out.println("map() was called");
                    return el.substring(0,3);
                })
                .skip(2)
                .collect(Collectors.toList());
        System.out.println(stringList);output
map() was called
map() was called
map() was called
[jav]- 
첫 번째 요소인 "Eric"은 먼저 문자열을 잘라내고, 다음 skip메서드 때문에 스킵된다.
- 
다음 요소인 "Elena"도 마찬가지로 문자열을 잘라낸 후 스킵된다. 
- 
마지막 요소인 "Java"만 문자열을 잘라내어 "Jav"가 된 후 스킵되지 않고 결과에 포함된다. 
- 
여기서 map() 메서드는 총 3번 호출된다. 
- 
여기서 메서드 순서를 바꾸면 어떻게 될까? 
- 
먼저 skip메서드가 먼저 실행되도록 해보자
       List<String> list = Arrays.asList("Eric","Elena","java");
        List<String> stringList = list.stream()
                .skip(2)
                .map(el->{
                    System.out.println("map() was called");
                    return el.substring(0,3);
                })
                .collect(Collectors.toList());
        System.out.println(stringList);output
map() was called
[jav]- 결과 스킵을 먼저하기 때문에 map메서드는 한 번 박에 호출되지 않는다.
- 이렇게 요소의 범위를 줄이는 작업을 먼저 실행하는 것이 불필요한 연산을 막을 수 있어 성능을 향상시킬 수 있다.
- 이런 메서드로는 filter,distinct,skip메서드가 존재한다
스트림 재사용:
- 종료 작업을 하지 않는 하나의 인스턴스로써 계속해서 사용이 가능하다.
- 하지만 종료작업을 하는 순간 스트림이 닫히기 때문에 재사용이 불가능하다.
- 스트림은 저장된 데이터를 꺼내서 처리하는 용도이지 데이터를 저장하려는 목적으로 설계되지 않았기 때문이다.
       Stream<String> stringStream =
                Stream.of("Eric","Elena","Java")
                .filter(name->name.contains("a"));
        Optional<String> firstElement = stringStream.findFirst();
        Optional<String> anyElement  = stringStream.findAny();
        System.out.println(firstElement);
        System.out.println(anyElement);
       Stream<String> stringStream =
                Stream.of("Eric","Elena","Java")
                .filter(name->name.contains("a"));
        Optional<String> firstElement = stringStream.findFirst();
        Optional<String> anyElement  = stringStream.findAny();
        System.out.println(firstElement);
        System.out.println(anyElement);output
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
	at java.util.stream.ReferencePipeline.findAny(ReferencePipeline.java:469)- 
위 코드에서 findFirst메서드를 실행하면서 스트림이 닫히기 대문에findAny메서드를 호출 하는 시간 런타임 예외가 발생한다.
- 
컴일러가 캐치할 수 없기 때문에 Stream 이 닫힌 후에 사용되지 않는지 주의해야 한다. 
- 
위 코드를 아래 코드처럼 변경할 수 있다. 
- 
데이터를 List에 저장하고 필요할 때마다 스트림을 생성해서 사용한다 
        List<String> names= Stream.of("Eric","Elena","Java")
                .filter(name->name.contains("a"))
                .collect(Collectors.toList());
        Optional<String> firstElement = names.stream().findFirst();
        Optional<String> anyElement  = names.stream().findAny();
        System.out.println(firstElement);
        System.out.println(anyElement);output
Optional[Elena]
Optional[Elena]지연 처리(Lazy Invocation)
- 스트림에서 최종 결과는 최종 작업이 이루어질 때 계산된다. 
- 아래 코드는 호출횟수를 카운트하는 코드이다.
 private long cnt;
    private void wasCalled(){
        cnt++;
    }
- 다음코드에서 리스트의 요소가 3개이기 때문에 총 세 번 호출되어 결과가 3이 출력될것이라고 예상된다.
- 하지만 출력값은 0이다.
        List<String> names= Arrays.asList("Eric","Elena","Java");
        cnt = 0;
        Stream<String> stringStream = names.stream()
                .filter(a->{
                    wasCalled();
                    return a.contains("a");
                });
        System.out.println(cnt); // 0 
- 왜냐하면 최종 작업이 실행되지 않아서 스트림의 연산이 실행되지 않았기 때문이다.
- 다음 코드처럼 최종작업인 collect메서드를 호출한 결과 3이 출력된다.
        List<String> names= Arrays.asList("Eric","Elena","Java");
        cnt = 0;
        names.stream()
                .filter(a->{
                    wasCalled();
                    return a.contains("a");
                })
                .collect(Collectors.toList());
        
        System.out.println(cnt); // 3
Null-safe 스트림 생성하기
- NullPointerException은 개발시 흔히 발생하는 예외이다.
- Optional을 이용해서 null에 안전한(null-safe) 스트림을 생성해보자
    public static <T> Stream<T> collectionToStream(Collection<T> collection){
        return Optional
                .ofNullable(collection)
                .map(Collection::stream)
                .orElseGet(Stream::empty);
    }
- 
위 코드는 인자로 받은 컬렉션 객체를 이용해 optional 객체를 만들고 스트림을 생성후 리턴하는 메서드이다. 
- 
그리고 만약 컬렉션이 비어있는 경우라면 빈 스트림을 리턴한다. 
- 
제네릭을 이용해 어떤 타입이든 받을수있다. 
List<Integer> integerList = Arrays.asList(1,2,3);
        List<String> stringList = Arrays.asList("a","b","c");
        Stream<Integer> integerStream = collectionToStream(integerList); // [1,2,3]
        Stream<String> stringStream =  collectionToStream(stringList); // ["a","b","c"]
- 이제 Null로 테스트를 해보겠습니다.
- 아래와 같이 리스트에 null이 있다면 런타임에러가 발생한다.
- 외부에서 인자로 받은 리스트로 작업을 하는 경우에 일어날 수 있는 상황이다.
        List<String> nullList=  null;
        nullList.stream()
                .filter(str->str.contains("a"))
                .map(String::length)
                .forEach(System.out::println);
- 하지만 위에서 만든 메서드를 이용하면 런타임에러가 발생하는 대신 빈 스트림으로 작업을 마칠 수있다.
        List<String> nullList=  null;
       Stream<String> stringStream = collectionToStream(nullList);
        stringStream
                .filter(str->str.contains("a"))
                .map(String::length)
                .forEach(System.out::println);
줄여쓰기(Simplified)
- 스트림 사용시 다음과 같은 겨웅에 같은 내용을 좀 더 간결하게 줄여쓸수있다.
- IntelliJ를 사용하면 다음과 같은 경우에 줄여쓸 것을 제안해준다.
- 많이 사용되는 것만 추렸다.
collection.stream().forEach() 
  → collection.forEach()
  
collection.stream().toArray() 
  → collection.toArray()
Arrays.asList().stream() 
  → Arrays.stream() or Stream.of()
Collections.emptyList().stream() 
  → Stream.empty()
stream.filter().findFirst().isPresent() 
  → stream.anyMatch()
stream.collect(counting()) 
  → stream.count()
stream.collect(maxBy()) 
  → stream.max()
stream.collect(mapping()) 
  → stream.map().collect()
stream.collect(reducing()) 
  → stream.reduce()
stream.collect(summingInt()) 
  → stream.mapToInt().sum()
stream.map(x -> {...; return x;}) 
  → stream.peek(x -> ...)
!stream.anyMatch() 
  → stream.noneMatch()
!stream.anyMatch(x -> !(...)) 
  → stream.allMatch()
stream.map().anyMatch(Boolean::booleanValue) 
  → stream.anyMatch()
IntStream.range(expr1, expr2).mapToObj(x -> array[x]) 
  → Arrays.stream(array, expr1, expr2)
Collection.nCopies(count, ...) 
  → Stream.generate().limit(count)
stream.sorted(comparator).findFirst() 
  → Stream.min(comparator)
- 
하지만 주의할점이 있다. 
- 
특정 케이스에서 조금 다르게 동작할 수 있다. 
- 
예를 들면 다음의 경우 stream을 생략할수있지만,
 
collection.stream().forEach()
-> collection.forEach()
- 다음 경우에서는 동기화는 차이가 있다.
// not synchronized
Collections.synchronizedList(...).stream().forEach()
//synchronized
Collections.synchronizedList(...).forEach()
- 다른 예제는 다음과 같이 collect를 생략하고 바로max메서드를 호출하는 경우이다.
stream.collect(MaxBy())
-> stream.max()
- 하지만 스트림이 비어서 값을 계산할 수 없을 떄의 동작은 다르다.
- 전자는 Optional 객체를 리턴하지만, 후자는 NullPointerException이 발생할 가능성이있다.
collect(Collectors.maxBy()) // optional
Stream.max() // NPE 발생가능
참고
 private long cnt;
    private void wasCalled(){
        cnt++;
    }
        List<String> names= Arrays.asList("Eric","Elena","Java");
        cnt = 0;
        Stream<String> stringStream = names.stream()
                .filter(a->{
                    wasCalled();
                    return a.contains("a");
                });
        System.out.println(cnt); // 0 collect 메서드를 호출한 결과 3이 출력된다.        List<String> names= Arrays.asList("Eric","Elena","Java");
        cnt = 0;
        names.stream()
                .filter(a->{
                    wasCalled();
                    return a.contains("a");
                })
                .collect(Collectors.toList());
        
        System.out.println(cnt); // 3- NullPointerException은 개발시 흔히 발생하는 예외이다.
- Optional을 이용해서 null에 안전한(null-safe) 스트림을 생성해보자
    public static <T> Stream<T> collectionToStream(Collection<T> collection){
        return Optional
                .ofNullable(collection)
                .map(Collection::stream)
                .orElseGet(Stream::empty);
    }- 
위 코드는 인자로 받은 컬렉션 객체를 이용해 optional 객체를 만들고 스트림을 생성후 리턴하는 메서드이다. 
- 
그리고 만약 컬렉션이 비어있는 경우라면 빈 스트림을 리턴한다. 
- 
제네릭을 이용해 어떤 타입이든 받을수있다. 
List<Integer> integerList = Arrays.asList(1,2,3);
        List<String> stringList = Arrays.asList("a","b","c");
        Stream<Integer> integerStream = collectionToStream(integerList); // [1,2,3]
        Stream<String> stringStream =  collectionToStream(stringList); // ["a","b","c"]- 이제 Null로 테스트를 해보겠습니다.
- 아래와 같이 리스트에 null이 있다면 런타임에러가 발생한다.
- 외부에서 인자로 받은 리스트로 작업을 하는 경우에 일어날 수 있는 상황이다.
        List<String> nullList=  null;
        nullList.stream()
                .filter(str->str.contains("a"))
                .map(String::length)
                .forEach(System.out::println);- 하지만 위에서 만든 메서드를 이용하면 런타임에러가 발생하는 대신 빈 스트림으로 작업을 마칠 수있다.
        List<String> nullList=  null;
       Stream<String> stringStream = collectionToStream(nullList);
        stringStream
                .filter(str->str.contains("a"))
                .map(String::length)
                .forEach(System.out::println);줄여쓰기(Simplified)
- 스트림 사용시 다음과 같은 겨웅에 같은 내용을 좀 더 간결하게 줄여쓸수있다.
- IntelliJ를 사용하면 다음과 같은 경우에 줄여쓸 것을 제안해준다.
- 많이 사용되는 것만 추렸다.
collection.stream().forEach() 
  → collection.forEach()
  
collection.stream().toArray() 
  → collection.toArray()
Arrays.asList().stream() 
  → Arrays.stream() or Stream.of()
Collections.emptyList().stream() 
  → Stream.empty()
stream.filter().findFirst().isPresent() 
  → stream.anyMatch()
stream.collect(counting()) 
  → stream.count()
stream.collect(maxBy()) 
  → stream.max()
stream.collect(mapping()) 
  → stream.map().collect()
stream.collect(reducing()) 
  → stream.reduce()
stream.collect(summingInt()) 
  → stream.mapToInt().sum()
stream.map(x -> {...; return x;}) 
  → stream.peek(x -> ...)
!stream.anyMatch() 
  → stream.noneMatch()
!stream.anyMatch(x -> !(...)) 
  → stream.allMatch()
stream.map().anyMatch(Boolean::booleanValue) 
  → stream.anyMatch()
IntStream.range(expr1, expr2).mapToObj(x -> array[x]) 
  → Arrays.stream(array, expr1, expr2)
Collection.nCopies(count, ...) 
  → Stream.generate().limit(count)
stream.sorted(comparator).findFirst() 
  → Stream.min(comparator)
- 
하지만 주의할점이 있다. 
- 
특정 케이스에서 조금 다르게 동작할 수 있다. 
- 
예를 들면 다음의 경우 stream을 생략할수있지만,
 
collection.stream().forEach()
-> collection.forEach()
- 다음 경우에서는 동기화는 차이가 있다.
// not synchronized
Collections.synchronizedList(...).stream().forEach()
//synchronized
Collections.synchronizedList(...).forEach()
- 다른 예제는 다음과 같이 collect를 생략하고 바로max메서드를 호출하는 경우이다.
stream.collect(MaxBy())
-> stream.max()
- 하지만 스트림이 비어서 값을 계산할 수 없을 떄의 동작은 다르다.
- 전자는 Optional 객체를 리턴하지만, 후자는 NullPointerException이 발생할 가능성이있다.
collect(Collectors.maxBy()) // optional
Stream.max() // NPE 발생가능
참고
collection.stream().forEach() 
  → collection.forEach()
  
collection.stream().toArray() 
  → collection.toArray()
Arrays.asList().stream() 
  → Arrays.stream() or Stream.of()
Collections.emptyList().stream() 
  → Stream.empty()
stream.filter().findFirst().isPresent() 
  → stream.anyMatch()
stream.collect(counting()) 
  → stream.count()
stream.collect(maxBy()) 
  → stream.max()
stream.collect(mapping()) 
  → stream.map().collect()
stream.collect(reducing()) 
  → stream.reduce()
stream.collect(summingInt()) 
  → stream.mapToInt().sum()
stream.map(x -> {...; return x;}) 
  → stream.peek(x -> ...)
!stream.anyMatch() 
  → stream.noneMatch()
!stream.anyMatch(x -> !(...)) 
  → stream.allMatch()
stream.map().anyMatch(Boolean::booleanValue) 
  → stream.anyMatch()
IntStream.range(expr1, expr2).mapToObj(x -> array[x]) 
  → Arrays.stream(array, expr1, expr2)
Collection.nCopies(count, ...) 
  → Stream.generate().limit(count)
stream.sorted(comparator).findFirst() 
  → Stream.min(comparator)하지만 주의할점이 있다.
특정 케이스에서 조금 다르게 동작할 수 있다.
예를 들면 다음의 경우 stream을 생략할수있지만,
collection.stream().forEach()
-> collection.forEach()
// not synchronized
Collections.synchronizedList(...).stream().forEach()
//synchronized
Collections.synchronizedList(...).forEach()collect를 생략하고 바로 max 메서드를 호출하는 경우이다.stream.collect(MaxBy())
-> stream.max()collect(Collectors.maxBy()) // optional
Stream.max() // NPE 발생가능[참고링크]https://futurecreator.github.io/2018/08/26/java-8-streams-advanced/
Author And Source
이 문제에 관하여(자바-20(Stream-2)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@yyong3519/자바-20Stream-2저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)