본문 바로가기
Book

[독서 기록] 모던 자바 인 액션 6장 스트림으로 데이터 수집

by Renechoi 2023. 1. 8.

 

 

통화별로 트랜잭션을 그룹화한 코드 

 

 

  
        List<Transaction> transactions = Arrays.asList(
                new Transaction(),
                new Transaction(),
                new Transaction()
        );
        
        Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
        
        for (Transaction transaction : transactions){
            Currency currency = transaction.getCurrency();
            List<Transacion> transactionsForCurrency = transactionsByCurrencies.get(currency);
            
            if (transactionsForCurrency == null) {
            	transactionsForCurrency = new ArrayList<>();
                transactionsByCurrencies.put(currency, transactionsForCurrency);
            }
            transactionsForCurrency.add(transaction);
            
        }

 

-> 

May<Currency, List<Transaction>> transactionsByCurrencies = transactions.stream()
	.collect(groupingBy(Transaction::getCurrency));

- 198 ~ 199p 

 

 

여기서는 groupingBy를 이용해서 '각 키 (통화) 버킷 그리고 각 키 버킷에 대응하는 요소 리스트를 값으로 포함하는 맵을 만들라'는 동작을 수행한다. 

- 199p 

 

 

counting 메서드 

long howManyDishes = menu.stream().collect(Collectors.counting());

 

 

최댓값과 최솟값 검색 

 

Comparator<Dish> dishCaloriesComparator = Comparator.comparingInt(Dish::getCalories);

Optoinal<Dish> mostCalorieDish = menu.stream().collect(mayBy(dishCaloriesComparator));

 

- 202p 

 

 

요약 연산 

int totalCalroeis = menu.stream().collect(summingInt(Dish::getCalories));

칼로리로 매핑된 각 요리의 값을 탐색하면서 초깃값(여기서는 0)으로 설정되어 있는 누적자에 칼로리를 더한다. 

- 203p

 

IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories));

- 204p 

 

 

문자열 연결 

String shortMenu = menu.stream().collect(joining());

String shortMenu = menu.stream().map(Dish::getName).collect(joining(", "));

 

- 204p 

 

 

리듀싱 요약 연산 

 

int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));

reducing은 인수 세 개를 받는다.

- 첫 번째 인수는 리듀싱 연산의 시작값이거나 스트림에 인수가 없을 때의 반환값 

- 두 번째 인수는 칼로리 정수로 변환할 때 사용한 반환값 

- 세 번째 인수는 같은 종류의 두 항목을 하나의 값으로 더하는 BinarayOperator 

 

Optional<Dish> mostCalorieDish = menu.stream().collect(reducing( (d1, d2) -> d1.getCalories() > d2.getCalories() ? d1 : d2) );

 

- 206p

 

 

카운팅을 제네릭으로 구현 

 

public static <T> Collector<T, ?, Log> counting(){
	return reducing(0L, e->1L, Long::sum);
}

- 208p 

 

 

Collectors의 groupingBy를 이용한 그룹핑 

 

Map<Dish.Type, List<Dish>> dishesByType = 
	menu.stream().collect(groupingBy(Dish::getType));

스트림 -> 분류함수 -> 그룹핑하여 맵핑 

 

public enum CaloricLevel { DIET, NORMAL, FAT } 

Map<Caloriclevel, List<Dish>> dishesByCaloriclevel = menu.stream().collect(
	groupingBy(dish -> {
    	if (dish.getCalories() <= 400) retrun CaloricLevel.DIET; 
        else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
        else return CaloricLevel.FAT; 
    }
   ));

- 210 ~ 211p 

 

 

다 수준 그룹화 

 

Map<Dish.TYPE, Map<CaloricLevel, List<Dish>>> dishesByTypeCaloricLevel = 
	menu.stream().collect(
    	groupingBy(Dish::getType,			// 첫 번째 수준의 분류 
        	groupingBy(dish -> {			// 두 번째 수준의 분류 
            	if (dish.getcalories() <= 400) return CaloricLevel.DIET;
                else if (dish.getcalories() <= 700) return CaloricLevel.NORMAL;
                else return CaloricLevel.FAT;
            })
        )
    );

 

- 214p 

 

 

분류하고 연산 하기 

 

May<Dish.TYPE, Dish> mostCaloricByType = 
	menu.stream()
    	.collect(groupingBy(Dish::getType,					// 분류 
        	collectingAndThem(
            	maxBy(comparingInt(Dish::getCalories)),		// 연산 
                Optional::get)));							// 반환

 

 

 

partioning => 불리언 값으 리턴하는 분할 

 

Map<Boolean, List<Dish>> partitionedMenu = menu.stream().collect(partioningBy(Dish::isVegetarian));

- 220p

 

 

stream을 이용한 소수 판단하기 

 


public boolean isPrime(int candidate) {
    // 2부터 candidate 미만 자연수를 생성한다 
    // 스트림의 모든 정수를 candidate로 나눌 수 없으면 true를 반환한다. 
    return IntStream.range(2, candidate).noneMatch(i -> candidate % i == 0);
}

public boolean isPrime2(int candidate){
    // 제곱근 이하로 수를 제한하기 
    int candidateRoot = (int) Math.sqrt(  (double) candidate);
    return IntStream.rangeClosed(2, candidateRoot).noneMatch(i -> candidate % i ==0);
}

public Map<Boolean, List<Integer>> partitionPrimes(int n){
    // 파티셔닝을 이용해 소수와 비소수를 구분한다. 
    return IntStream.rangeClosed(2, n).boxed().collect(partitioningBy(candidate -> isPrime(candidate)));
}

- 222p 

 

성능 개선하기 

 

 

isPrime 메서드로 중간 결과 리스트를 전달하기 

public static boolean isPrime(List<Integer> primes, int candidate) {
	return primes.stream().noneMatch(i -> candidate % i == 0);
    }

 

제곱근 보다 작은 소수만 사용하도록 최적화하기 

 

-> 정렬된 리스트와 프레디케이트를 인수로 받아 리스트의 첫 요소에서 시작해서 프레디케이트를 만족하는 가장 긴 요소로 이루어진 리스트를 반환하는 takeWhile이라는 메서드를 구현한다. 

public static boolean isPrime(List<Integer> primes, int candidate) {
	int candidateRoot = (int) Math.sqrt( (double) candidate);
    return primes.stream()
    	.takeWhile(i -> i<=candidateRoot)
        .noneMatch(i -> candidate % i == 0);
        
    }

 

- 233p 

 

 

반응형