5. 스트림 활용
-
filter 메서드는 Predicate를 인수로 받아서 프레디케이트와 일치하는 모든 요소를 포함하는 스트림을 반환한다.
-
dictinct는 중복을 필터링한다. (고유 여부는 hashCode와 equals로 결정된다)
-
takeWhile 를 이용하면 해당 프레디케이트가 false면 동작을 멈춘다.
List<Dish> filteredMenu = specialMenu.stream() .takeWhile(dish -> dish.getCalories() < 320) .collect(toList());
specialMenu 내부에 칼로리 오름차순으로 정렬이 되어있다면 해당 프레디케이트가 true일 동안만 작동한다. filter 였으면 다 돌았을 것이다.
-
dropWhile은 해당 프레디케이트가 처음으로 거짓이 되는 지점까지 발견된 요소를 버린다.
List<Dish> slicedMenu = specialMenu.stream() .dropWhile(dish -> dish.getCalories() <320) .collect(toList());
-
limit은 주어진 크기 까지만 반환한다.
-
skip은 주어진 크기만큼 요소를 제외한다.
-
flatMap은 생성된 스트림을 하나의 스트림으로 평면화 시킨다.
List<String> uniqueCharacters = words.stream() .map(word -> word.split("")) .flatMap(Arrays::stream) .distinct() .collect(toList());
각 단어들을 잘라서 평면화된 스트림으로 만들고 distinct()를 이용해서 사용된 문자들만 List로 받는다.
-
.reduce( 초기값, 리듀싱 연산) 을 통해 복잡한 질의를 표현할 수 있다. 초기값이 없는 경우에는 Optional를 받게된다. 왜냐하면, 스트림에 아무 요소도 없을 수 있기 때문이다.
-
map, filter는 입력 스트림에서 각 요소를 받아 0 또는 결과를 출력 스트림으로 보낸다. 내부 상태를 갖지 않는 연산이다.
reduce, sum, max는 결과를 누적할 내부 상태가 필요하다. 스트림엣 ㅓ처리하는 요소 수와 관계없이 내부 상태의 크기는 한정되어 있다.
sorted, distinct는 과거의 이력을 알고 있어야 한다. 모든 요소가 버퍼에 추가되어 있어야 한다. 이러한 연산을 내부 상태를 갖는 연산이라고 한다. -
IntStream은 max, min, average 등의 유틸리티 메서드를 지원한다.
-
기본형 스트림으로 박싱과 언박싱은 .mapToInt, .boxed() 등을 이용할 수 있다.
-
특정 숫자 범위를 이용하기 위해 IntStream.rangeClosed(1,100) 등과 같이 이용할 수 있다. range는 표기값을 포함하지 않는다.
-
Stream.iterate(초깃값, 람다) 를 통해 무한스트림을 만들 수 있다. ex. Stream.iterate(0, n -> n + 2)
프레디케이트도 지원한다. 0에서 시작해서 100보다 크면 숫자 생성을 중단하는 코드는 다음과 같이 구현 가능하다. IntStream.iterate(0, n -> n < 100, n -> n + 4)
-
Stream.generate 또한 무한스트림을 만들 수 있다. iterate와 같이 생산된 각 값을 연속적으로 계산하지 않는다. Supplier를 인수로 받아서 새로운 값을 생산한다.
손너잘
손너잘은 집을 사기 위해 열심히 저금을 하고 있다. 이번 우테코은행에서는 손너잘과 같은 사람들을 위해 적금 시스템을 만들었다.
적금은 4년 만기 이며 매달 돈을 내면 이자를 매달 복리로 계산하여 준다. 이자울는 10%이다.
아래와 같이 손너잘이 저금한 금액이 들어왔을 때 최종적으로 손너잘이 받을 금액을 산출하시오.단 복리계산은 다음과 같이 한다.1년, 1월에 74만원을 입금한 손너잘은 이자 10퍼를 더한 7.4만원을 2월달에 받고 2월에 80만원을 입금하였으므로 그 시점 자산은 74 + 80 + 7.4 인 171.4만원이 된다. 이 금엑에 또 10퍼의 이자를 받고 3월달에 합쳐지고.....또한 4년 마지막 달의 이율은 계산하지 않는다.이해가 안되면 댓글 부탁 드립니다.입력은 행은 '년' 이며 열은 매달 저금한 금액이다.
int[][] savings = new int[][] {
{74, 80, 59, 68, 74, 80, 59, 68, 74, 80, 59, 68},
{45, 34, 22, 54, 63, 22, 64, 34, 13, 34, 75, 33},
{23, 34, 66, 64, 74, 43, 11, 56, 46, 34, 57, 43},
{13, 44, 56, 7, 45, 45, 6, 45, 53, 66, 77, 5},
};
답안
double sonNeoJalsMoney = Arrays.stream(savings)
.flatMapToDouble(arr -> Arrays.stream(arr).mapToDouble(Double::valueOf))
.reduce(0, (beforeMoney, newMoney) -> (beforeMoney * 1.1) + newMoney);
final double money = Arrays.stream(savings)
.flatMapToDouble(arr -> Arrays.stream(arr).asDoubleStream())
.reduce(0.0, (sum, value) -> sum = 1.1 * sum + value);
나봄
우테코에서 로또 과제를 하고 있던 나봄.
로또 번호 생성을 Stream.generate()
을 이용해 생성하고 싶었다.Stream.generate()
을 이용해 로또 번호가 담겨있는 List 를 만들어 주세요!
요구사항 :
- 로또 번호는 1~45 사이이다.
- 로또 번호는 중복되면 안된다.
- List의 사이즈는 6 개이다.
답안
List lottoNumbers = Stream.generate(() -> new Random().nextInt(45) + 1)
.distinct()
.limit(6)
.collect(Collectors.toList());
List<Integer> collect = new Random()
.ints(1, 46)
.distinct()
.limit(6)
.boxed()
.collect(toList());
랜덤 수를 만들 때에는 generate()
보다는 ints()
를 이용하라고 한다. Random 객체를 미리 생성해두면 시간을 더 단축시킬 수 있다.
웨지
햄버거 문제 이어서 나갑니당!
아니 매니저님 그냥 말로만 하시면 어떡해요 그래도 햄버거 메뉴 소스는 주셔야 할거 아니에요... 드디어 햄버거 목록을 받았습니다! 해당 소스를 이용해서 매니저가 요구했던 조건에 맞는 결과를 검출해주세요!
다시보는 요구사항
일단 메뉴를 전부 줄게, 다 적어나봐..
1. 다이어트 메뉴를 만들어야해, 500칼로리가 안 되는 메뉴들만 뽑아봐
2. 그중 가격이 가장 비싼걸 찾아야 하거든?
3. 3개까지만.
4. 그 메뉴들의 가격 목록을 만들어줘
---
class HamburgerShop {
static class Hamburger implements Comparable<Hamburger>{
String name;
int price;
int calorie; public Hamburger(String name, int price, int calorie) {
this.name = name;
this.price = price;
this.calorie = calorie;
} @Override
public int compareTo(Hamburger that) {
return Integer.compare(this.price, that.price);
}
} public static void main(String[] args) {
List<Hamburger> menus = Arrays.asList(
new Hamburger("쿼터파운더치즈", 4000, 600),
new Hamburger("빅맥", 4600, 500),
new Hamburger("베이컨토마토디럭스", 8000, 700),
new Hamburger("치즈", 2800, 350),
new Hamburger("더블치즈", 3800, 390),
new Hamburger("1955버거", 6500, 800),
new Hamburger("맥치킨", 2500, 410),
new Hamburger("상하이치킨", 4500, 500),
new Hamburger("불고기", 2000, 300),
new Hamburger("행운", 3500, 450)
); List<Integer> topThreePrice = menus.stream()
//중간연산을 작성해 주세요~!
.collect(Collectors.toList()); System.out.println(topThreePrice);
}
}
---
[4600, 4500, 3800]
답안
List<Integer> topThreePrice = menus.stream()
.filter(menu -> menu.calorie < 500)
.sorted(reverseOrder())
.map(menu -> menu.price)
.limit(3)
collect(toList());
라이언
map과 flatMap의 차이를 이해한 것을 토대로 최대한 간략하게 서술하시오.
답안
flatmap은 map 과 달리 평면화된 스트림을 반환. 예를 들어, map으로 어떤 리스트 배열을 stream화 시켰다면, 반환되는 것은 stream의 stream이었을것. 이때 flatmap을 사용하면 하나의 stream으로 합쳐져서 반환된다.
중간곰
아래 코드의 결과를 눈으로 예측해보세요~
(1번)
Stream.iterate(0, n -> n + 1)
.skip(5)
.limit(10)
.filter( n -> n % 2 == 0)
.skip(1)
.limit(3)
.skip(1)
.forEach(System.out::println);(2번)
Stream.iterate(0, n -> (n % 2 == 0), n -> n + 1)
.limit(10)
.forEach(System.out::println);
답안
10,12 / 0
완태
아래 사칙 연산중, stream(), parallelStream() 의 결과가 다른 경우는 언제일까요?
List<Double> doubles = Arrays.asList(1.0,2.0,3.0,4.0);//plus
doubles.stream().reduce(0.0, (a, b) -> (a+b))
doubles.parallelStream().reduce(0.0, (a, b) -> (a+b))//minus
doubles.stream().reduce(0.0, (a, b) -> (a-b))
doubles.parallelStream().reduce(0.0, (a, b) -> (a-b))//multiply
doubles.stream().reduce(1.0, (a, b) -> (a*b))
doubles.parallelStream().reduce(1.0, (a, b) -> (a*b))//divide
doubles.stream().reduce(1.0, (a, b) -> (a/b))
doubles.parallelStream().reduce(1.0, (a, b) -> (a/b))
답안
덧셈과 곱셈은 항의 순서가 바뀌어도 결과값은 같지만 뺄셈과 나눗셈은 그렇지 않으므로 뺄셈과 나눗셈
검프
카드 덱을 만들려고 한다.
현재 2개의 리스트는 준비가 되어있다. 이를 이용하여 의미있는 카드 이름을 만들고 싶다.
아래코드는 2중 for문이 들어가 메모리 걱정에 잠에 못들게 한다.
이를 flatMap을 사용하여 리팩토링 해보자 ㅎㅎ
public class Application {
public static void main(String[] args) {
List<String> symbols = Arrays.asList("스페이드", "하트", "다이아몬드", "클로버");
List<String> numbers = Arrays.asList("A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"); List<String> cards = new ArrayList<>();
for (String symbol : symbols) {
for (String number : numbers) {
cards.add(symbol + number);
}
}
System.out.println("cards = " + cards);
}
}
답안
List<String> cards = numbers.stream()
.flatMap(number -> symbols.stream()
.map(symbol -> number + symbol))
.collect(toList());
파즈
기본형 특화 스트림은 박싱/언박싱 비용을 줄여주기 때문에 굉장히 유용하다고 볼 수 있다.
그렇다면 기본형 Optional은 어떠할 것 같은지 작성해보시오~~
답안
요소가 많은 경우 기본형 특화 스트림은 성능향상을 시켜줄 수 있다. 그러나 Optional의 최대 요소 수는 한 개 이므로 성능 개선을 기대할 수 없다.
그리고 기본형 특화 Optional은 Optional 클래스의 유용한 메서드 map, flatMap, filter 등을 지원하지 않으므로 기본형 특화 Optional을 지양해야 한다고 한다. 그리고 기본형 특화 Optional로 생성한 결과는 다른 일반 Optional과 혼용할 수 없다.
찰리
아래 보기의 참, 거짓을 평가하고 거짓이라면 간단한 이유를 말씀해주세요
A. findAny, findFirst, anyMatch, allMatch, limit 는 쇼트서킷 연산이다.
B. sorted, distinct, reduce 는 연산을 하려면 모든 요소가 버퍼에 추가되어 있는 상태에서 해야하기 때문에 연산을 수행하는데 필요한 저장소의 크기는 정해져있지 않다.
C. IntStream은 객체 스트림으로 복원할 수 없다.
D. IntStream의 range메서드를 range(0, 100) 라고 호출했을때 범위는 0~99이다.
E. 스트림의 필터링 메서드인 takeWhile, dropWhile 메서드는 Java 9부터 사용할 수 있다.
답안
A. 참
B. reduce는 아님 거짓
C. 가능함 거짓
D. 참
E. 참
다니
아래 코드는 종료되지 않는 무한 스트림입니다.
- 스트림이 조건을 충족했을 때 종료되도록 코드를 수정해주세요 ! (정답은 한 개 이상일 수 있습니다 !)
- 코드를 실행했을 때 어떻게 출력되는지 결과를 작성해주세요 ~!
IntStream.iterate(10, n -> n * 2)
.filter(n -> n < 30000)
.forEach(System.out::println);
답안
IntStream.iterate(10, n -> n < 30000, n -> n * 2)
.forEach(System.out::println);
IntStream.iterate(10, n -> n * 2)
.takeWhile(n -> n < 30000)
.forEach(System.out::println);
김김
함수형 프로그래밍에서 자주 쓰이는 fold에 매칭되는 reduce는 참으로 유용하죠. 근데 자바에서 사용할 때에는 몇가지 유념할 점이 있습니다.
Stream.of(1,2,3,4).reduce(Integer::sum);
위와 같은 형태의 문장은 단순히 10을 리턴하는 것이 아니라 Optional의 형태로 리턴합니다.
그 이유는 무엇이고, int형 변수로 리턴하도록 하도록 코드를 수정한다면, 어떻게 바꾸어야할까요??
답안
초기값을 설정한다. ex) reduce(0, Integer::sum)
'공부 기록들 > 우테코' 카테고리의 다른 글
[모던 자바 인 액션 스터디] 7장 병렬 데이터 처리와 성능 (0) | 2021.03.28 |
---|---|
[모던 자바 인 액션 스터디] 6장 스트림 활용 (0) | 2021.03.21 |
[모던 자바 인 액션 스터디] 4장 스트림 소개 (0) | 2021.03.14 |
순차적 스트림, 병렬 스트림 그리고 findAny와 findFirst 에 대해 (0) | 2021.03.07 |
함수형 인터페이스를 이용할 때 착각했던 점 (0) | 2021.03.05 |