Skip to content

Latest commit

 

History

History
183 lines (141 loc) · 5.66 KB

스트림은_주의해서_사용하라.md

File metadata and controls

183 lines (141 loc) · 5.66 KB

아이템 45 : 스트림은 주의해서 사용하라

핵심 정리

  • 스트림을 과하게 사용하면 가독성을 해친다
  • char 연산은 stream을 사용하지 말라
  • stage가 있는 상태에서 연산 이전 값 접근을 위해서는 매핑을 거꾸로 수행하라
  • 팀 규율과 개인 선호에 따라 반복문 vs 스트림을 선택하라

스트림 API가 제공하는 추상 개념 2가지

  1. Stream : 데이터 원소의 유한 혹은 무한 시퀀스 ex) Stream.of(배열) == 이 배열의 시퀀스
  2. Stream pipline : 원소들로 수행하는 연산단계
  3. fluent API : 메서드 연쇄를 지원

스트림 파이프라인

  • 중간 연산 + 종단 연산 으로 이루어짐
  • 지연평가 : 평가는 종단 연산이 호출될 때 이루어짐
스트림 API는 다재다능하다
그러나, API가 할 수 있다는 것이 해야하는 것으로 이어지진 않는다

스트림을 과용하면 프로그램이 읽거나 유지보수하기 힘들어진다.

  • 스트림을 사용하여 키값과 일치하는 value를 찾는 예시
public class ObaStream {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("콜리", "브로");
        map.put("몽키", "건우");
        System.out.println(findValue(map, "콜리"));
    }
    
    private static String findValue(Map<String, String> map, String key){
        return map.entrySet().
                stream().
                filter(e -> e.getKey().equals(key))
                .findFirst()
                .orElseThrow(IllegalArgumentException::new)
                .getValue();
                
        
    }
}
  • 어떨 땐 반복문 or 더 나은 방안이 있을 수도 있음을 고려하라
System.out.println(map.get("콜리"));

스트림 비추 상황 vs 추천 상황

비추 상황

  • 범위 안에서 지역변수를 수정할 때

    • 람다 : final 지역변수만 연산 내에서 사용 가능
    • 반복문 : 지역변수 수정 가능
  • 범위 바깥의 beak, continue 등 분기가 필요할 때

public class flowex33 {

    public static void main(String[] args) {
        Loop1 : for(int i=2; i<10; i++)
        {
            for(int j=1; j<10; j++)
            {
                if (j==5)
                {
                    break Loop1; //바깥 반복문을 벗어남
                }
                System.out.println(i+"*"+j+"="+i*j);
            }
            System.out.println();
        }
    }
}
  • 메서드 선언에 명시된 검사 예외를 던져야 할 때

스트림 추천 상황

  • 일관된 시퀀스 변환/필터링 -> map / filter
  • 일관된 연산 결합 -> iterate / reduce
  • 컬렉션에 모을 때 -> collect()
  • 특정 조건을 만족하는 원소를 찾을 때 / findAny

char용 스트림을 지원하지 않는 이유

  • char는 연산 시 자동 형변환 된다
  • 최종 연산시 다시 명시적으로 형변환 해주어야 한다
  • 따라서 char 값 처리에서는 스트림을 삼가하는 것이 좋다

ex)

 "Hello world!".chars().forEach(System.out::print);

// result : 721011081081113211911111410810033
  • char 연산의 결과는 int가 되어 원하는 값이 나오지 않는다
 // Fixes the problem
"Hello world!".chars().forEach(x -> System.out.print((char) x));

// result : Hello world!
  • 다시 형변환 해줌으로써 온전한 char이 출력되었다

여러 stage에서 stream 사용법

  • 여러 stage에 동시에 값을 접근해야 할 때 스트림을 주의하자
  • 스트림은 한 값을 매핑하면 원래의 값을 잃는다
  • 다음 연산에서 원래 값이 필요하다면 스트림 사용에 주의하라
  • 매핑을 거꾸로 수행하여 원상복귀하는 극복방안이 있다

메르센 소수 예시

  • p가 소수고, 2^p -1도 소수인 p
  • ex) 2 -> 2^2-1 = 3 => 2는 메르센 소수이다
  • ex) 4 -> 2^4-1 = 15 => 4는 메르센 소수가 아니다
// Generating the first twent Mersenne primes using streams (Page 208)
public class MersennePrimes {
    static Stream<BigInteger> primes() {
        return Stream.iterate(TWO, BigInteger::nextProbablePrime);
    }

    public static void main(String[] args) {
        primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE)) // 소수 -> 2^소수 -1  2 3 5 7 -> 3 7 31 127
                .filter(mersenne -> mersenne.isProbablePrime(50)) // 메르세니 수인지 소수 판단
                .limit(20) // 20개만 출력할 거임
                .forEach(mp -> System.out.println(mp.bitLength() + ": " + mp)); //다시 원래대로 원복 3 7 31 127 -> 2 3 5 7
    }
}
  • 2^소수-1을 바탕으로 소수를 판별하는 로직

결국 취향 문제이다

반복문을 활용한 덱 생성로직

private static List<Card> newDeck() {
        List<Card> result = new ArrayList<>();
        for (Suit suit : Suit.values())
            for (Rank rank : Rank.values())
                result.add(new Card(suit, rank));
        return result;
    }

스트림을 사용한 방법

private static List<Card> newDeck() {
        return Stream.of(Suit.values())
                .flatMap(suit ->
                        Stream.of(Rank.values())
                                .map(rank -> new Card(suit, rank)))
                .collect(toList());
    }
  • 반복문은 더 단순하고 자연스럽다
  • 그러나, 함수형 프로그래밍에 익숙한 프로그래머라면 스트림 방식을 좋아할수도 있다
  • 확신이 서지 않는다면 반복문을 사용하는 것이 더 안전하다
  • 스트림 방식이 나아보이고 동료들도 스트림을 선호한다면 사용하자