Java 함수식 프로그래밍(二): 집합의 사용

제2장: 집합의 사용
우리는 항상 각종 집합, 숫자, 문자열, 그리고 대상을 사용한다.그것들은 집합된 코드를 조작하는 데 조금만 최적화해도 코드를 많이 뚜렷하게 할 수 있다.이 장에서 우리는 lambda 표현식을 어떻게 사용하여 집합을 조작하는지 탐색했다.우리는 그것으로 집합을 두루 훑어보고 집합을 새로운 집합으로 바꾸며 집합에서 원소를 삭제하고 집합을 통합시킨다.
목록을 두루 훑어보다
리스트를 훑어보는 것은 가장 기본적인 집합 작업인데, 이렇게 여러 해 동안, 그것의 조작에도 약간의 변화가 생겼다.우리는 가장 오래된 판본에서 현재 가장 우아한 판본까지 이름을 두루 훑어보는 작은 예를 사용한다.
다음 코드를 사용하면 변경할 수 없는 이름의 목록을 쉽게 만들 수 있습니다.

final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
System.out.println(friends.get(i));
}
다음은 가장 일반적인 목록을 훑어보고 인쇄하는 방법입니다.

for(int i = 0; i < friends.size(); i++) {
System.out.println(friends.get(i));
}
나는 이런 방식을 자학형 기법이라고 부른다. 시끄럽고 실수하기 쉽다.우리는 멈춰서 잘 생각해 봐야 한다. "i<일까요, i<=일까요?"이것은 우리가 구체적인 어떤 원소를 조작해야 할 때만 의미가 있지만, 그래도 우리는 불가변의 원칙을 견지하는 함수식 풍격을 사용하여 실현할 수 있다. 이것은 우리가 곧 토론할 것이다.
Java는 상대적으로 선진적인 for 구조도 제공했다.

collections/fpij/Iteration.java
for(String name : friends) {
System.out.println(name);
}
밑바닥에서, 이런 방식의 교체는 Iterator 인터페이스를 사용하여 이루어진 것이며, 그것의hasNext와next 방법을 호출했다.이 두 가지 방식은 모두 외부 교체기에 속하는데, 그들은 어떻게 하는지, 무엇을 하고 싶은지를 한데 묶었다.우리의 현식 제어는 어디에서 시작하고 어디로 끝날지 알려준다.두 번째 버전은 밑바닥에서 Iterator의 방법을 통해 이런 것들을 한다.현식 조작에서는 브레이크와continue 문장으로 교체를 제어할 수 있다.두 번째 버전은 첫 번째 버전보다 물건이 좀 적다.만약 우리가 집합된 어떤 원소를 수정하지 않으려면, 그것의 방식은 첫 번째 원소보다 좋다.그러나 이 두 가지 방식은 모두 명령식이므로 현재의 자바에서는 이런 방식을 버려야 한다.함수식으로 바꾼 원인은 다음과 같다.
1. for 순환 자체는 직렬적이어서 병행화하기 어렵다.
2. 이런 순환은 비다태적이다.얻은 것은 바로 바라는 것이다.우리는 집합을 for순환에 직접 전달하는 것이지, 집합에서 특정한 조작을 수행하기 위한 방법을 호출하는 것이 아니다.
3. 디자인 차원에서 이렇게 쓴 코드는'Tell, Don't Ask'의 원칙에 위배된다.우리는 교체를 하부 라이브러리에 남겨 두는 것이 아니라 교체를 실행할 것을 요청한다.
낡은 명령식 프로그래밍에서 더 우아한 내부 교체기의 함수식 프로그래밍으로 전환할 때가 되었다.내부 교체기를 사용한 후에 우리는 많은 구체적인 조작을 밑바닥 방법 라이브러리에 던져 집행했다. 당신은 구체적인 업무 수요에 더욱 집중할 수 있다.밑바닥의 함수는 교체를 책임질 것이다.우리는 우선 내부 교체기로 이름 목록을 하나하나 들자.
Iterable 인터페이스는 JDK8에서 강화되었습니다. forEach라는 전문적인 이름이 있습니다. 이것은 Comsumer 형식의 매개 변수를 수신합니다.이름에서 보듯이 Consumer의 실례는 바로 그것의accept 방법을 통해 그것의 대상에 소비하는 것이다.우리는 익숙한 익명 내부 클래스의 문법으로 이 forEach 방법을 사용합니다.

friends.forEach(new Consumer<String>() { public void accept(final String name) {
System.out.println(name); }
});
우리는 friends 집합의forEach 방법을 호출하여 Consumer의 익명 실현을 전달했다.이 forEach 방법은 집합된 모든 요소에 대해 전송된 Consumer의accept 방법을 호출하여 이 요소를 처리하도록 합니다.이 예시에서 우리는 단지 그것의 값, 즉 이 이름을 인쇄했을 뿐이다.이 버전의 출력 결과를 살펴보겠습니다. 이전 두 개의 결과와 같습니다.

