본문 바로가기
카테고리 없음

모던 자바 인 액션 스터디 2. 스트림.

by 손너잘 2021. 3. 7.

찰리

======================
모던 자바 인 액션 2회차
4장. 스트림 소개
======================
맥북 프로가 사고싶은 찰리는 배민 커넥트를 시작하는데
찰리의 배달을 받는 조건은 다음과 같습니다.

  • 배달장소가 “요기요“인 주문은 받지 않습니다.
  • 거리가 2000 미만인 주문만 받습니다.

추가로 다음의 정보도 확인하려합니다.

  • 전체 배달목록 중 “우테코“가 포함되는 장소에서 주문한 갯수

아래와 같이 Iterator 객체를 사용해서 배달요청을 필터링하는 코드를 만들었습니다. 하지만 다음날 제이슨의 강의에서 Stream API를 배우고 코드를 리팩토링 하기로 결심합니다.
아래의 main 메서드 안에있는 코드에 Stream API를 적용시켜주세요!

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class StreamEx {
    public static void main(String[] args) {
        List<WoowahanOrder> deliveries = Arrays.asList(
                new WoowahanOrder("짜장면2", "우테코 루터회관", 1500),
                new WoowahanOrder("교촌허니콤보", "우테코 백엔드 강의실", 1999),
                new WoowahanOrder("햄버거", "우테코 프론트엔드 강의실", 2000),
                new WoowahanOrder("신호등 치킨", "요기요 본사", 525),
                new WoowahanOrder("민트 초코 피자", "요기요테크코스", 1525)
        );
        List<WoowahanOrder> pickedOrders = new ArrayList<>();
        Iterator<WoowahanOrder> iterator  = deliveries.iterator();
        while (iterator.hasNext()) {
            WoowahanOrder order = iterator.next();
            if (!order.getArrivalAddress().contains("요기요") && order.getDistance() < 2000) {
                pickedOrders.add(order);
            }
        }
        Iterator<WoowahanOrder> iterator2  = deliveries.iterator();
        int count = 0;
        while (iterator2.hasNext()) {
            WoowahanOrder order = iterator2.next();
            if (order.getArrivalAddress().equals("우테코")) {
                count++;
            }
        }
        System.out.println(pickedOrders.toString());
        System.out.println("우테코에서 요청한 주문 : " + count);
    }
}
class WoowahanOrder {
    String food;
    String arrivalAddress;
    int distance;
    public WoowahanOrder(String food, String arrivalAddress, int distance) {
        this.food = food;
        this.arrivalAddress = arrivalAddress;
        this.distance = distance;
    }
    public String getFood() {
        return food;
    }
    public String getArrivalAddress() {
        return arrivalAddress;
    }
    public int getDistance() {
        return distance;
    }
    @Override
    public String toString() {
        return "WoowahanOrder{" +
                "food='" + food + '\'' +
                ", arrivalAddress='" + arrivalAddress + '\'' +
                '}';
    }
}

나의 답

더보기
System.out.println(deliveries.stream()
        .filter(wo -> !wo.getArrivalAddress().contains("요기요"))
        .filter(wo -> wo.getDistance() < 2000)
        .collect(toList())
        .toString());

System.out.println("우테코에서 요청한 주문 :" + deliveries.stream()
        .filter(wo -> wo.getArrivalAddress().contains("우테코"))
        .count());

손너잘

4장 문제손너잘은 숫자를 정렬하고자 한다. 이번 장에서 스트림을 배운 손너잘은 스트림을 이용해 이 문제를 해결해 보고자 했다.
스트림을 처음 배운 손너잘을 각 스트림의 사용법을 제대로 확인하고자 아래와 같은 소스를 짜고, 그 로그를 확인하기 위해 i를 프린트 시켰다.
그런데 이게 무슨일인가?? 창에는 아무 print도 찍히지 않았다;;;; 이러한 현상이 발생한 이유를 최대한 자세히 서술해 보시오.

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1,2,3,6,2,7,4);
    Stream<Integer> stream = list.stream();
    Stream<Integer> sorted = stream.sorted();
    IntStream intStream = sorted.mapToInt(i -> {
        System.out.println(i);
        return i;
    });
}

나의 답

더보기

스트림의 지연된 연산 때문이다. 소스에 있는 연산들은 모두 중간 연산들이다. 스트림의 지연된 연산은 최종 연산이 수행되기 전까지 중간 연산을 수행하지 않는다. 중간 연산은 단지 최종 연산의 연산 법을 정의할 뿐이다. 따라서 위 소스는 동작하지 않는다.


나봄

public static void main(String[] args) {
    List<Integer> integers = Arrays.asList(1, 2);
    integers.stream()
            .peek(i -> System.out.println("peek : " + i))
            .forEach(i -> System.out.println("foreach : "+ i));
}

