- 스트림을 과하게 사용하면 가독성을 해친다
- char 연산은 stream을 사용하지 말라
- stage가 있는 상태에서 연산 이전 값 접근을 위해서는 매핑을 거꾸로 수행하라
- 팀 규율과 개인 선호에 따라 반복문 vs 스트림을 선택하라
- Stream : 데이터 원소의 유한 혹은 무한 시퀀스 ex) Stream.of(배열) == 이 배열의 시퀀스
- Stream pipline : 원소들로 수행하는 연산단계
- 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("콜리"));
-
범위 안에서 지역변수를 수정할 때
- 람다 : 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 값 처리에서는 스트림을 삼가하는 것이 좋다
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에 동시에 값을 접근해야 할 때 스트림을 주의하자
- 스트림은 한 값을 매핑하면 원래의 값을 잃는다
- 다음 연산에서 원래 값이 필요하다면 스트림 사용에 주의하라
- 매핑을 거꾸로 수행하여
원상복귀하는 극복방안
이 있다
- 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());
}
- 반복문은 더 단순하고 자연스럽다
- 그러나, 함수형 프로그래밍에 익숙한 프로그래머라면 스트림 방식을 좋아할수도 있다
- 확신이 서지 않는다면 반복문을 사용하는 것이 더 안전하다
- 스트림 방식이 나아보이고 동료들도 스트림을 선호한다면 사용하자