본문 바로가기
개발/Java

Collection Framework API

by BellOne4222 2024. 1. 29.

Collection Framework API


람다와 스트림 API

  • 함수형 인터페이스

    • 단 하나의 추상메서드를 가진 인터페이스

    • 자바 8부터 함수형 인터페이스를 사용하면 람다 표현식을 활용 할 수 있다.

    • 인터페이스의 구현체를 람다 표현식으로 구현

    • @FunctionalInterface 어노테이션을 사용하여 표시

    • 어노테이션은 선택 사항이지만 컴파일러에게 해당 인터페이스가 함수형 인터페이스 임을 알려주는 역할을 합니다.

    • 사용하는 이유

      • 람다 표현식 지원
      • 메서드 참조
      • Stream API와의 통합
      • 병렬 프로그래밍
      • 코드 재사용
    • 인터페이스는 new를 사용하여 객체 생성을 할 수 없기 때문에 익명 클래스를 사용하여 구현체 구현

      package fc.java.model2;
      
      @FunctionalInterface // 함수형 인터페이스
      public interface MathOperation {
        public int operation(int x, int y); // 추상 메서드
      }
      
      public class FunctionInterfaceTest2 {
        public static void main(String[] args) {
            // MathOperation 인터페이스를 내부 익명 내부 클래스로 구현해보자
            MathOperation mo = new MathOperation() {
      
                // 익명 내부 클래스
                @Override
                public int operation(int x, int y) {
                    return x + y;
                }
            };
            int result = mo.operation(10, 20);
            System.out.println(result);
        }
      }
      

함수형 인터페이스 메서드 참조

  • 이미 정의된 메서드를 직접 참조하여 람다 표현식을 더욱 간결하게 만들 수 있다.

    • 메서드 참조는 기존 메서드를 재사용하고 코드 중복을 줄이는데 도움이 된다.

    • 메서드 참조는 다음 네가지 유형이 있다.

      @FunctionalInterface
      public interface Converter<F, T> {
        T convert(F from);
      }
    • 정적 메서드 참조 → 클래스명 :: 메서드명

      public class IntegerUtils {
      
        // 정적 메서드, 클래스 메서드
        public static int stringToInt(String s){
            return Integer.parseInt(s);
        }
      }
      
      public class IntegerUtilsTest {
        public static void main(String[] args) {
      
            // 정적메서드 참조
            Converter<String, Integer> converter = IntegerUtils::stringToInt;
            Integer result = converter.convert("123"); // Auto-Unboxing
            System.out.println(result); // 123
        }
      }
    • 인스턴스 메서드(static이 없는 메서드) 참조 : 객체참조 :: 메서드명

      public class StringUtils {
      
        // 인스턴스 메서드(static이 없는 메서드)
        public String reverse(String s){
            return new StringBuffer(s).reverse().toString();
        }
      }
      
      public class StringUtilsTest {
        public static void main(String[] args) {
            StringUtils stringUtils = new StringUtils();
      
            // 인스턴스 메서드 참조
            Converter<String, String> converter = stringUtils::reverse;
            String result = converter.convert("hello");
            System.out.println(result);
      
        }
      }
    • 특정 객체의 인스턴스 메서드 참조 : 클래스명 :: 메서드명

      public class SortCompareTest {
        public static void main(String[] args) {
            List<String> names = Arrays.asList("홍", "김", "이");
      
            // String::compareTo -> 특정 객체의 메서드를 참조
            Collections.sort(names, String::compareTo);
            System.out.println(names);
        }
      }
    • 생성자 참조 : 클래스명 :: new

      @FunctionalInterface
      public interface PersonFactory {
        public Person create(String name, int age);
      }
      
      public class PersonFactoryTest{
        public static void main(String[] args) {
      
            // Person의 생성자 참조
            PersonFactory personFactory = Person::new;
            Person person = personFactory.create("홍길동", 40);
            System.out.println(person);
      
            // 익명 내부 클래스
            PersonFactory personFactory1 = new PersonFactory() {
                @Override
                public Person create(String name, int age) {
                    return new Person(name, age);
                }
            };
      
            Person person1 = personFactory1.create("나길동", 32);
            System.out.println(person1);
        }
      }

      람다식이란?

    • 자바 8부터 도입 되었으며 함수형 프로그래밍에서 사용되는 함수를 간결하게 표현하기 위한 방법 중 하나

    • 익명 함수의 한 형태로서 메서드에 대한 구현을 간결하게 표현하는 방법

    • (parameters) → {expression}

      • parameters : 메서드에서 사용할 매개변수
      • expression : 메서드의 구현체
    • 코드를 더 간결하고 읽기 쉽게 만들 수 있다.

    • 람다식은 함수형 인터페이스와 함께 사용되고, 람다식으로 구현하여 사용할 수 있다.

    • 인터페이스가 오직 하나의 추상 메서드를 가지고 있어야 사용가능하다.

      public class LambdaExample {
        public static void main(String[] args) {
            /*MathOperation add = new MathOperation() {
                @Override
                public int operation(int x, int y) {
                    return x+y;
                }
            };*/
      
            // 람다식 : 코드가 간결 -> 구현이 쉽다
            MathOperation add = (x, y) -> x+y;
            MathOperation multi = (x, y) -> x*y;
            int result = add.operation(10,20);
            int result1 = multi.operation(10,20);
            System.out.println(result);
            System.out.println(result1);
        }
      }

      람다식 사용방법

    • 람다 표현식을 메서드 내에서 사용

    • 람다 표현식을 메서드의 인자로 전달

    • 유연성을 높일 수 있다.

      @FunctionalInterface
      public interface StringOperation {
        public String apply(String s);
      }
      
      public class LambdaApply {
        public static void main(String[] args) {
      
            // 람다 표현식을 메서드 내에서 사용
            StringOperation toUpperCase = s -> s.toUpperCase();
            StringOperation toLowerCase = s -> s.toLowerCase();
      
            String input = "Lambda Expressions";
            System.out.println(processString(input, toUpperCase));
            System.out.println(processString(input, toLowerCase));
        }
      
        // 메서드의 매개변수로 전달
        public static String processString(String input, StringOperation operation){
            return operation.apply(input);
        }
      }
      

      Stream API

    • 배열을 스트림으로 변환

      • 배열의 원소들을 스트림 형태로 변환하여 처리 할 수 있게 하는 것
      • 스트림은 원본 데이터를 변경하지 않고, 필요한 데이터 처리 작업을 적용한 결과를 생성하기 때문에 인덱스를 통한 직접 접근은 제공하지 않음
    • Stream(스트림)

      • 자바 8에서 도입된 기능으로, 데이터의 흐름을 다루기 위한 선언형 api
      • 필터링, 매핑, 정렬 등 다양한 데이터 처리 작업을 적용 할 수 있으며, 최종 결과를 배열이나 컬렉션으로 변환할 수 있다.
      • 데이터 처리 작업을 연속적인 파이프라인을 나타낼 수 있어 가독성이 높고, 병렬 처리(연속적으로 스트림 기능을 점을 통해 사용가능)를 쉽게 구현할 수 있다.
      • 배열을 스트림으로 변환하려면 Arrays.stream() 메서드 사용
      • 스트림을 배열로 변환하려면 toArray() 메서드를 사용
      • 스트림을 컬렉션으로 변환하려면 collect() 메서드를 사용
      public class StreamApiTest {
        public static void main(String[] args) {
            int[] numbers = {1, 2, 3, 4, 5};
      
            // IntStream stream = Arrays.stream(numbers);
            int sumOfEvens = Arrays.stream(numbers) // 배열을 스트림을 변환
                .filter(n -> n % 2 == 0)
                .sum();
      
            System.out.println(sumOfEvens);
      
            int[] evenNumbers = Arrays.stream(numbers)
                .filter(n -> n % 2 == 0)
                .toArray(); // 스트림을 배열로 변환
      
            for(int even : evenNumbers){
                System.out.println(even);
            }
        }
      
      }
    • 스트림의 두 가지 연산

      • 중간 연산 : 스트림을 처리하고 다른 스트림을 반환
      • 최종 연산 : 스트림을 처리하고 결과를 반환
