Java 함수 프로그래밍(10): 컬렉터

앞에서 우리는 Stream이 되돌아오는 요소를 Array List로 맞추기 위해collect () 방법을 몇 번 사용했다.이것은 Reduce 작업으로 하나의 집합을 다른 유형으로 바꾸는 데 매우 유용하다.collect () 함수, 만약에 Collectors 도구 클래스의 몇 가지 방법과 결합하여 사용한다면 매우 큰 편의성을 제공할 수 있습니다. 이 절에서 소개할 것입니다.
우리는 여전히 앞의 Person 목록을 계속 사용하여 예를 들어collect () 방법이 도대체 어떤 능력이 있는지 살펴보자.만약 우리가 원시 목록에서 20세 이상의 모든 사람을 찾아내야 한다고 가정하자.다음은 가변성 및 forEach () 메서드를 사용한 버전입니다.

List<Person> olderThan20 = new ArrayList<>(); people.stream()
        .filter(person -> person.getAge() > 20)
.forEach(person -> olderThan20.add(person)); System.out.println("People older than 20: " + olderThan20);
우리는 필터 () 방법을 사용하여 목록에서 20세 이상의 모든 사람을 필터링했습니다.그리고 forEach 방법에서 앞에서 초기화된 Array List에 요소를 추가합니다.우리는 먼저 이 코드의 출력 결과를 보고 잠시 후에 다시 그것을 재구성하러 간다.

People older than 20: [Sara - 21, Jane - 21, Greg - 35]
프로그램이 출력한 결과는 맞지만, 아직 약간의 문제가 있다.우선, 원소를 집합에 추가하는 것은 저급 조작에 속한다. 그것은 명령식이지 성명식이 아니다.만약 우리가 이 교체를 병행적으로 개조하고 싶다면, 라인 안전의 문제인 가변성을 고려해서 그것을 병행화하기 어렵다.다행히도collect () 방법을 사용하면 이 문제를 쉽게 해결할 수 있습니다.어떻게 이루어졌는지 봅시다.
collect() 방법은 Stream을 받아들여 결과 용기에 수집합니다.이 일을 완성하려면 세 가지를 알아야 한다.
+ 결과 용기를 만드는 방법 (예를 들어 ArrayList::new 방법을 사용함) + 하나의 요소를 용기에 추가하는 방법 (예를 들어 ArrayList::add 방법을 사용함) + 하나의 결과 집합을 다른 것에 통합하는 방법 (예를 들어 ArrayList::addAll 방법을 사용함)
직렬 작업에 있어서 마지막 하나는 필요하지 않다.코드 설계의 목표는 직렬과 병행을 동시에 지원할 수 있는 것이다.
우리는 이 조작들을 필터링된 흐름을 수집하기 위해collect 방법에 제공합니다.

List<Person> olderThan20 =
people.stream()
.filter(person -> person.getAge() > 20)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println("People older than 20: " + olderThan20);
이 코드의 결과는 앞과 같지만, 이렇게 쓰면 많은 장점이 있다.
우선, 우리가 프로그래밍하는 방식은 더욱 초점을 맞추고, 표현성도 더욱 강하며, 결과를 Array List에 수집하려는 목적을 명확하게 전달한다.collect ()의 첫 번째 매개 변수는 공장이나 생산자이고, 뒤의 매개 변수는 원소를 수집하는 작업입니다.
둘째, 우리는 코드에서 현식 수정 작업을 실행하지 않았기 때문에 이 교체를 쉽게 병행할 수 있다.Array List 자체가 라인이 안전하지 않지만, 우리는 베이스 라이브러리에서 수정 작업을 완성하도록 합니다. 이것은 협업과 라인 안전의 문제를 잘 처리할 것입니다.
조건이 허용된다면,collect () 방법은 요소를 다른 하위 목록에 병렬적으로 추가한 다음, 하나의 라인으로 안전한 방식으로 큰 목록에 통합할 수 있습니다. (마지막 매개 변수는 통합 작업에 사용됩니다.)
우리는 수동으로 요소를 목록에 추가하는 것보다 콜렉션 () 방법을 사용하는 것이 너무 많은 것을 보았다.다음은 이 방법의 다시 불러오는 버전입니다. 더 간단하고 편리합니다. 이것은 Collector를 매개 변수로 사용합니다.이 Collector는 생산자, 첨가기, 합병기를 포함한 인터페이스입니다. 앞의 버전에서 이러한 조작은 독립된 매개 변수로 각각 전송되는 방법입니다. Collector를 사용하면 더욱 간단하고 다시 사용할 수 있습니다.Collectors 도구 클래스는 ToList 방법을 제공하여 Collector의 실현을 생성하여 요소를arrayList에 추가할 수 있습니다.앞의 코드를 수정하고 이collect () 방법을 사용합니다.

