프로그래밍/Java

Java Stream(map, filter, redute, sorted, collect)

C.Story 2023. 2. 7. 17:58
반응형

Java Stream API는 Java 8에 도입된 새로운 기능으로 데이터 컬렉션 작업에 대한 접근 방식을 제공합니다.

스트림은 병렬 또는 순차적으로 처리할 수 있는 일련의 요소입니다.

Stream API의 주요 기능은 다음과 같습니다.

  • 스트림 작업은 느리게 수행됩니다. 즉, 필요할 때만 실행됩니다. 이를 통해 최적화가 가능하고 성능이 향상될 수 있습니다.
  • 스트림 작업은 한 작업의 출력이 다음 작업의 입력으로 사용되는 파이프라인에서 함께 연결될 수 있습니다.
    이를 통해 복잡한 데이터 처리 파이프라인을 생성할 수 있습니다.
  • 스트림을 병렬로 처리할 수 있습니다. 즉, 동시에 처리할 수 있는 여러 부분으로 분할할 수 있습니다. 이렇게 하면 대규모 데이터 컬렉션으로 작업할 때 성능이 향상될 수 있습니다.

    Stream API는 map, filter, reduce 등을 포함하여 스트림에서 수행할 수 있는 광범위한 작업을 제공합니다.

map()

 

map 메서드는 스트림의 각 요소를 새로운 요소로 변환하는 함수입니다.

스트림의 각 요소에 적용되는 함수를 인수로 사용하고 결과를 포함하는 새 스트림을 반환합니다.

이 함수는 일반적으로 매퍼 함수라고 합니다.

 

다음은 문자열 목록을 대문자로 변환하는 예입니다.

List<String> words = Arrays.asList("hello", "world", "java");
List<String> uppercaseWords = words.stream()
                                   .map(String::toUpperCase)
                                   .collect(Collectors.toList());


이 코드의 결과는 uppercaseWords 목록에 "HELLO", "WORLD", "JAVA" 요소가 포함되어 있다는 것입니다.

이 예제에서 map 메서드는 단어 스트림의 각 요소에 toUpperCase 메서드를 적용하고 결과를 포함하는 새 스트림을 반환합니다. 

그런 다음 수집 방법을 사용하여 스트림의 요소를 목록으로 수집합니다.


다음은 개체 목록을 DTO(Data Transfer Objects) 목록으로 변환하는 map 메서드의 또 다른 사용 예입니다.

List<Person> people = 
	Arrays.asList(new Person("John", 25), new Person("Jane", 30), new Person("Jim", 35));
List<PersonDTO> personDTOs = 
    people.stream()
    .map(person -> new PersonDTO(person.getName(), person.getAge()))
    .collect(Collectors.toList());


이 코드의 결과로 personDTOs 목록에는 원래 Person 개체를 단순화된 형식으로 나타내는 PersonDTO 개체 목록이 포함되어 있습니다.

이 예제에서 맵 메서드는 새 PersonDTO(person.getName(), person.getAge()) 식을 people 스트림의 각 요소에 적용하여 각 Person 개체에 대한 새 PersonDTO 개체를 만듭니다. 그런 다음 수집 방법을 사용하여 스트림의 요소를 목록으로 수집합니다.


filter()

 

Java의 필터 메서드는 주어진 조건과 일치하는 요소만 포함하는 새 스트림을 반환하는 Stream API 함수입니다. 요소가 결과에 포함되어야 하는지 여부를 나타내는 부울을 반환하는 단일 메서드 테스트를 정의하는 기능 인터페이스인 Predicate를 인수로 사용합니다.

다음은 문자열 목록에서 문자 'a'를 포함하는 단어를 가져오는 필터 메서드의 또 다른 사용 예입니다.

List<String> words = 
	Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
List<String> wordsWithA = 
    words.stream()
    .filter(word -> word.contains("a"))
    .collect(Collectors.toList());