## Predicate란?

- argument를 받아 boolean 값을 반환하는 함수형 인터페이스
- functional method: test()

```java
public class StreamExample {

    public static boolean isEven(int number){
        return number % 2 == 0;
    }

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // asList로 배열로 변환
        // Predicate : 함수형 인터페이스 정의
        Predicate<Integer> isEven = n -> n % 2 == 0;
        int sumOfSquares = numbers.stream() // 배열을 스트림으로 변환
            // .filter(isEven) // 짝수로 필터링
            .filter(StreamExample::isEven) // 메서드 참조
            .sorted() // 정렬
            .map(n -> n * n) // 각 원소를 제곱해서 저장
            .reduce(0, Integer::sum); // 0을 초기값으로 sum메서드 참조해서 모두 더하기
        System.out.println(sumOfSquares);

    }
}
```

```java
public class MapStreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        List<Integer> squaredNumbers = numbers.stream()
            .map(n->n*n)
            .collect(Collectors.toList()); // 배열로 변환
        System.out.println(squaredNumbers);

    }
}
```

```java
public class MapStreamExample2 {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry", "orange");

        List<String> uppercase = words.stream()
            .map(word -> word.toUpperCase())
            .collect(Collectors.toList()); // 컬렉션을 리스트로 바꾸기
        System.out.println("uppercase = " + uppercase); // uppercase = [APPLE, BANANA, CHERRY, ORANGE]
        for (String str : uppercase) {
            System.out.println(str);

        }
    }
}
```

'개발 > Java' 카테고리의 다른 글

API 활용  (0) 2024.01.29
제네릭(Generic)  (0) 2024.01.29
Collection API  (0) 2024.01.29
인터페이스 기반의 프로그래밍  (0) 2024.01.29
자바 String 클래스  (1) 2024.01.29