위의 코드는 과연 어떤 순으로 시작이 될까요!

  1. peek :1, peek :2, foreach :1, foreach :2 순으로 찍힌다.
  2. peek :1, foreach :1, peek :2, foreach :2 순으로 찍힌다.

나의 답

더보기

답은 2번.

peek은 스트림 연산 중간의 결과를 출력하기 위한 메서드 이다. 스트림의 동작과정을 살펴보면, 각 요소가 스트림 메서드들을 타고 내려오면서 결과를 도출한다. 따라서 각 요소마다 peek -> foreach 순으로 실행된다.


웨지

4장 문제
웨지는 맥x날드 알바를 다년간 한적이 있습니당ㅎㅎ
여러 메뉴를 다양한 방식으로 디스플레이 해야하는데, 매니저의 요구사항을 받아 스트림으로 만들어 놓으려고 해요. 상황에 알맞은 중간 연산을 맵핑해주세요!

일단 메뉴를 전부 줄게, 다 적어나봐..

일단 메뉴를 전부 줄게, 다 적어나봐..
1. 다이어트 메뉴를 만들어야해, 300칼로리가 안 되는 메뉴들만 뽑아봐
2. 그중 가격이 가장 비싼걸 찾아야 하거든?
3. 3개까지만.
4. 그 메뉴들의 가격 목록을 만들어줘

---

a. map()
b. limit()
c. sorted()
d. filter()

나의 답

더보기

d - c - b - a


라이언

4장 문제
뭐라고 출력할지 적으시오, 또한 해당 코드에서 쇼트서킷과 루프퓨전에 연관지어 서술하시오.

List<String> result = Stream.of("우테코", "짱짱", "모두들 다같이", "루터에서 열심히", "스터디 합시다", "화이팅 화이팅")
        .filter(s -> {
            System.out.println("filtering " + s);
            return s.length()>=4;
        })
        .map(s -> {
            System.out.println("mapping " + s);
            return s;
         })
        .limit(3)
        .collect(Collectors.toList());
System.out.println("결과:");
result.forEach(System.out::println);

나의 답

더보기

모두들 다같이 루터에서 열심히 스터디 합시다

 

스트림의 게으른 특성으로 인해 filter와 map과 같이 서로 다른 연산의 병합이 가능하다. -> 루프 퓨전
또한 limit을 통해 스트림의 길이를 제한하며, 스트림의 길이가 특정 숫자가 되면 뒤의 연산을 무시한다. -> 쇼트서킷

 

따라서 위와 같은 결과가 나온다.


중간곰

4장 문제
아래 두 코드는 Integer list를 정렬, 중복제거 하는 코드이다.
자신이라면 둘 중 어떤 코드를 선택할지, 어떤 차이점이 있을지 생각해보자. (차이가 없을 수도 있다.)

numbers.stream()
                .distinct()
                .sorted()
                .collect(Collectors.toList());
numbers.stream()
                .sorted()
                .distinct()
                .collect(Collectors.toList());

나의 답

더보기

후자를 선택할 것 같다. distinct가 어떻게 구현되어있는지는 잘 모르겠으나, 정렬되어 있는 상황에서의 distinct 연산이 더욱 낮은 시간복잡도를 나타낼 것으로 예상된다.

 

* 라이언의 자료 : https://stackoverflow.com/questions/43806467/java-streams-how-to-do-an-efficient-distinct-and-sort


완태

쉽게 쉽게 가겠습니다 ㅎㅎ4장 문제
어떤 부분이 잘못됬을까요? 알아맞춰보세요!

int count = menu.stream()
                .filter(d -> d.getPrice() > 10000)
                .distinct()
                .limit(3)           
                .count();

나의 답

더보기

반환형이 잘못되었다. Long으로 반환되어야 한다.


다니

4장 문제공백을 채워주세요!

  • 스트림 API의 특징에는 ____, ____, ____가 있다.
  • 스트림은 ____, ____, ____으로 이루어진다.
  • 자바 컬렉션은 ____, 스트림은 ____을 사용한다.
  • 중간 연산은 파이프라인으로 구성되어 최종 연산에서 한 번에 처리된다. 이를 ____ 계산된다고 한다.
  • 중간 연산은 ____을 반환하고, 최종 연산은 ____을 반환한다.
  • 중간 연산의 예시로는 ____가 있고, 최종 연산의 예시로는 ____가 있다.

나의 답

더보기

1. 조립의 가능, 병렬성, 선언형

2. 데이터소스, 중간연산, 최종연산

3. 외부반복, 내부반복

4. 게으르게

5. 스트림, 결과