이 코드의 결과는 wordsWithA 에 "apple", "banana", "date" 요소가 포함되어 있다는 것입니다.

이 예제에서 필터 메서드는 단어 스트림의 각 요소에 word.contains("a") 표현식을 적용하고 

조건과 일치하는 요소만 포함하는 새 스트림을 반환합니다. 

그런 다음 수집 방법을 사용하여 스트림의 요소를 목록으로 수집합니다.


다음은 PersonDTO 개체 목록에서 연령이 30보다 큰 PersonDTO 개체의 이름을 가져오기 위해 필터 및 맵 메서드를 모두 사용하는 또 다른 예제입니다.

List<PersonDTO> people = Arrays.asList(new PersonDTO("John", 25), new PersonDTO("Jane", 35), new PersonDTO("Jim", 40));
List<String> namesOfOlderPeople = people.stream()
                                        .filter(person -> person.getAge() > 30)
                                        .map(PersonDTO::getName)
                                        .collect(Collectors.toList());


이 코드의 결과는 namesOfOlderPeople 에 "Jane", "Jim" 가 포함되어 있다는 것입니다.

이 예제에서 filter는 people.getAge() > 30 식을 people 스트림의 각 요소에 적용하고 조건과 일치하는 PersonDTO 개체만 포함하는 새 스트림을 반환합니다. 그런 다음 map 메서드는 PersonDTO::getName 표현식을 스트림의 각 PersonDTO 개체에 적용하고 이름만 포함하는 새 스트림을 반환합니다. 그런 다음 collect(Collectors.toList())를 사용하여 스트림의 요소를 List로 수집합니다.


reduce()

 

reduce는 스트림의 요소를 단일 값으로 집계하는 고차 함수입니다. 합, 곱, 최소값, 최대값 또는 스트림 요소의 연결을 찾는 것과 같은 다양한 유형의 계산을 수행하는 데 사용할 수 있는 다목적 작업입니다.

다음은 정수 목록의 합을 찾기 위한 reduce 메서드의 사용 예입니다.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = numbers.stream().reduce(0, (a, b) -> a + b);


이 코드의 결과는 sum 변수에 값 15가 포함된다는 것입니다.

이 예제에서 reduce 메서드는 초기 값(0)과 스트림의 요소를 결합하는 방법을 정의하는 이항 연산자((a, b) -> a + b)라는 두 가지 인수를 사용합니다. 이진 연산자는 두 개의 인수(a 및 b)를 사용하고 이들을 결합한 결과를 반환합니다. reduce 메소드는 스트림의 요소와 초기 값에 이진 연산자를 적용하고 최종 결과를 반환합니다.

축소 작업은 종료 작업이므로 스트림을 종료하고 결과를 생성합니다. 이는 추가 처리가 가능한 새 스트림을 생성하는 맵 및 필터와 같은 중간 작업과 대조됩니다.


다음은 이름이 "J"로 시작하는 PersonDTO 개체의 수명 합계를 찾기 위해 filter 메서드와 함께 reduce 메서드를 사용하는 예제입니다.

List<PersonDTO> people = Arrays.asList(new PersonDTO("John", 25), new PersonDTO("Jane", 35), new PersonDTO("Jim", 40));
Integer sumOfAges = people.stream()
                          .filter(person -> person.getName().startsWith("J"))
                          .map(PersonDTO::getAge)
                          .reduce(0, (a, b) -> a + b);


이 코드의 결과는 sumOfAges = 65가 된다는 것입니다.

이 예제에서 필터 메서드는 person.getName().startsWith("J") 표현식을 people 스트림의 각 PersonDTO 개체에 적용하고 조건과 일치하는 PersonDTO 개체만 포함하는 새 스트림을 반환합니다. map 메소드는 다음을 적용합니다.


sorted()

 