Brian
Nate
Neal
Raju
Sara
Scott
우리는 단지 한 군데만 고쳤다. 우리는 유행이 지난 for순환을 버리고 새로운 내부 교체기를 사용했다.좋은 점은 우리가 이 집합을 어떻게 교체하는지 지정하지 않아도 모든 요소를 어떻게 처리하는지에 더욱 집중할 수 있다는 것이다.단점은 코드가 더 수다스러워 보인다는 것이다. 이것은 완전히 새로운 인코딩 스타일이 가져온 기쁨을 깨끗이 씻어버릴 것이다.다행히도 이것은 쉽게 고칠 수 있다. 이것이 바로 람다 표현식과 새로운 컴파일러의 위력이 크게 발휘될 때이다.익명 내부 클래스를lambda 표현식으로 바꾸는 수정을 좀 더 하겠습니다.

friends.forEach((final String name) -> System.out.println(name));
이렇게 하면 훨씬 좋아질 것 같다.코드가 더 적어졌지만, 우선 이것이 무슨 뜻인지 봅시다.이 forEach 방법은 lambda 표현식이나 코드 블록을 받아들여 목록의 요소를 조작하는 고급 함수입니다.호출할 때마다 집합된 요소는name 변수에 연결됩니다.베이스 라이브러리에서 lambda 표현식 호출을 위탁 관리합니다.그것은 표현식의 실행 지연을 결정할 수 있고, 적당하면 병행 계산도 할 수 있다.이 버전의 출력도 앞의 것과 같다.

Brian
Nate
Neal
Raju
Sara
Scott
내부 교체기의 버전은 더욱 간결하다.그리고 그것을 사용하면 우리는 모든 원소의 처리 작업에 더욱 집중할 수 있다. 어떻게 훑어보지 말고. 이것은 성명식이다.
하지만 이 판본은 결함이 있다.일단 forEach 방법이 실행되기 시작하면 다른 두 버전과 달리 우리는 이 교체를 벗어날 수 없다.이것을 해결할 수 있는 다른 방법이 있지.따라서 이런 작법은 집합 안의 모든 원소를 처리해야 할 때 비교적 자주 사용된다.다음에 우리는 순환을 제어할 수 있는 다른 함수를 소개할 것이다.
lambda 표현식의 표준 문법은 파라미터를 () 안에 넣고 유형 정보를 제공하며 쉼표로 파라미터를 구분하는 것이다.Java 컴파일러는 우리를 해방시키기 위해 자동으로 유형 유도도 할 수 있다.유형을 쓰지 않는 것이 당연히 더 편리하지, 일이 적어지고, 세상도 조용해졌다.다음은 이전 릴리즈에서 매개변수 유형을 제거한 다음입니다.

friends.forEach((name) -> System.out.println(name));
이 예에서 자바 컴파일러는 상하문 분석을 통해name의 유형이 String이라는 것을 알 수 있다.이것은 호출된 방법인forEach의 서명을 보고 파라미터에 있는 이 함수식 인터페이스를 분석합니다.이어서 이 인터페이스의 추상적인 방법을 분석하고 파라미터의 개수와 유형을 볼 것이다.설령 이 lambda 표현식이 여러 개의 매개 변수를 수신한다고 해도 우리는 유형 유도를 할 수 있지만 이렇게 하면 모든 매개 변수는 매개 변수 유형을 가지고 있을 수 없다.lambda 표현식에서 매개 변수 형식은 모두 쓰지 않거나 쓰려면 모두 써야 합니다.
Java 컴파일러는 단일 매개 변수의 lambda 표현식에 대해 특수 처리를 합니다. 형식을 유도하려면 매개 변수 양쪽의 괄호를 생략할 수 있습니다.

friends.forEach(name -> System.out.println(name));
여기에 작은 경고가 있습니다. 형식을 유도하는 매개 변수는final 형식이 아닙니다.앞의 현식 성명 유형 예에서, 우리는 동시에 매개 변수를final로 표시합니다.이렇게 하면 람다 표현식에서 매개 변수의 값을 수정하는 것을 방지할 수 있다.일반적으로 매개 변수의 값을 수정하는 것은 나쁜 습관이다. 이렇게 하면 버그를 일으키기 쉽기 때문에final로 표시하는 것은 좋은 습관이다.불행하게도, 만약 우리가 유형 유도를 사용하고 싶다면, 우리는 스스로 규칙을 준수하고 파라미터를 수정하지 말아야 한다. 왜냐하면 컴파일러는 더 이상 우리를 보호하지 않기 때문이다.
여기까지 오느라 고생이 많았는데, 지금은 코드량이 확실히 좀 줄었다.하지만 이것은 아직 가장 간단하지 않다.우리 마지막 미니멀리즘을 체험해 봅시다.

friends.forEach(System.out::println);
위의 코드에서 우리는 인용하는 방법을 사용했다.우리는 방법명으로 전체 코드를 직접 바꿀 수 있다.다음 절에서 우리는 이것을 깊이 있게 연구할 것이다. 그러나 지금 우리는 먼저 Antoine de Saint-Exupéry의 명언을 기억해 보자. 완벽은 더 이상 무엇을 추가할 수 없는 것이 아니라 더 이상 무엇을 뺄 수 없는 것이다.
lambda 표현식은 우리로 하여금 간단명료하게 집합을 진행할 수 있게 한다.다음 절에서는 삭제 작업과 집합 전환을 할 때도 이렇게 간결한 코드를 쓸 수 있도록 하는 방법을 설명할 것이다.

좋은 웹페이지 즐겨찾기