Stream에서 반환된 요소를 ArrayList로 결합하기 전에 이전에 Collect() 메서드를 여러 번 사용했습니다. 이는 컬렉션을 다른 유형(일반적으로 변경 가능한 컬렉션)으로 변환하는 데 유용한 축소 작업입니다. Collector() 함수는 Collectors 도구 클래스의 일부 메서드와 함께 사용하면 이 섹션에서 소개하는 것처럼 매우 편리합니다.
계속해서 이전 Person 목록을 예로 사용하여 Collect() 메서드가 수행할 수 있는 작업을 살펴보겠습니다. 원래 목록에서 20세 이상의 모든 사람을 찾고 싶다고 가정해 보겠습니다. 다음은 가변성과 forEach() 메서드를 사용하여 구현된 버전입니다.
다음과 같이 코드 코드를 복사합니다.
List<Person> oldThan20 = new ArrayList<>();
.filter(사람 -> person.getAge() > 20)
.forEach(person -> oldThan20.add(person)); System.out.println("20세 이상의 사람: " + oldThan20);
우리는 목록에서 20세 이상의 모든 사람을 필터링하기 위해 filter() 메소드를 사용합니다. 그런 다음 forEach 메서드에서 이전에 초기화된 ArrayList에 요소를 추가합니다. 먼저 이 코드의 출력을 살펴보고 나중에 다시 구성해 보겠습니다.
다음과 같이 코드 코드를 복사합니다.
20세 이상의 사람: [사라 - 21세, 제인 - 21세, 그렉 - 35세]
프로그램의 출력은 정확하지만 여전히 약간의 문제가 있습니다. 첫째, 컬렉션에 요소를 추가하는 것은 낮은 수준의 작업입니다. 이는 선언적이 아니라 필수입니다. 이 반복을 동시로 변환하려면 스레드 안전 문제를 고려해야 합니다. 가변성으로 인해 병렬화가 어렵습니다. 다행히 이 문제는 Collect() 메서드를 사용하여 쉽게 해결할 수 있습니다. 이것이 어떻게 달성되는지 봅시다.
Collect() 메소드는 Stream을 승인하고 이를 결과 컨테이너로 수집합니다. 그러기 위해서는 다음 세 가지를 알아야 합니다.
+ 결과 컨테이너를 생성하는 방법(예: ArrayList::new 메서드 사용) + 컨테이너에 단일 요소를 추가하는 방법(예: ArrayList::add 메서드 사용) + 한 결과 집합을 다른 결과 집합에 병합하는 방법 (예를 들어 ArrayList: :addAll 메소드 사용)
마지막 항목은 직렬 작업에 필요하지 않습니다. 코드는 직렬 및 병렬 작업을 모두 지원하도록 설계되었습니다.
이러한 작업을 Collect 메소드에 제공하고 필터링된 스트림을 수집하도록 합니다.
다음과 같이 코드 코드를 복사합니다.
목록<Person> ageThan20 =
people.stream()
.filter(사람 -> person.getAge() > 20)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println("20세 이상의 사람: " + oldThan20);
이 코드의 결과는 이전과 동일하지만 이렇게 작성하면 많은 이점이 있습니다.
우선, 우리의 프로그래밍 방법은 더 집중적이고 표현력이 풍부하여 결과를 ArrayList에 수집하는 목적을 명확하게 전달합니다. Collect()의 첫 번째 매개변수는 팩토리 또는 생산자이고, 후속 매개변수는 요소를 수집하는 데 사용되는 작업입니다.
둘째, 코드에서 명시적인 수정을 수행하지 않기 때문에 이 반복을 병렬로 쉽게 수행할 수 있습니다. 기본 라이브러리가 수정을 처리하도록 하고 ArrayList 자체가 스레드로부터 안전하지 않더라도 조정 및 스레드 안전 문제를 처리합니다. 좋은 작업이었습니다.
조건이 허용되는 경우, Collect() 메소드는 여러 하위 목록에 요소를 병렬로 추가한 다음 스레드로부터 안전한 방식으로 이를 큰 목록으로 병합할 수 있습니다(마지막 매개변수는 병합 작업에 사용됩니다).
목록에 요소를 수동으로 추가하는 것보다 Collect() 메서드를 사용하면 많은 이점이 있다는 것을 확인했습니다. 이 메서드의 오버로드된 버전을 살펴보겠습니다. 이 메서드는 더 간단하고 편리하며 Collector를 매개 변수로 사용합니다. 이 Collector는 생산자, 가산기 및 결합자를 포함하는 인터페이스입니다. 이전 버전에서는 이러한 작업이 독립적인 매개변수로 메서드에 전달되었습니다. Collectors 도구 클래스는 ArrayList에 요소를 추가하기 위해 Collector 구현을 생성할 수 있는 toList 메서드를 제공합니다. 이전 코드를 수정하고 Collect() 메서드를 사용해 보겠습니다.
다음과 같이 코드 코드를 복사합니다.
목록<Person>oldThan20 =
people.stream()
.filter(사람 -> person.getAge() > 20)
.collect(Collectors.toList());
System.out.println("20세 이상의 사람: " + oldThan20);
Collectors 도구 클래스의 Collect() 메서드의 간결한 버전이 사용되지만 여러 가지 방법으로 사용할 수 있습니다. Collectors 도구 클래스에는 다양한 수집 및 추가 작업을 수행하는 여러 가지 메서드가 있습니다. 예를 들어, toList() 메소드 외에도 Set에 추가할 수 있는 toSet() 메소드, 키-값 세트로 수집하는 데 사용할 수 있는 toMap() 메소드 및 Join() 메서드는 문자열로 연결될 수 있습니다. 또한 mapping(),collectingAndThen(), minBy(), maxBy() 및 groupingBy()와 같은 메서드를 결합하여 사용할 수도 있습니다.
groupingBy() 메서드를 사용하여 연령별로 사람들을 그룹화해 보겠습니다.
다음과 같이 코드 코드를 복사합니다.
Map<Integer, List<Person>> peopleByAge =
people.stream()
.collect(Collectors.groupingBy(Person::getAge));
System.out.println("연령별 그룹: " + peopleByAge);
그룹화를 완료하려면 단순히 Collect() 메서드를 호출하면 됩니다. groupingBy()는 람다 식 또는 메서드 참조(이를 분류 함수라고 함)를 허용하고 그룹화해야 하는 객체의 특정 속성 값을 반환합니다. 함수에서 반환된 값에 따라 호출 컨텍스트의 요소가 특정 그룹에 배치됩니다. 그룹화 결과는 출력에서 볼 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
연령별 그룹화: {35=[그렉 - 35], 20=[존 - 20], 21=[사라 - 21, 제인 - 21]}
사람들을 연령별로 분류했습니다.
이전 예에서는 연령별로 사람들을 그룹화했습니다. groupingBy() 메서드의 변형은 여러 조건을 기준으로 그룹화할 수 있습니다. 간단한 groupingBy() 메서드는 분류자를 사용하여 요소를 수집합니다. 일반 groupingBy() 수집기는 각 그룹에 대한 수집기를 지정할 수 있습니다. 즉, 아래에서 볼 수 있듯이 요소는 수집 프로세스 중에 다양한 분류자와 컬렉션을 통과합니다.
위의 예를 계속해서 이번에는 연령별로 그룹화하는 대신 사람들의 이름을 가져와 연령별로 정렬합니다.
다음과 같이 코드 코드를 복사합니다.
Map<Integer, List<String>> nameOfPeopleByAge =
people.stream()
.모으다(
groupingBy(Person::getAge, mapping(Person::getName, toList())));
System.out.println("나이별로 그룹화된 사람들: " + nameOfPeopleByAge);
이 버전의 groupingBy()는 두 개의 매개변수를 허용합니다. 첫 번째 매개변수는 그룹화 조건인 age이고, 두 번째 매개변수는 mapping() 함수에서 반환되는 결과인 수집기입니다. 이러한 메서드는 모두 Collectors 도구 클래스에서 제공되며 이 코드에서 정적으로 가져옵니다. mapping() 메서드는 두 개의 매개 변수를 허용합니다. 하나는 매핑에 사용되는 속성이고 다른 하나는 목록이나 집합과 같이 개체가 수집되는 위치입니다. 위 코드의 출력을 살펴보겠습니다.
다음과 같이 코드 코드를 복사합니다.
연령별로 그룹화된 사람들: {35=[Greg], 20=[John], 21=[Sara, Jane]}
보시다시피, 사람들의 이름은 연령별로 그룹화되어 있습니다.
다시 조합 연산을 살펴보겠습니다. 이름의 첫 글자를 기준으로 그룹화한 다음 각 그룹에서 가장 나이가 많은 사람을 선택합니다.
다음과 같이 코드 코드를 복사합니다.
Comparator<Person> byAge = Comparator.comparing(Person::getAge);
Map<Character, Optional<Person>> oldPersonOfEachLetter =
people.stream()
.collect(groupingBy(사람 -> person.getName().charAt(0),
감소(BinaryOperator.maxBy(byAge))));
System.out.println("각 문자의 가장 나이가 많은 사람:");
System.out.println(oldestPersonOfEachLetter);
먼저 이름을 알파벳순으로 정렬했습니다. 이를 달성하기 위해 groupingBy()의 첫 번째 매개 변수로 람다 식을 전달합니다. 이 람다 식은 그룹화를 위해 이름의 첫 글자를 반환하는 데 사용됩니다. 두 번째 매개변수는 더 이상 mapping()이 아니지만 축소 작업을 수행합니다. 각 그룹 내에서 maxBy() 메서드를 사용하여 모든 요소 중 가장 오래된 요소를 파생시킵니다. 결합된 많은 작업으로 인해 구문이 약간 부풀어 보이지만 전체 내용은 다음과 같습니다. 이름의 첫 번째 문자로 그룹화한 다음 그룹에서 가장 오래된 것으로 작업합니다. 주어진 문자로 시작하는 이름 그룹에서 가장 오래된 사람을 나열하는 이 코드의 출력을 고려하십시오.
다음과 같이 코드 코드를 복사합니다.
각 편지의 가장 나이가 많은 사람:
{S=선택사항[Sara - 21], G=선택사항[Greg - 35], J=선택사항[Jane - 21]}
우리는 이미 Collect() 메소드와 Collectors 유틸리티 클래스의 강력한 기능을 경험했습니다. IDE 또는 JDK의 공식 문서에서 수집기 도구 클래스를 연구하고 그것이 제공하는 다양한 방법에 익숙해지는 데 시간을 투자하세요. 다음으로 람다 표현식을 사용하여 일부 필터를 구현하겠습니다.