sorted 메소드는 기본 순서 또는 제공된 Comparator에 따라 정렬된 원래 스트림의 요소를 포함하는 스트림을 반환하는 스트림 중간 작업입니다. 기본 순서는 스트림에 있는 객체의 compareTo 메서드 또는 해당 Comparable 인터페이스 구현에 의해 부과되는 순서입니다.

 

다음은 오름차순으로 연령별로 PersonDTO 개체 목록을 정렬하는 정렬된 메서드의 사용 예입니다.

List<PersonDTO> people = 
	Arrays.asList(new PersonDTO("John", 35), new PersonDTO("Jane", 25), new PersonDTO("Jim", 40));
List<PersonDTO> sortedPeople = 
    people.stream()
    .sorted(Comparator.comparingInt(PersonDTO::getAge))
    .collect(Collectors.toList());


이 코드의 결과는 sortedPeople 목록에 (Jane25), (John35), (Jim40) 요소가 포함된다는 것입니다.

이 예제에서 sorted는 스트림에 정렬 순서를 정의하는 인수로 Comparator를 사용합니다.

Comparator.comparingInt 메서드는 나이를 기준으로 PersonDTO 개체를 비교하는 Comparator를 반환합니다. sorted 메서드는 Comparator에서 정의한 정렬 순서에 따라 people 스트림의 요소를 정렬하고 정렬된 요소를 collect(Collectors.toList())를 이용하여 List로 반환합니다.

sorted()는 중간 작업이므로 추가 처리가 가능한 새 스트림을 반환합니다. 최종 결과를 얻으려면 스트림에서 collect와 같은 터미널 작업을 호출해야 합니다.


collect()

 

collect 메서드는 스트림의 요소를 컬렉션 또는 데이터 구조로 수집하는 Java 스트림의 터미널 작업입니다. collect 메서드는 Collector를 인수로 사용하여 스트림의 요소를 결과로 누적하는 방법을 정의합니다.

Collectors 클래스에는 toList, toSet, toMap 및 groupingBy와 같은 미리 정의된 많은 Collector 메서드가 있습니다.

다음은 서로 다른 Collector 구현과 함께 collect 메서드를 사용하여 사용자 지정 DTO 스트림의 요소를 서로 다른 데이터 구조로 수집하는 예제입니다.

class Person {
  private int id;
  private String name;
  private int age;

  public Person(int id, String name, int age) {
    this.id = id;
    this.name = name;
    this.age = age;
  }

  public int getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public int getAge() {
    return age;
  }
}

List<Person> people = Arrays.asList(
  new Person(1, "John", 30),
  new Person(2, "Jane", 25),
  new Person(3, "Jim", 35)
);

// Collecting elements into a List
List<Person> sortedPeople = 
    people
    .stream()
    .sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
    .collect(Collectors.toList());

// Collecting elements into a Set
Set<Person> uniquePeople = 
    people
    .stream().collect(Collectors.toSet());

// Collecting elements into a Map
Map<Integer, Person> peopleMap = 
    people
    .stream().collect(Collectors.toMap(Person::getId, Function.identity()));

// Collecting elements into a Map using groupingBy
Map<Integer, List<Person>> peopleByAge = 
    people
    .stream().collect(Collectors.groupingBy(Person::getAge));


이 예제에서 collect 는 Person 개체의 요소를 list, set, map 및 연령별로 그룹화된 맵으로 수집하기 위해 다양한 Collector 메서드와 함께 사용됩니다. 이 코드의 결과는 다음과 같습니다.

  • sortedPeople 목록에는 이름별로 정렬된 Person 개체가 포함되어 있습니다.
  • uniquePeople 세트에는 중복이 없는 Person 객체가 포함되어 있습니다.
  • peopleMap 맵에는 id 값으로 키가 지정되는 Person 개체가 포함되어 있습니다.
  • peopleByAge 맵에는 연령 값으로 그룹화된 Person 개체 목록이 포함되어 있습니다.

 


이상으로 Java Stream 포스팅을 마치겠습니다.

읽어주셔서 감사합니다.

반응형