ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Stream, 왜 재사용 할 수 없을까
    Java 2021. 3. 1. 18:04

    Stream

    Stream 이란?

    A sequence of elements supporting sequential and parallel aggregate operations.

    Oracle Java8 Document를 보면 위와 같이 순차 및 병렬 집계 작업들을 지원하는 일련의 요소들 이라고 나와있다. Stream을 이용하면 컬렉션 데이터를 처리하는데 필요한 기능들 (ex> 특정 조건에 맞는 대상 수집, 정렬, 특정객체로 변환)을 연결하여 간결하게 처리가 가능하다.

    만약 7천원 이하 메뉴들의 이름을 확인해야할 경우 아래처럼 구현할 수 있다.

    public class Menu {
    
            private String name;
            private int price;
    
            public Menu(String name, int price) {
                this.name = name;
                this.price = price;
            }
    
            public boolean canBuy(int money) {
                return money >= price;
            }
    
            public String getName() {
                return this.name;
            }
        }
    
        public class Menus {
    
            private List<Menu> menus;
    
            public Menus(List<Menu> menus) {
                this.menus = menus;
            }
    
            public List<String> getMenuList(int money) {
                return menus.stream()
                      .filter(menu -> menu.canBuy(money))
                      .map(Menu::getName)
                      .collect(toList());
            }
        }

    Menus.getMenuList() 를 보면 Stream을 이용하여 구매가능한 메뉴들의 이름 List를 반환하고 있다.
    filter() 부분을 보면 List에 속한 요소들을 하나씩 조건에 해당하는지 판단하고 있다. 여기서는 주어진 금액으로 해당 메뉴를 구매할 수 있는지 판단하고 있다.

    그 다음으로 map() 인데 여기서 map은 HashMap과 같은 map이 아니라 A를 B로 매핑한다 의 의미이다. filter에서 통과한 Menu 요소를 메뉴이름이라는 String으로 매핑하는 과정을 처리한다. 마지막으로 이렇게 변환된 메뉴이름들을 한대 모아 List로 반환하는 과정을 .collect(toList()) 에서 처리한다.

    @DisplayName("구매가능한 메뉴이름 확인")
        @Test
        void getMenuListByMoney() {
            //given
            Menu 김치볶음밥 = new Menu("김치볶음밥", 6_500);
            Menu 소불고기김밥 = new Menu("소불고기김밥", 4_500);
            Menu 철판제육볶음밥 = new Menu("철판제육볶음밥", 8_000);
            List<Menu> menuList = Arrays.asList(김치볶음밥, 철판제육볶음밥, 소불고기김밥);
            Menus menus = new Menus(menuList);
    
            //when, then
            assertThat(menus.getMenuList(7_000)).containsExactly(김치볶음밥.getName()
                  , 소불고기김밥.getName());
        }
    
        // 7_000원으로 구매가능한 메뉴는 김치볶음밥, 소불고기김밥 이다.

     


    재사용 불가 🙅

    스트림을 선언해두고 재사용하려고 한다면 IllegalStateException 예외를 만나게 된다.

    java.lang.IllegalStateException: stream has already been operated upon or closed

    이미 작동되었거나 종료됐기때문에 사용할 수 없다고 한다.

    Stream Document를 보면 Stream은 재사용 할 수 없다고 나온다. 스트림은 BaseStream.close() 메서드를 갖고 있고 AutoCloseable을 상속한다. 하지만, 대부분의 Stream은 사용 후 close 할 필요가 없으며 IO자원을 사용하는 (ex> Files.lines(Path, Charset)) 경우에만 close가 필요하다고 말하고 있다.

    Most streams are backed by collections, arrays, or generating functions, which require no special resource management. 
    (If a stream does require closing, it can be declared as a resource in a try-with-resources statement.)

    대부분의 경우는 필요없다고 명시하지만 그래도 재사용은 안된다. 그냥 규칙인 것 같다.

    StackOverflow에도 Stream 재사용 방법에 대한 질문이 있었는데, 해결책으로 Supplier를 이용하는 방법을 제안하고 있었다. ( 좋은방법인지는 모르겠다. 결국 stream을 매번 만들어서 사용함 )

    댓글

Paycis's