JAVA 스트림 가공과 결과

금송·2024년 10월 4일
0

이론

목록 보기
22/26
post-thumbnail

데일리 문제 풀이

  1. 람다식에 대한 설명으로 틀린것은 ? 4

4번의 @xxx의 어노테이션은 단순히 정보전달용의 문자고 함수형 인터페이스만 람다식으로 표현이 가능하다. 함수형 인터페이스란 하나의 추상 메소드만 선언된 인터페이스이다.

  1. 메소드 참조에 대한 설명으로 틀린 것은? 4

생성자 참조인 “클래스::new”는 매개변수가 있어도 호출할 수 있다.

  1. 잘못 작성한 람다식은? 2

매개변수가 하나일 경우 괄호가 생략 가능하지만 2개 이상일 경우 괄호를 생략할 수 없다.

그래서 제대로 된 람다식은 (a , b) → a * b

  1. 다음 코드는 컴파일 에러가 발생합니다. 그 이유는?

함수형 인터페이스 람다식 안의 x의 값을 변경하고 있는데 (x = x * 10) 여기서 오류가 났을 것이다.

메서드의 매개변수는 중괄호 블럭 안에서 final변수인데 해당 값을 위처럼 바꾸려고 해서 오류가 남.

  • 수정된 코드
    package chap12.dailyquiz;
    
    import java.util.function.IntSupplier;
    
    public class LambdaQuiz_4 {
        public static int method(int x, int y) {
            IntSupplier supplier = () -> {   // final int x = ?
                // x *= 10;        x = x * 10
                int a = x * 10;
                // int result = x + y;
                int result = a + y;
                return result;
                // result 2줄 합쳐서 적으면 -> return a + y;
            };
            int result = supplier.getAsInt();
            return result;
            // result 2줄 합쳐서 적으면 -> return supplier.getAsInt();
        }
    
        public static void main(String[] args) {
            System.out.println(method(3, 5));       // 35
        }
    }
  1. 다음은 배열 항목 중 최대값 또는 최소값을 찾는 코드입니다. maxOrMin() 메소드의 매개값을 람다식으로 기술해보세요.
  • 문제 코드
    public class LambdaExample_5 {
        private static int[] scores = {10, 50, 3};
    
        public static int maxOrMin(IntBinaryOperator operator) {
            int result = scores[0];
            for (int score : scores) {
                result = operator.applyAsInt(result, score);
            }
            return result;
        }
    
        public static void main(String[] args) {
            int max = maxOrMin(
                    /* 최대값 얻기 구현 */
            );
            System.out.println("최대값 : " + max);
            
            int min = maxOrMin(
                    /* 최소값 얻기 구현 */
            );
            System.out.println("최소값: " + min);
        }
    }
    
    // 결과값
    최대값 : 50
    최소값: 3
  • 풀이 코드
    package chap12.dailyquiz;
    
    import java.util.function.IntBinaryOperator;
    
    public class LambdaQuiz_5 {
        private static int[] scores = {10, 50, 3};
    
        public static int maxOrMin(IntBinaryOperator operator) {
            int result = scores[0];
            for (int score : scores) {
                result = operator.applyAsInt(result, score);
            }
            return result;
        }
    
        public static void main(String[] args) {
            int max = maxOrMin(
                    /* 최대값 얻기 구현 */
                    // (int x, int y) -> x >= y ? x : y
                    /* 해당 구문을 자바에서 제공해주는 메서드 Math.max(x,y)
                        (int x, int y) -> {
                            if(x >= y){
                                return x;
                            } else {
                                return y;
                            }
                        }
                    */
                    (x, y) -> Math.max(x,y)     // == Math::max
            );
            System.out.println("최대값 : " + max);
    
            int min = maxOrMin(
                    /* 최소값 얻기 구현 */
                    // (int x, int y) -> x <= y ? x : y
                    (x, y) -> Math.min(x,y)     // == Math::min
            );
            System.out.println("최소값: " + min);
        }
    }
    // 결과값 
    최대값 : 50
    최소값: 3
  1. 다음은 학생의 영어 평균 점수와 수학 평균 점수를 계산하는 코드입니다. avg() 메소드를 선언해보세요.
  • 문제 코드
    public class LambdaExample_6 {
        private static Student[] students = {
                new Student("홍길동", 90, 96),
                new Student("저팔계", 95, 93)
        };
        
    
    		/*  avg() 메소드 작성
        */
    
        public static void main(String[] args) {
            double englishAvg = avg( s -> s.getEnglishScore() );
            System.out.println("영어 평균 점수: " + englishAvg);
            
            double mathAvg = avg( s -> s.getMathScore() );
            System.out.println("수학 평균 점수: " + mathAvg);
        }
        
        public static class Student {
            private String name;
            private int englishScore;
            private int mathScore;
    
            public Student(String name, int englishScore, int mathScore) {
                this.name = name;
                this.englishScore = englishScore;
                this.mathScore = mathScore;
            }
    
            public String getName() {
                return name;
            }
    
            public int getEnglishScore() {
                return englishScore;
            }
    
            public int getMathScore() {
                return mathScore;
            }
        }
    }
  • 풀이 코드
    package chap12.dailyquiz;
    
    import java.util.function.Function;
    
    public class LambdaQuiz_6 {
        private static Student[] students = {
                new Student("홍길동", 90, 96),
                new Student("저팔계", 95, 93)
        };
    
        /*  avg() 메소드 작성
         */
        static double avg(Function<Student, Integer> function) {
            // 1. 점수 다 더하기 , 2. 총점수 / students.length
            int score = 0;  // 미리 변수 선언 하여 차차 점수 더해주기
            for(Student student : students) {
                score += function.apply(student);   // 점수 다 더하기
            }
            return (double) score / students.length; // 평균값 구해주기
        }
    
        public static void main(String[] args) {
            double englishAvg = avg( s -> s.getEnglishScore() );
            // input : Student, output : int
            // input, output 모두 있는 함수형 인터페이스 -> Function<Student, Integer>
            System.out.println("영어 평균 점수: " + englishAvg);
    
            double mathAvg = avg( s -> s.getMathScore() );
            System.out.println("수학 평균 점수: " + mathAvg);
        }
    
        public static class Student {
            private String name;
            private int englishScore;
            private int mathScore;
    
            public Student(String name, int englishScore, int mathScore) {
                this.name = name;
                this.englishScore = englishScore;
                this.mathScore = mathScore;
            }
    
            public String getName() {
                return name;
            }
    
            public int getEnglishScore() {
                return englishScore;
            }
    
            public int getMathScore() {
                return mathScore;
            }
        }
    }
    // 결과값
    영어 평균 점수: 92.5
    수학 평균 점수: 94.5
  1. 6번의 main() 메소드에서 avg() 호출할 때 매개값으로 준 람다식을 메소드 참조로 변경해보세요.
  • 문제 코드
    double englishAvg = average( s -> s.getEnglishScore() );
    -> double englishAvg = avg (      );
    
    double mathAvg = average( s -> s.getMathScore() );
    -> double mathAvg = avg (        );
  • 풀이 코드
    double englishAvg = average( s -> s.getEnglishScore() ); // 람다식을 메소드 참조로 변경 
    																												 // Student::getEnglishScore
    -> double englishAvg = avg (Student::getEnglishScore);
    
    double mathAvg = average( s -> s.getMathScore() );
    -> double mathAvg = avg (Student::getMathScore);