List<Person> olderThan20 =
people.stream()
.filter(person -> person.getAge() > 20)
.collect(Collectors.toList());
System.out.println("People older than 20: " + olderThan20);
Collectors 도구 클래스의 간결한 버전의collect () 방법을 사용했습니다.Collectors 도구 클래스에는 다양한 수집과 추가 작업을 할 수 있는 여러 가지 방법이 있습니다.예를 들어 toList () 방법 외에 toSet () 방법도 있습니다. 하나의 Set에 추가할 수 있고, toMap () 방법은 키-value 집합에 수집할 수 있으며, Joining () 방법도 있습니다. 문자열로 연결할 수 있습니다.mapping (), collecting And Then (), minBy (), maxBy (), grouping By () 등의 방법을 조합하여 사용할 수 있습니다.
그룹화 바이 () 방법으로 사람들을 연령별로 그룹화합니다.

Map<Integer, List<Person>> peopleByAge =
people.stream()
.collect(Collectors.groupingBy(Person::getAge));
System.out.println("Grouped by age: " + peopleByAge);
간단하게collect () 방법을 호출하면 그룹을 완성할 수 있습니다.그룹 바이 () 는 lambda 표현식이나 방법 인용을 받아들인다. 분류 함수라는 것은 그룹을 나누는 대상의 어떤 속성의 값을 되돌려준다.이 함수가 되돌아오는 값에 따라 상하문에 호출된 요소를 그룹에 넣습니다.출력에서 그룹 결과를 볼 수 있습니다.

Grouped by age: {35=[Greg - 35], 20=[John - 20], 21=[Sara - 21, Jane - 21]}
이 사람들은 이미 나이에 따라 조를 나누었다.
앞의 이 예에서 우리는 사람들의 나이에 따라 그들을 그룹별로 수집했다.그룹 바이 () 방법의 변종은 여러 조건에 따라 그룹을 나눌 수 있다.간단한 grouping By () 방법은 분류기를 사용하여 원소를 수집합니다.일반적인 그룹 바이 () 컬렉터는 그룹마다 컬렉터를 지정할 수 있습니다.즉, 원소는 수집하는 과정에서 서로 다른 분류기와 집합을 거쳐 아래를 볼 수 있다.
위의 예를 계속 사용하면, 이번에는 우리가 나이에 따라 그룹을 나누지 않고, 우리는 사람의 이름만 얻고, 그들의 나이에 따라 순서를 정한다.

Map<Integer, List<String>> nameOfPeopleByAge =
people.stream()
.collect(
groupingBy(Person::getAge, mapping(Person::getName, toList())));
System.out.println("People grouped by age: " + nameOfPeopleByAge);
이 버전의groupingBy()는 두 가지 파라미터를 받아들입니다. 첫 번째는 나이입니다. 이것은 그룹을 나누는 조건입니다. 두 번째는 수집기입니다. 이것은 맵핑() 함수로 되돌아온 결과입니다.이 방법들은 모두 Collectors 도구 클래스에서 나온 것으로 이 코드에서 정적 가져오기를 진행했다.mapping () 방법은 두 개의 매개 변수를 받아들인다. 하나는 비추는 속성이고, 하나는 대상이 수집해야 하는 곳이다. 예를 들어list나set이다.위의 코드의 출력 결과를 살펴보겠습니다.

People grouped by age: {35=[Greg], 20=[John], 21=[Sara, Jane]}
사람들의 이름은 이미 나이에 따라 조를 나누는 것을 볼 수 있다.
우리는 다시 한 번 조합의 조작을 보았다. 이름의 이니셜에 따라 그룹을 나누고 각 그룹에서 가장 나이가 많은 사람을 뽑는다.

Comparator<Person> byAge = Comparator.comparing(Person::getAge);
Map<Character, Optional<Person>> oldestPersonOfEachLetter =
people.stream()
.collect(groupingBy(person -> person.getName().charAt(0),
reducing(BinaryOperator.maxBy(byAge))));
System.out.println("Oldest person of each letter:");
System.out.println(oldestPersonOfEachLetter);
우리는 먼저 이름의 이니셜에 따라 정렬을 진행했다.이를 실현하기 위해, 우리는 lambda 표현식을 그룹핑 바이 () 의 첫 번째 매개 변수로 전송했다.이 람다 표현식은 그룹을 나누기 위해 이름의 이니셜을 되돌려 주는 데 쓰인다.두 번째 파라미터는 더 이상mapping () 이 아니라reduce 작업을 실행했습니다.그룹마다 maxBy () 방법을 사용하여 모든 요소에서 가장 나이가 많은 분을 내보냅니다.많은 조작을 조합했기 때문에 이 문법은 좀 비대해 보이지만 전체적으로 읽으면 이렇다. 이름의 이니셜에 따라 그룹을 나누고 그룹에서 가장 나이가 많은 분을 내놓는다.이 코드의 출력을 보십시오. 지정한 알파벳으로 시작하는 그룹 이름 중 가장 나이가 많은 사람을 보여 줍니다.

Oldest person of each letter:
{S=Optional[Sara - 21], G=Optional[Greg - 35], J=Optional[Jane - 21]}
우리는 이미collect () 방법과 Collectors 도구 클래스의 위력을 배웠다.당신의 IDE나 JDK의 공식 문서에서 Collectors 도구 종류를 연구하는 데 시간을 좀 더 들여서 여러 가지 방법을 익히세요.다음은 lambda 표현식으로 필터의 실현을 완성할 것입니다.

좋은 웹페이지 즐겨찾기