6. [map, filter, reduce ...], [count, findAny, findFirst ...]


검프

4장 ——-
검프는 사람들의 이름의 글자수가 궁금하다.
stream을 사용하여 사람들의 이름의 글자수를 List에 저장하기로 한다.

글자수의 2배는 어떤 값일까?

라는 생각이 문득 든 검프, 다시 stream을 사용하여 글자수에 2배를 한 값을 List에 저장하기로한다.

문제, 현재 코드는 개발자를 힘들게한다. 그 이유를 찾고, 각자 리팩토링 해보자!
public class Application {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("손너잘", "중간곰", "찰리", "웨지");
        Stream<String> stream = names.stream();
        List<Integer> nameLengths = stream.map(String::length)
                .collect(Collectors.toList());
        System.out.println("nameLengths = " + nameLengths);
        List<Integer> doubleNameLengths = stream.map(map -> map.length() * 2)
                .collect(Collectors.toList());
        System.out.println("doubleNameLengths = " + doubleNameLengths);
    }
}

나의 답

더보기

스트립의 재사용은 불가능 하다.

public class Application {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("손너잘", "중간곰", "찰리", "웨지");
        
        List<Integer> nameLengths = names.stream().map(map -> map.length())
                .collect(Collectors.toList());
        System.out.println("nameLengths = " + nameLengths);
        
        List<Integer> doubleNameLengths = names.stream().map(map -> map.length() * 2)
                .collect(Collectors.toList());
        System.out.println("doubleNameLengths = " + doubleNameLengths);
    }
}

파즈

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
long count = list.stream().peek(System.out::println).count();
System.out.println(count);

의 출력 결과를 작성해보시오~~

나의 답

더보기

5

 

파즈의 답변

 API Note:
An implementation may choose to not execute the stream pipeline (either sequentially or in parallel) if it is capable of computing the count directly from the stream source. In such cases no source elements will be traversed and no intermediate operations will be evaluated. Behavioral parameters with side-effects, which are strongly discouraged except for harmless cases such as debugging, may be affected. For example, consider the following stream:     IntStream s = IntStream.of(1, 2, 3, 4);
     long count = s.peek(System.out::println).count();The number of elements covered by the stream source is known and the intermediate operation, peek, does not inject into or remove elements from the stream (as may be the case for flatMap or filter operations). Thus the count is 4 and there is no need to execute the pipeline and, as a side-effect, print out the elements.

IntStream 인터페이스의 ``count()`` 메서드에 대한 설명이다.
기존의 스트림에서 바로 count할 수 있다고 판단되면 파이프라인을 안거친다.


김김

4장
현대 자바 개발자가 stream을 써야하는 이유는 단순히 함수형 언어처럼 코드를 작성함으로써 얻을 수 있는 가독성 뿐만 아니라 성능에도 지대한 영향을 미치기 때문입니다. 스트림을 사용함으로써 얻을 수 있는 성능상의 이점을 다음과 같이 2가지 키워드로 정리해 보았습니다. 빈칸 ①, ②, ③을 채워주세요! (한글이 아니어도 되고, 책에서 번역된 용어와 토씨하나 같을 필요 역시 없습니다.)A. __①__: 멀티 코어 컴퓨터가 대세가 되어감에 따라 멀티 쓰레딩을 활용한 프로그래밍은 더욱 중요해졌고, 자바 8 이후 개발자는 parallelStream을 활용하여 쉽게 __①__을 얻을 수 있게 되었습니다. 물론 그 이전에도 synchronized 키워드를 통해 코드를 작성하면 __①__을 얻을 수 있었으나 꽤 악랄한 난이도를 자랑했다고 합니다.B. 최적화: '모던 자바 인 액션'에서는 스트림을 이용한 최적화 원리로 ____ evaluation과 ____ evaluation을 제시하고 있습니다.
B-____ evaluation: ____ evaluation란 회로 이론에서 유래한 용어입니다. 연산의 중간에 이미 확실하게 결과를 알 수 있는 시점에 도달하면 남은 연산을 하지 않고 답을 내버리는 방식을 뜻하는 것으로, 이런 프로그램의 흐름이 마치 회로에서 합선이 발생했을 때 전류가 합선에 의한 지름길을 따라 흐르는 것과 같다하여 지어졌습니다.
B-____ evaluation: ____evaluation이란 해당 연산이 필요시에만 수행되는 방식을 뜻하는데, 마치 필요할 때가 되어서야 실행하는 모습이 무엇이든 미리 해두려고 하는 부지런한 성격과는 반대된다는 의미에서 지어졌습니다.

 

나의 답

더보기

1. 병렬성

2. 쇼트서킷

3. lazy

 

댓글