Stream 사용해서 조건에 맞는 데이터 추출하기

라헬·2022년 12월 24일
0
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName =
			menu.stream()
            .filter(d -> d.getCalories() < 400)// 400칼로리 이하
            .sorted(comparing(Dish::getCalories))// 칼로리로 정렬
            .map(Dish::getName)// 요리명 추출
            .collect(toList());//리스트로 저장

나는
public static final List menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 400, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH)
);
를 사용했기때문에
__[season fruit, rice] 이렇게 출력됐다.

List<String> names = menu.stream()
			        .filter(dish -> {
			          System.out.println("filtering " + dish.getName());
			          return dish.getCalories() > 300;
			        })
			        .map(dish -> {
			          System.out.println("mapping " + dish.getName());
			          return dish.getName();
			        })
			        .limit(3)
			        .collect(toList());
			    System.out.println(names);
List<String> threeHighCaloricDishNames =
menu.stream() // 메뉴에서 스트림을 얻는다.
.filter(dish -> dish.getCalories() > 300) //300칼로리 이상 요리를 필터링해서 파이프라인 연산을 만든다.
.map(Dish::getName) // 요리명을 추출한다
.limit(3) // 3개만 추출한다
.collect(toList()); // 결과를 리스트로 저장

filter메서드는 프레디케이트로 인수를 받아 프레디케이트(boolean) 일치하는 모든 요소를 스트림으로 반환한다.

List<Dish> vegetarianDishes =
menu.stream()
.filter(Dish::isVegetarian) // 채식 요리만 걸러라!
.collect(toList());

다음은 중복제거를 하는 distinct이다.

List<Integer> numbers = Arrays.asList( 2, 3, 5, 1, 2, 4, 7, 4, 7);
numbers.stream()
.filter( i -> i % 2 == 0 ) // 짝수만 가져와!
.distinct()
.forEach(System.out::println);

그럼 2,4 만 출력되는 것을 확인할 수 있다

스트림은 전체를 반복하면서 프레디케이트를 적용하는데 원하는 조건이 나왔을때 반복 작업을 중단하는 방법은 없을까? 바로 Java 9에서 스트림을 효과적으로 선택할 수 있도록 나온 takeWhile, dropWhile을 사용하면 된다

List<Dish> sliceMenu1 =
specialMenu.strea()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());

이렇게 스트림을 슬라이스 할 수 있다.

     정반대로    나머지 요소를 선택하려면?
            ```

List sliceMenu2
= specialMenu.stream()
.dropWhile(dish -> dish.getCalories() < 320 )
.collect(toList());

                                             프레디케이트가 거짓이 되는 지점까지 발견된 요소들을 버리는 기능을 한다. 그렇기에 무한한 남은 요소를 가진 무한 스트림에서 동작한다.
                                             
                                             요소를 건너뛰는 방법은 없을까?
                                             없긴 왜 없을까 skip하면 된다
                                             ```
List<Dish> dishes = menu.stream()
  .filter(d -> d.gerCalories() > 300 )
  .skip(2)
  .collect(toList());

스트림 각 요소에 함수를 적용하는 건?

List<String> words = Arrays.asList("Java", "C++", "C", "Dev", "BTS", "봉준호", "손흥민", "rachel", "let's go!");
		List<Integer> wordLength = words.stream()
				.map(String::length)
				.collect(toList());

그럼 출력 결과로
[4, 3, 1, 3, 3, 3, 3, 6, 9] 이렇게 나올 것이다.

위에서 했던 요리 예제에선 어떻게 적용한다?

List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName)
.map(String::length)
.collect(toList());

반대로 고유 문자들로 이루어진 리스트가 있다면 이걸 다 쪼개버리고 싶다 다 조각내버려!!!

words.stream().map(word -> word.split("")) // 각단어를 개별로 배열변환
.flatMap(Arrays::stream) //생성된 스트림을 하나로 평면화
.distinct() 
.collect(toList());

flatMap은 각 배열을 스트림의 콘텐츠로 매핑한다. 그래서 평면화된 스트림을 반환한다.

적어도 한 요소와 일치하는지 확인하기
menu.stream().anyMatch(Dish::isVegetarian)) {
System.out.println("비건음식 하나쯤은 있대요");
}

그럼 전부다 일치하는지 확인하려면?
menu.stream()
.allMatch(dish -> dish.getCalories(0 < 1000 );
1000칼로리 이하인 메뉴들만 나올 것이다.. 근데 이렇게 칼로리 계산하면서 피곤하게 살아야하나 ㅠ
allMatch가 있으니 그 반대는 없나? noneMatch가 있다

                                         ```

menu.stream().noneMatch(d -> d.getCalories() >= 1000 );

  이 세가지 메서드는 스트림 쇼트서킷 기법을 활용한다 %% || 같은거!
  
  이제 검색을 해볼까나

Optional dish = menu.stream()
.filter(Dish::isVegetarian)
.findAny();

  
  첫번째 요소 찾기

someNumbers.stream()
.map( n -> n * n)
.filter(n -> n % 3 == 0)
.findFirst();

  
  요소의 합도 구할 수 있을까?
  

int sum = numbers.stream(0.reduce(0, (a, b) -> a + b);

  
  reduce는 두 개의 인수를 갖는다
  
  최대값과 최소값 구하기

Optional max = numbers.stream().reduce(Integer::max);
Optional min = numbers.stream().reduce(Integer::min);

  
  근데,, 위에 있는 sum도 좀 더 깔끔하게 짤 순 없을까?
  또 왜 없겠냐,,, 특화 스트림이 있다.

int calories = menu.stream()
.mapToInt9Dish::getCalories) // IntStream 반환
.sum();

  IntStream은 max, min, average등도 지원한다. 스트림이 비어있다면 sum은 0을 반환한다.
  
  근데,,,, 숫자 스트림으로 만든 다음에 원상태인 스트림으로 바꿀 순 없나? 
  IntStream은 정수값만 들어가잖아 Dish같은 다른 값을 반환하고 싶어! 그러면
  또 왜 없겠냐,,
  

IntStream insm = menu.stream().mapToInt(Dish::getCalories);
Stream stream = insm.boxed();

  또한 IntStream에서 0때문에 잘못된 결과가 나올 수 있어서 OptionalInt라는 것도 존재함

OptionalInt maxCalories = menu.stream()
.mapToInt(Dish::getCalories)
.max();

  
  개쩐다...........대박....이렇게 간결하게 코드를 짤 수 있다니!!!!!!
  레거시한 방법으로만 개발해왔던 금융권 개발자인 나에게 정말이지 감동 그잡채다,,,,크흡
  잊지말자 도태되지말자 나도 사용할 수 있는 플젝에선 반드시 써보자는 다짐으로 기록해본다
  
profile
성장하고 싶은 풀스택 개발자

0개의 댓글