transactions.stream()
.filter(transaction -> transaction.getYear() == 2011)
.sorted(Comparator.comparing(Transaction::getValue))
.collect(Collectors.toList());
transactions.stream()
.map(transaction -> transaction.getTrader().getCity())
.collect(Collectors.toSet());
transactions.stream()
.map(Transaction::getTrader)
.filter(trader -> Objects.equals(trader.getCity(), "Cambridge"))
.distinct()
.sorted(Comparator.comparing(Trader::getName))
.collect(Collectors.toList());
transactions.stream()
.map(transaction -> transaction.getTrader().getName())
.distinct()
.sorted()
.reduce("", (n1, n2) -> n1 + n2);
transactions.stream()
.map(transaction -> transaction.getTrader().getName())
.distinct()
.sorted()
.collect(Collectors.joining());
boolean result5 = transactions.stream()
.anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"));
int calories = menu.stream()
.map(Dish::getCalories)
.reduce(0, Integer::sum)
위 코드는 합계를 계산하기 전 Integer에 대하여 언박싱이 필요합니다.
아래처럼 하면 좋겠으나 불가능
Stream.map(Dish::getCalories)
.sum()
Stream<T'>에 무엇이 들어올지 모르니 당연하게 sum() 제공이 불가능합니다.
이러한 단점때문에 기본형에 특화된 스트림(primitive stream specialization)을 지원합니다.
int에 특화된 IntStream
double에 특화된 DoubleStream
long에 특화된 LongStream
operation
을 제공합니다.map() : 제네릭을 사용한 스트림 반환
mapToInt(), mapToLong(), mapToDouble() : 특화 스트림 반환
Stream<Integer'>
과IntStream
은 다르다.
Stream class는 객체관점에서 operator를 제공하지만
특화 스트림에서는 해당 타입만을 위한 operator를 제공합니다.
IntStream의 map연산은 'Int를 인수로 받아서 Int를 반환하는 람다(IntUnaryOperator)'를 인수로 받습니다.
만약 다시 제네릭을 활용하는 스트림으로 변경하고 싶다면 boxed()를 사용해 다시 변환할 수 있습니다.
IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
Stream에서 0과 null의 구별이 필요할 수도 있습니다.
아무런 요소도 없는데 0이라고 표현하는게 맞을까?
이러한 상황에서 A회사는 null, B회사는 0이라고 표현할 수 있습니다.
그렇다면 기술적인 부분에서도 간결하게 비즈니스를 충족시켜줄 의무가 있다!
OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();
int max = maxCalories.orElse(1);
💬 OptionalInt, OptionalDouble, OptionalLong 3가지 특화 스트림을 제공
프로그램에서는 특정 범위의 숫자를 이용해야 하는 상황이 자주 발생합니다. (ex : 1 ~ 100)
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
System.out.println(evenumbers.count());
rangeClosed()의 결과는 filter()를 이용해서 짝수만 필터링 할 수 있으나 filter를 호출해도 아무 계산도 이루어지지 않습니다.
숫자 스트림을 사용하여 피타고라스 수 만들기
a x a + b x b = c x c
a, b가 제공된다면 두 수가 피타고라스 수의 일부가 될 수 있는 조합인지 확인하는 코드
연산 결과가 부동 소숫점이라면 1로 나누었을 때 나머지는 소숫점 이하가 됩니다.
Math.sqrt(a*a + b*b) % 1 == 0;
->
filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
완성된 피타고라스 요소를 반환
stream.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.map(b -> new int[]{a, b, (int) Math.sqrt(a*a + b*b)})
b값 생성
IntStream.rangeClosed(1, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.boxed()
.map(b -> new int[]{a, b, (int) Math.sqrt(a*a + b*b)})
boxed()를 mapToObj로 처리
IntStream.rangeCLosed(1, 100)
.filter(b -> Math.sqrt(a*a + b*b) % 1 == 0)
.mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a*a + b*b)});
a값 생성
Stream<int[]> pythagoreanTriples =
IntStream.rangeClosed(1, 100).boxed()
.flatMap(a -> IntStream.rangeClosed(a, 100)
.mapToObj(b -> new double[]{a, b, Math.sqrt(a*a + b*b)})
.filter(t -> t[2] % 1 == 0)
);
값으로 스트림 만들기
Stream.of()를 사용하여 문자열 스트림을 생성
Stream<String> stream = Stream.of("Modern", "Java", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
Stream<String> emptyStream = Stream.empty();
때로는 nullable한 값을 가질 수 도 있음
String homeValue = System.getProperty("home"); // Key에 대응하는 값이 없다면 Null 반환
Stream<String> homeValueStream = homeValue == null ? Stream.empty() : Stream.of(homeValue);
Stream.ofNullable()을 사용해 간결하게 표현
String homeValue = System.getProperty("home"); // Key에 대응하는 값이 없다면 Null 반환
Stream<String> homeValueStream = Stream.ofNullable(homeValue);
null이 될 수 있는 객체를 포함하는 스트림값을 flatMap과 함께 사용
Stream<String> values =
Stream.of("config", "home", "user")
.flatMap(key -> Stream.ofNullable(System.getProperty(key)));
Stream<Object> values =
Stream.of("config", "home", "user")
.map(key -> Stream.ofNullable(System.getProperty(key)));
배열로 스트림 만들기
Arrays.stream()은 다양한 형태로 오버로드가 되어 있음
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
파일로 스트림 만들기
파일을 처리하는 등의 I/O 연산에 사용하는 자바의 NIO API(논블록 I/O)도 스트림 API를 활용
할 수 있도록 업데이트
java.nio.file.Files
의 많은 정적 메소드가 스트림을 반환
ex) Files.lines는 주어진 파일의 행 스트림을 문자열로 반환
파일에서 고유한 단어 수 찾기
long uniqueWords = 0;
// Stream은 AutoCloseable을 지원
try(Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
// 각 행을 여러 스트림으로 만들지 않고 평면화
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
// 평면화된 알파벳에서 중복을 제거한 개수 파악
.distinct()
.count();
} catch (IOException e) {
throw new RuntimeException(e);
}
함수로 무한 스트림 만들기
스트림 API는 함수에서 스트림을 만들 수 있는 두 정적 메서드 iterate, generate
를 지원
두 연산을 이용해 (infinite stream, unbounded stream), 고정되지 않은 크기의 스트림을 만들 수 있음
iterate
주어진 함수를 이용해서 값을 만드나 limit()을 이용하여 지정된 범위까지만 출력하도록 해야함
함수는 predicate, unaryOperator.. 여러가지 형태를 지원
Stream.iterate(0, n -> n + 2[제공해야 하는 함수])
.limit(10)
.forEach(System.out::println);
❌ 아래와 같이 사용할 수 없음
, 끝없는 수를 제공하기 때문에 filter는 제공하는
모든 수를 모두 filtering 해야 한다.
IntStream.iterate(0, n -> n + 4)
.filter(n -> n < 100)
.forEach(System.out::println);
쇼트 서킷을 지원하는 takeWhile을 사용해야 한다.
IntStream.iterate(0, n -> n + 4)
.takeWhile(n -> n < 100)
.foreach(Systeam.out::println);
generate
iterate와 비슷하게 값을 계산하는 무한 스트림을 만들 수 있음
차이점은 생산된 각 값을 연속적으로 계산하지 않고
Supplier<T'>를 인수로 받아서 새로운 값을 생성
나중에 계산에 사용할 어떤 값도 저장하지 않음, supplier의 형태: () -> T
Stream.generate(Math::random)
// limit이 없다면 언바운드 상태가 된다.
.limit(5)
.forEach(System.out::println);
발행자가 꼭 값이 없어야 하는 것은 아니지만
병렬 코드에서 발행자에 상태가 있으면 안전하지 않음
상태를 가질 수 없다. 불변
IntStream ones = IntStream.generate(() -> 1);
상태에 접근할 수 있다. 가변
IntStream twos = IntStream.generate(new IntSupplier() {
public int getAsInt() {
return 2;
}
});
스트림을 병렬로 처리하면서 올바른 결과를 얻으려면 불변 상태 기법을 고수하자!