스트림

스트림 = 데이터 흐름

  1. 스트림 생성 : 스트림 인스턴스 생성 (배열이나 컬렉션을 스트림 인스턴스로 변환)
  2. 스트림 가공 : 원하는 결과를 만들어가는 중간 작업
  3. 스트림 결과 : 최종 결과를 만들어내는 작업

→ 자바에서 Stream로 제공

컬렉션으로 스트림 객체 생성

배열로 스트림 객체 생성

숫자 범위로 스트림 객체 생성 (IntStream.range(1,5) // [1,2,3,4])

파일로 스트림 객체 생성

스트림 연결 (concat())

필터링 - distinct, filter

필터링은 과정에서 가공에 해당되는 부분.

Stream<T> stream = // 스트림 객체 생성

// 메서드 체이닝 : 메서드들을 엮어줄 수 있는 방식
stream.distinct()
			.filter()
			.method2() // -> 리턴타입 Stream<>
			.method3()
			.......
// 리턴 타입이 스트림이라 스트림 관련 메서드로 메서드 체이닝이 가능함.
// 디자인 패턴 - 코드를 효율적으로 작성하는 방법론. 그중에 메서드 체이닝으로 작성된 패턴은 빌더 패턴.

distinct

중복을 제거하기 위해 사용하는 메서드

Stream의 경우 “Object.equals(Object)”가 true이면 동일한 객체로 판단

IntStream, LongStream, DoubleSteam의 경우 값이 동일한 경우 중복을 제거한다

List<String> list = Arrays.asList("a", "b","b","b","b", "c", "c", "c");
        list.stream()
	          .distinct()     // 중복 제거 메서드
	          .forEach(x -> System.out.print(x));   //== .forEach(System.out::print);
	          //여기서 forEach는 stream의 마지막 단계에 해당
// 결과값
abc

filter

주어진 Predicate가 true인 것만 필터링한다.

Predicate란 매개변수를 받아 boolean 값을 반환하는 함수라고 생각해주시면 되고 즉, 조건이 참이면 true, 거짓이면 false를 리턴한다.

List<String> sentences = Arrays.asList("김밥", "김밥", "김치", "나비", "나방");
sentences.stream()
    .filter(str -> str.startsWith("김"))     // 앞글자가 김인 글자만 추출 -> 여기까지만 하면 [김밥 김밥 김치] 출력
    .distinct()                             // 중복 제거
    .forEach(System.out::println);
// 결과
김밥
김치
List<String> sentences = Arrays.asList("김밥", "김밥", "김치", "나비", "나방");
Set<String> set = sentences.stream()
                          .filter(str -> str.startsWith("김"))     // 앞글자가 김인 글자만 추출
                          .distinct()                             // 중복 제거
                          .collect(Collectors.toSet());           // Set<String>
                        //.forEach(System.out::print);
System.out.println(set);

// 결과
[김밥, 김치]
  • 실습 코드
    package chap13;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /*
    *  13.3장 필터링
    * */
    public class FilteringExample {
        public static void main(String[] args) {
            List<String> list = Arrays.asList("a", "b","b","b","b", "c", "c", "c");
            list.stream()
                    .distinct()     // 중복 제거 메서드
                    .forEach(x -> System.out.print(x));   //== .forEach(System.out::print);
                    //여기서 forEach는 stream의 마지막 단계에 해당
            System.out.println("\n====================");
            List<String> sentences = Arrays.asList("김밥", "김밥", "김치", "나비", "나방");
            Set<String> set = sentences.stream()
                                      .filter(str -> str.startsWith("김"))     // 앞글자가 김인 글자만 추출
                                      .distinct()                             // 중복 제거
                                      .collect(Collectors.toSet());           // Set<String>
                                    //.forEach(System.out::print);
            System.out.println(set);
            System.out.println("\n====================");
            String[] dupArray = {"nwjns","nwjns","nwjns","nwjns","nwjns"}; // 중복된 요소를 가진 배열
            Set<String> hashset = Arrays.stream(dupArray).collect(Collectors.toSet());
            List<String> arrayList = Arrays.stream(dupArray).distinct().toList(); // 리스트 배열을 반환. method(배열)
            System.out.println(hashset);
            System.out.println(arrayList);
            System.out.println("\n===================");
    
        }
    }
    // 결과값
    abc
    ====================
    [김밥, 김치]
    
    ====================
    [nwjns]
    [nwjns]
    
    ===================

필터링 - map, flatMap

map

map 메소드는 스트림의 요소를 하나씩 특정 값으로 변환한다.

변환된 값은 새로운 스트림으로 만들어진다.

각각의 값을 각각 가공해주고 싶을 때 사용.

flatMap

map과 같이 다른 값으로 대체하는 건 같지만 각각의 요소를 스트림으로 반환.

이때 대체하는 값이 스트림일 경우 flatMap을 사용

// flatmap  2차원 -> 1차원
Integer[][] array = {{1,2},{3,4}};
Arrays.stream(array)
        .flatMap(element -> Arrays.stream(element))
        .forEach(System.out::println);
  • 실습 코드
    package chap13;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class MappingExample {
        public static void main(String[] args) {
            List<String> list = Arrays.asList("a", "b", "c", "d", "e");
            list.stream()
                    .map(element -> element.toUpperCase())      // == String::toUpperCase
                    .forEach(System.out::print);        // ABCDE
    
            // flatmap  2차원 -> 1차원
            Integer[][] array = {{1,2},{3,4}};
            Arrays.stream(array)
                    .flatMap(element -> Arrays.stream(element))
                    .forEach(System.out::println);
    
            // flatmap
            List<String> sentences = Arrays.asList("Hello Newjeans", "Newjeans world");
            String[] strArry1 = {"Hello", "Newjeans"};
            String[] strArry2 = {"Newjeans", "world"};
            sentences.stream()
                    .flatMap(element -> Arrays.stream(element.split(" ")))
                    .forEach(System.out::println);
            List<String> collect = sentences.stream()
                    .flatMap(element -> Arrays.stream(element.split(" ")))
                    .collect(Collectors.toList());
            System.out.println(collect);        // [Hello, Newjeans, Newjeans, world]
    
        }
    }
    // 결과값
    ABCDE1
    2
    3
    4
    Hello
    Newjeans
    Newjeans
    world
    [Hello, Newjeans, Newjeans, world]
  • 문제

```java
List<Integer> numbers = Arrays.asList(1,2,3,4,5)
1)
List<Integer> squares = numbers.stream()
											        .map(x->x*x)
											        .collect(Collectors.toList());
2)
numbers = numbers.stream().map(i -> (int) Math.pow(i, 2)).collect(Collectors.toList());
```

```java
List<String> sentences = Arrays.asList("Hello:world", "Java:Stream:flatMap", "Functional:programming");

List<String> collect = sentences.stream()
    .flatMap(element -> Arrays.stream(element.split(":")))
    .collect(Collectors.toList());
System.out.println(collect);
```

Q. 이차원 컬렉션에 들어있는 정수들을 하나의 컬렉션으로 변환해주세요

List<List<Integer>> numbers = Arrays.asList(
       Arrays.asList(1, 2, 3),
       Arrays.asList(4, 5, 6),
       Arrays.asList(7, 8, 9)
);
List<Integer> numbers2 = numbers2.stream()
																.flatMap(x -> x.stream())
																.toList();
System.out.println(numbers2)

List<Person> people = Arrays.asList(
    new Person("Alice", 30),
    new Person("Bob", 25),
    new Person("Charlie", 28)
);

List<String> names = people.stream()
                           .map(Person::getName)
                           .collect(Collectors.toList());

Q4-추가문제
-> 나이가 28세 이상인 사람들의 이름 List 출력 : [Alice, Charlie]

  // 나이가 28세 이상인 사람들의 이름 [Alice, Charlie]
  List<String> peopleList = people.stream()
          .filter(person -> person.getAge() >= 28)
          .map(person -> person.getName())
          .collect(Collectors.toList());   // == toList()
  System.out.println(peopleList);
  

정렬 - sorted

말 그대로 스트림의 요소들을 정렬하기 위해 사용하는 메서드

옵션을 주지않으면 디폴트로 오름차순으로 정렬

만약 역순으로 내림차순 하고싶다면 .sort(Comparator.reverseOrder())을 써주면 된다.

public class SortedExample {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("e", "a", "c", "q", "w", "e", "r");

        // 오름차순 정렬
        List<String> sortedList = list.stream()
                                    .sorted()
                                    .toList();
        System.out.println(sortedList);

        // 내림차순 정렬
        List<String> sortedList2 = list.stream()
                                    .sorted(Comparator.reverseOrder())
                                    .toList();
        System.out.println(sortedList2);
    }
}

