소개
람다 표현식은 Java SE 8의 중요한 새로운 기능입니다. 람다 표현식을 사용하면 기능 인터페이스를 표현식으로 바꿀 수 있습니다. 람다 식은 메서드와 마찬가지로 일반 매개 변수 목록과 이러한 매개 변수를 사용하는 본문(식 또는 코드 블록일 수 있음)을 제공합니다.
람다 표현식은 컬렉션 라이브러리도 향상시킵니다. Java SE 8에는 컬렉션 데이터에 대한 일괄 작업을 위해 java.util.function 패키지와 java.util.stream 패키지라는 두 가지 패키지가 추가되었습니다. 스트림은 반복자와 비슷하지만 추가 기능이 많이 있습니다. 전반적으로 람다 표현식과 스트림은 Java 언어에 제네릭과 주석을 추가한 이후 가장 큰 변화입니다. 이 기사에서는 간단한 예제부터 복잡한 예제까지 람다 식과 스트림의 강력한 기능을 살펴보겠습니다.
환경 준비
Java 8이 설치되지 않은 경우 람다 및 스트림을 사용하기 전에 먼저 Java 8을 설치해야 합니다(변환기는 테스트를 위해 가상 머신에 설치하는 것을 권장합니다). NetBeans 및 IntelliJ IDEA와 같은 도구 및 IDE는 람다 표현식, 반복 가능한 주석, 압축 프로필 및 기타 기능을 포함한 Java 8 기능을 지원합니다.
람다 표현식 구문
기본 구문:
(매개변수) -> 표현식
또는
(매개변수) ->{ 문 }
다음은 Java 람다 표현식의 간단한 예입니다.
다음과 같이 코드 코드를 복사합니다.
// 1. 매개변수가 필요하지 않으며 반환 값은 5입니다.
() -> 5
// 2. 매개변수(숫자형)를 받고 그 값의 2배를 반환합니다.
x -> 2*x
// 3. 2개의 매개변수(숫자)를 수락하고 그 차이를 반환합니다.
(x, y) -> xy
// 4. 2개의 int형 정수를 받고 그 합을 반환합니다.
(int x, int y) -> x + y
// 5. 문자열 객체를 수락하고 값을 반환하지 않고 콘솔에 인쇄합니다. (void를 반환하는 것처럼 보입니다.)
(문자열 s) -> System.out.print(들)
기본 람다 예시
이제 람다 표현식이 무엇인지 알았으니 몇 가지 기본 예제부터 시작해 보겠습니다. 이 섹션에서는 람다 표현식이 코딩 방식에 어떤 영향을 미치는지 살펴보겠습니다. 플레이어 목록이 있다고 가정하면 프로그래머는 for 문("for 루프")을 사용하여 탐색할 수 있으며 이는 Java SE 8에서 다른 형식으로 변환될 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
String[] atp = {"라파엘 나달", "노박 조코비치",
"스타니슬라스 바브린카",
"데이비드 페러", "로저 페더러",
"앤디 머레이", "토마스 베르디치",
"후안 마르틴 델 포트로"};
List<String> 플레이어 = Arrays.asList(atp);
//이전 루프 방법
for (문자열 플레이어 : 플레이어) {
System.out.print(player + "; ");
}
//람다 표현식 및 함수 연산 사용
플레이어.forEach((플레이어) -> System.out.print(플레이어 + "; "));
//Java 8에서는 이중 콜론 연산자를 사용합니다.
플레이어.forEach(System.out::println);
보시다시피 람다 표현식을 사용하면 코드를 한 줄로 줄일 수 있습니다. 또 다른 예는 익명 클래스가 람다 표현식으로 대체될 수 있는 그래픽 사용자 인터페이스 프로그램입니다. 마찬가지로 Runnable 인터페이스를 구현할 때 다음과 같이 사용할 수도 있습니다.
다음과 같이 코드 코드를 복사합니다.
//익명 내부 클래스 사용
btn.setOnAction(new EventHandler<ActionEvent>() {
@보수
공개 무효 핸들(ActionEvent 이벤트) {
System.out.println("안녕하세요!");
}
});
// 아니면 람다 표현식을 사용하세요.
btn.setOnAction(event -> System.out.println("Hello World!"));
다음은 Runnable 인터페이스를 구현하기 위해 람다를 사용하는 예입니다.
다음과 같이 코드 코드를 복사합니다.
// 1.1 익명 내부 클래스 사용
새로운 스레드(새로운 Runnable() {
@보수
공개 무효 실행() {
System.out.println("안녕하세요!");
}
}).시작();
// 1.2 람다 표현식 사용
new Thread(() -> System.out.println("Hello world!")).start();
// 2.1 익명 내부 클래스 사용
실행 가능한 race1 = new Runnable() {
@보수
공개 무효 실행() {
System.out.println("안녕하세요!");
}
};
// 2.2 람다 표현식 사용
실행 가능한 race2 = () -> System.out.println("Hello world!");
// run 메소드를 직접 호출합니다(새 스레드가 열리지 않습니다!)
race1.run();
race2.run();
Runnable의 람다 식은 블록 형식을 사용하여 5줄의 코드를 한 줄의 명령문으로 변환합니다. 다음 섹션에서는 람다를 사용하여 컬렉션을 정렬하겠습니다.
Lambda를 사용하여 컬렉션 정렬
Java에서는 Comparator 클래스를 사용하여 컬렉션을 정렬합니다. 아래 예에서는 플레이어의 이름, 성, 이름 길이 및 마지막 문자를 기반으로 합니다. 이전 예제와 마찬가지로 먼저 익명 내부 클래스를 사용하여 정렬한 다음 람다 식을 사용하여 코드를 간소화합니다.
첫 번째 예에서는 목록을 이름별로 정렬합니다. 이전 방식을 사용하면 코드는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다.
String[] 플레이어 = {"라파엘 나달", "노박 조코비치",
"스타니슬라스 바브린카", "데이비드 페러",
'로저 페더러', '앤디 머레이',
"토마스 베르디히", "후안 마르틴 델 포트로",
"리차드 가스켓", "존 이스너"};
// 1.1 익명의 내부 클래스를 사용하여 이름에 따라 플레이어를 정렬합니다.
Arrays.sort(players, new Comparator<String>() {
@보수
공개 int 비교(문자열 s1, 문자열 s2) {
return (s1.compareTo(s2));
}
});
람다를 사용하면 다음 코드로 동일한 기능을 구현할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
// 1.2 람다 표현식을 사용하여 플레이어 정렬
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Arrays.sort(players, sortByName);
// 1.3은 다음 형식을 취할 수도 있습니다:
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));
그 외 순위는 아래와 같습니다. 위의 예와 마찬가지로 코드는 익명의 내부 클래스와 일부 람다 식을 통해 Comparator를 구현합니다.
다음과 같이 코드 코드를 복사합니다.
// 1.1 익명 내부 클래스를 사용하여 성을 기준으로 플레이어를 정렬합니다.
Arrays.sort(players, new Comparator<String>() {
@보수
공개 int 비교(문자열 s1, 문자열 s2) {
return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
}
});
// 1.2 람다 표현식을 사용하여 성에 따라 정렬
Comparator<String> sortBySurname = (문자열 s1, 문자열 s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
Arrays.sort(players, sortBySurname);
// 1.3 아니면 이렇게 원저자가 실수한건지 괄호가 너무 많아서...
Arrays.sort(플레이어, (문자열 s1, 문자열 s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) )
);
// 2.1 익명 내부 클래스를 사용하여 이름 길이에 따라 플레이어를 정렬합니다.
Arrays.sort(players, new Comparator<String>() {
@보수
공개 int 비교(문자열 s1, 문자열 s2) {
return (s1.length() - s2.length());
}
});
// 2.2 람다 표현식을 사용하여 이름 길이에 따라 정렬
Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
Arrays.sort(players, sortByNameLenght);
// 2.3 또는 이것
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));
// 3.1 익명 내부 클래스를 사용하여 마지막 문자에 따라 플레이어를 정렬합니다.
Arrays.sort(players, new Comparator<String>() {
@보수
공개 int 비교(문자열 s1, 문자열 s2) {
return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
}
});
// 3.2 람다 표현식을 사용하여 마지막 문자에 따라 정렬
비교기<String> sortByLastLetter =
(문자열 s1, 문자열 s2) ->
(s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(players, sortByLastLetter);
// 3.3 또는 이것
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
그게 바로 간단하고 직관적입니다. 다음 섹션에서는 람다의 기능을 더 자세히 살펴보고 이를 스트림과 함께 사용하겠습니다.
람다 및 스트림 사용
Stream은 컬렉션의 래퍼이며 일반적으로 람다와 함께 사용됩니다. 람다를 사용하면 맵, 필터, 제한, 정렬, 개수, 최소, 최대, 합계, 수집 등과 같은 다양한 작업을 지원할 수 있습니다. 마찬가지로 Streams는 지연 작업을 사용하므로 실제로 모든 데이터를 읽지 않으며 getFirst()와 같은 메서드를 만나면 체인 구문이 종료됩니다. 다음 예제에서는 람다와 스트림이 수행할 수 있는 작업을 살펴보겠습니다. 우리는 Person 클래스를 생성하고 이 클래스를 사용하여 추가 스트리밍 작업에 사용될 일부 데이터를 목록에 추가했습니다. Person은 단순한 POJO 클래스입니다.
다음과 같이 코드 코드를 복사합니다.
공개 클래스 사람 {
개인 문자열 이름, 성, 직업, 성별;
개인 급여, 나이;
public Person(String firstName, String lastName, String job,
문자열 성별, int 연령, int 급여) {
this.firstName = 이름;
this.lastName = 성;
this.gender = 성별;
this.나이 = 나이;
this.job = 직업;
this.salary = 급여;
}
// 게터와 세터
// .
}
다음으로 Person 개체를 저장하는 데 사용되는 두 개의 목록을 만듭니다.
다음과 같이 코드 코드를 복사합니다.
List<Person> javaProgrammers = new ArrayList<Person>() {
{
add(new Person("Elsdon", "Jaycob", "Java 프로그래머", "남성", 43, 2000));
add(new Person("탐센", "브리타니", "자바 프로그래머", "여성", 23, 1500));
add(new Person("플로이드", "도니", "자바 프로그래머", "남성", 33, 1800));
add(new Person("신디", "조니", "자바 프로그래머", "여성", 32, 1600));
add(new Person("Vere", "Hervey", "Java 프로그래머", "남성", 22, 1200));
add(new Person("Maude", "Jaimie", "Java 프로그래머", "female", 27, 1900));
add(new Person("숀", "랜달", "자바 프로그래머", "남성", 30, 2300));
add(new Person("제이든", "코리나", "자바 프로그래머", "여성", 35, 1700));
add(new Person("Palmer", "Dene", "Java 프로그래머", "male", 33, 2000));
add(new Person("애디슨", "Pam", "Java 프로그래머", "여성", 34, 1300));
}
};
List<Person> phpProgrammers = new ArrayList<Person>() {
{
add(new Person("Jarrod", "Pace", "PHP 프로그래머", "남성", 34, 1550));
add(new Person("Clarette", "Cicely", "PHP 프로그래머", "female", 23, 1200));
add(new Person("빅터", "채닝", "PHP 프로그래머", "남성", 32, 1600));
add(new Person("토리", "셰릴", "PHP 프로그래머", "여성", 21, 1000));
add(new Person("오스본", "Shad", "PHP 프로그래머", "남성", 32, 1100));
add(new Person("로잘린드", "라일라", "PHP 프로그래머", "여성", 25, 1300));
add(new Person("프레이저", "휴이", "PHP 프로그래머", "남성", 36, 1100));
add(new Person("퀸", "타마라", "PHP 프로그래머", "여성", 21, 1000));
add(new Person("앨빈", "Lance", "PHP 프로그래머", "남성", 38, 1600));
add(new Person("에본", "샤리", "PHP 프로그래머", "여성", 40, 1800));
}
};
이제 forEach 메서드를 사용하여 위 목록을 반복하고 출력합니다.
다음과 같이 코드 코드를 복사합니다.
System.out.println("모든 프로그래머의 이름:");
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
또한 프로그래머의 급여를 5% 인상하기 위해 forEach 메소드를 사용합니다:
다음과 같이 코드 코드를 복사합니다.
System.out.println("프로그래머에게 급여를 5% 인상합니다:");
Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);
또 다른 유용한 메소드는 filter()로, 월 급여가 $1400가 넘는 PHP 프로그래머를 표시할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
System.out.println("여기 월급이 $1,400가 넘는 PHP 프로그래머가 있습니다:")
phpProgrammers.stream()
.filter((p) -> (p.getSalary() > 1400))
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
필터를 정의한 다음 이를 재사용하여 다른 작업을 수행할 수도 있습니다.
다음과 같이 코드 코드를 복사합니다.
// 필터 정의
Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
Predicate<Person> 급여Filter = (p) -> (p.getSalary() > 1400);
Predicate<Person>genderFilter = (p) -> ("female".equals(p.getGender()));
System.out.println("여기에는 24세 이상이며 월급이 $1,400 이상인 여성 PHP 프로그래머가 있습니다.");
phpProgrammers.stream()
.filter(ageFilter)
.filter(급여필터)
.filter(성별필터)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
//필터 재사용
System.out.println("24세 이상의 여성 Java 프로그래머:");
javaProgrammers.stream()
.filter(ageFilter)
.filter(성별필터)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
결과 세트 수를 제한하려면 제한 방법을 사용하십시오.
다음과 같이 코드 코드를 복사합니다.
System.out.println("처음 3명의 Java 프로그래머:");
javaProgrammers.stream()
.한계(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("상위 3명의 여성 Java 프로그래머:");
javaProgrammers.stream()
.filter(성별필터)
.한도(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
정렬은 어떻습니까? 스트림에서 처리할 수 있습니까? 대답은 '예'입니다. 다음 예에서는 Java 프로그래머를 이름과 급여별로 정렬하고 목록에 넣은 다음 목록을 표시합니다.
다음과 같이 코드 코드를 복사합니다.
System.out.println("이름순으로 정렬하여 상위 5명의 Java 프로그래머를 표시합니다:");
List<Person> sortedJavaProgrammers = javaProgrammers
.개울()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.한계(5)
.collect(toList());
sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
System.out.println("월급에 따라 Java 프로그래머를 정렬합니다:");
sortedJavaProgrammers = javaProgrammers
.개울()
.sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.collect( toList() );
sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
최저 급여와 최고 급여에만 관심이 있는 경우 정렬 후 첫 번째/마지막을 선택하는 것보다 빠른 방법은 min 및 max 방법입니다.
다음과 같이 코드 코드를 복사합니다.
System.out.println("가장 낮은 급여를 받는 Java 프로그래머:");
개인 = javaProgrammers
.개울()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
.얻다()
System.out.printf("이름: %s %s; 급여: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
System.out.println("최고 연봉을 받는 자바 프로그래머:");
사람 사람 = javaProgrammers
.개울()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
.얻다()
System.out.printf("이름: %s %s; 급여: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())
위의 예에서 우리는 Collect 메소드가 어떻게 작동하는지 살펴보았습니다. map 메소드와 함께 Collect 메소드를 사용하여 결과 세트를 String, Set 또는 TreeSet에 넣을 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
System.out.println("PHP 프로그래머의 이름을 문자열로 연결합니다:");
문자열 phpDevelopers = phpProgrammers
.개울()
.map(사람::getFirstName)
.collect(joining(" ; ")); // 추가 작업에서 토큰으로 사용할 수 있습니다.
System.out.println("설정할 Java 프로그래머의 이름을 저장하십시오:");
Set<String> javaDevFirstName = javaProgrammers
.개울()
.map(사람::getFirstName)
.collect(toSet());
System.out.println("Java 프로그래머의 이름을 TreeSet에 저장합니다:");
TreeSet<String> javaDevLastName = javaProgrammers
.개울()
.map(사람::getLastName)
.collect(toCollection(TreeSet::new));
스트림은 병렬일 수도 있습니다. 예는 다음과 같습니다:
다음과 같이 코드 코드를 복사합니다.
System.out.println("Java 프로그래머에게 지불된 모든 돈을 계산하세요:");
int totalSalary = javaProgrammers
.parallelStream()
.mapToInt(p -> p.getSalary())
.합집합();
summaryStatistics 메소드를 사용하여 스트림에 있는 요소의 다양한 요약 데이터를 얻을 수 있습니다. 다음으로 getMax, getMin, getSum 또는 getAverage와 같은 메소드에 액세스할 수 있습니다.
다음과 같이 코드 코드를 복사합니다.
//숫자의 개수, 최소값, 최대값, 합계 및 평균을 계산합니다.
List<Integer> 숫자 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics 통계 = 숫자
.개울()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("목록에서 가장 큰 숫자: " + stats.getMax());
System.out.println("목록에서 가장 작은 숫자: " + stats.getMin());
System.out.println("모든 숫자의 합: " + stats.getSum());
System.out.println("모든 숫자의 평균: " + stats.getAverage());
좋아요, 그게 다입니다. 마음에 드셨으면 좋겠습니다!
요약
이 기사에서는 기본 예제부터 람다 및 스트림을 사용하는 보다 복잡한 예제에 이르기까지 람다 식을 사용하는 다양한 방법을 배웠습니다. 또한 람다 표현식과 Comparator 클래스를 사용하여 Java 컬렉션을 정렬하는 방법도 배웠습니다.
번역가의 메모: 매우 고급스러워 보이지만 람다 표현식의 본질은 컴파일러가 유추하는 "구문 설탕"일 뿐이며 이를 일반 코드로 변환하고 래핑하는 데 도움이 되므로 더 적은 코드를 사용하여 동일한 기능을 달성할 수 있습니다. . 아주 고급 해커들이 작성한 코드와 똑같기 때문에 무분별하게 사용하지 않는 것이 좋습니다. 간결하고 이해하기 어렵고 디버그하기 어렵고 유지 관리 직원이 혼내고 싶을 것입니다.