루핑(looping) - peek, forEach

루핑은 요소 전체를 반복하여 어떠한 작업을 수행하는 것을 말한다.

peek - 가공하는 메서드

forEach - 결과를 리턴하는 메서드

peek

그냥 확인해본다는 단어의 뜻처럼 특정 결과를 반환하지 않는다.

최종 “결과”를 집계하는 메소드를 호출하지 않으면 아무 일도 일어나지 않는다.

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1,2,3,4,5);

    // 짝수를 걸러내서 총 합 구하기
    int sum = list.stream()
            .mapToInt(element -> element)   // 여기서 primitive 타입으로 변환, Integer -> int
            .filter(element -> element % 2 == 0)
            .peek(System.out::println)
            .sum();  // primitive 타입이어야 연산 가능. int로 변환 // 6
    System.out.println("짝수들의 총 합 : " + sum);
}

IntStream intStream = list.stream()
						            .mapToInt(element -> element)
						            .filter(element -> element % 2 == 0)
						            .peek(System.out::println);     // 가공단계라 출력해도 아무것도 안나옴. 
list.stream()
        .mapToInt(element -> element)
        .filter(element -> element % 2 == 0)
        .peek(System.out::println)     // 아래에 호출문이 있어서 2 4 출력.
        .sum();

forEach

forEach는 peek과 다르게 “결과” 단계에서 사용하는 메서드

따라서 sum과 같은 “결과” 메소드와 중복해서 사용할 수 없다.

// forEach 사용해서 필터링된 stream 요소 출력
List<Integer> list2 = Arrays.asList(1,2,3,4,5);
list2.stream()
        .filter(x -> x % 2 == 0)
        .forEach(x -> System.out.println(x));

수집 - collect()

수집 메서드는 결과 단계에서 사용

collect

collect는 스트림의 최종 단계에서 사용하는 메서드

어떤 요소를 어떤 컬렉션에 수집할지 결정할 수 있다.

스트림으로 가공한 데이터를 최종적으로 컬렉션을 반환한다고 생각해주면 됨.

// 자주 사용하는 작업은 Collectors 클래스에서 제공
Collectors.toSet()
Collectors.toList()
...
Collectors.toCollection()

Collectors에서 제공하는 메서드

Collectors 정적 메소드설명
toList요소를 List에 저장
toSet요소를 Set에 저장
toCollection요소를 특정 컬렉션에 저장
toMap요소를 Map에 저장

그룹핑하여 수집

collect 메서드는 컬렉션의 요소들을 그룹핑해서 Map 객체를 생성하는 기능도 제공한다.

Collectors의 groupingBy를 사용하면 된다.

grouping 시각화 예시

groupingBy의 첫 번째 파라미터는 그룹핑을 위한 키 값이다. 첫 번째 파라미터 n → n는 요소 값 그 자체를 키로 사용한다는 의미이다. 두 번째 파라미터는 집계 함수이다. 그룹핑 후 어떻게 집계할 것인지 정할 수 있는데 여기서는 단순하게 그룹 별 카운트 코드만 작성하였다.

public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 1, 2, 2, 2, 3, 4, 5, 5, 5);
        // 1 : 2개
        // 2 : 3개
        // 3 : 1개
        // 4 : 1개
        // 5 : 3개
        Map<Integer, Long> collected = list.stream()
                .collect(
                        Collectors.groupingBy(
                                n -> n,
                                Collectors.counting()
                        )
                );
        collected.forEach((key, value) ->
                System.out.println(key + " : " + value + "개"));
    }
profile
goldsong

0개의 댓글

관련 채용 정보