우리는 람다가 Java에 클로저 개념을 도입하기를 오랫동안 기다려왔지만 이를 컬렉션에서 사용하지 않으면 많은 가치를 잃게 됩니다. 기존 인터페이스를 람다 스타일로 마이그레이션하는 문제는 기본 메서드를 통해 해결되었습니다. 이 기사에서는 Java 컬렉션의 대량 데이터 작업(대량 작업)을 심층적으로 분석하고 람다의 가장 강력한 역할에 대한 미스터리를 풀어보겠습니다.
1.JSR335에 대하여
JSR은 Java 사양 요청의 약어로, Java 사양 요청을 의미합니다. Java 8 버전의 주요 개선 사항은 Java를 멀티 코어 프로세서용 코드를 더 쉽게 작성할 수 있도록 하는 것을 목표로 하는 Lambda 프로젝트(JSR 335)입니다. JSR 335=람다 표현 + 인터페이스 개선(기본 방법) + 일괄 데이터 작업. 이전 두 기사와 함께 JSR335 관련 내용을 완전히 배웠습니다.
2. 외부 반복과 내부 반복
과거에 Java 컬렉션은 내부 반복을 표현할 수 없었고 외부 반복의 한 가지 방법, 즉 for 또는 while 루프만 제공했습니다.
다음과 같이 코드 코드를 복사합니다.
사람 목록 = asList(new Person("Joe"), new Person("Jim"), new Person("John"));
for (사람 p : 사람) {
p.setLastName("Doe");
}
위의 예는 소위 외부 반복이라고 불리는 이전 접근 방식입니다. 루프는 고정된 시퀀스 루프입니다. 오늘날의 멀티 코어 시대에 병렬 루프를 실행하려면 위의 코드를 수정해야 합니다. 효율성이 얼마나 향상될 수 있는지는 아직 불확실하며 특정 위험(스레드 안전 문제 등)이 발생할 수 있습니다.
내부 반복을 설명하려면 Lambda와 같은 클래스 라이브러리를 사용해야 합니다. 람다 및 Collection.forEach를 사용하여 위 루프를 다시 작성해 보겠습니다.
다음과 같이 코드를 복사합니다. person.forEach(p->p.setLastName("Doe"));
이제 jdk 라이브러리는 루프를 제어합니다. 각 개인 개체에 성이 어떻게 설정되는지 신경 쓸 필요가 없습니다. 라이브러리는 실행 환경, 병렬, 비순차 또는 게으름에 따라 이를 수행하는 방법을 결정할 수 있습니다. 로딩. 이는 내부 반복이며 클라이언트는 p.setLastName 동작을 데이터로 API에 전달합니다.
실제로 내부 반복은 컬렉션의 일괄 작업과 밀접한 관련이 없습니다. 이를 통해 문법 표현의 변화를 느낄 수 있습니다. 일괄 작업과 관련하여 정말 흥미로운 점은 새로운 스트림 API입니다. 새로운 java.util.stream 패키지가 JDK 8에 추가되었습니다.
3.스트림 API
스트림은 데이터 흐름만을 나타내며 데이터 구조가 없으므로 한 번 통과한 후에는 더 이상 탐색할 수 없습니다. (이 점은 프로그래밍 시 주의가 필요합니다. 컬렉션과 달리 몇 번을 시도해도 데이터가 남아 있습니다. 탐색됩니다) 소스는 컬렉션, 배열, IO 등이 될 수 있습니다.
3.1 중간 및 최종점 방법
스트리밍은 빅데이터 운영을 위한 인터페이스를 제공하여 데이터 운영을 더 쉽고 빠르게 만듭니다. 필터링, 매핑, 순회 횟수 감소 등의 메서드가 있습니다. 이러한 메서드는 중간 메서드와 터미널 메서드의 두 가지 유형으로 구분됩니다. 중간 메서드는 기본적으로 항상 Stream을 반환해야 합니다. 우리는 최종 결과를 얻고 싶습니다. 그렇다면 엔드포인트 작업을 사용하여 스트림에서 생성된 최종 결과를 수집해야 합니다. 이 두 메서드의 차이점은 반환 값을 살펴보는 것입니다. Stream이면 중간 메서드이고, 그렇지 않으면 end 메서드입니다. 자세한 내용은 Stream의 API를 참조하세요.
여러 중간 방법(필터, 맵)과 끝점 방법(수집, 합계)을 간략하게 소개합니다.
3.1.1필터
데이터 스트림에서 필터링 기능을 구현하는 것은 우리가 생각할 수 있는 가장 자연스러운 작업입니다. Stream 인터페이스는 필터 조건을 정의하는 람다 식을 사용하는 작업을 나타내는 Predicate 구현을 허용하는 필터 메서드를 노출합니다.
다음과 같이 코드 코드를 복사합니다.
명 목록 = …
스트림 personOver18 = person.stream().filter(p -> p.getAge() > 18);//18세 이상의 사람을 필터링합니다.
3.1.2지도
객체를 변환할 때와 같이 지금 일부 데이터를 필터링한다고 가정해 보겠습니다. Map 연산을 사용하면 입력 매개변수를 받아들이고 이를 반환하는 함수(Function<T, R>의 일반 T와 R은 각각 실행 입력과 실행 결과를 나타냄)의 구현을 실행할 수 있습니다. 먼저 이를 익명 내부 클래스로 설명하는 방법을 살펴보겠습니다.
다음과 같이 코드 코드를 복사합니다.
스트림 성인 = 명
.개울()
.filter(p -> p.getAge() > 18)
.map(새 함수() {
@보수
공공성인신청(인명) {
return new Adult(person);//18세 이상의 사람을 성인으로 변환
}
});
이제 위의 예를 람다 식으로 변환합니다.
다음과 같이 코드 코드를 복사합니다.
스트림 맵 = person.stream()
.filter(p -> p.getAge() > 18)
.map(사람 -> 새로운 성인(사람));
3.1.3개수
count 메소드는 스트림 결과의 최종 통계를 만들고 정수를 반환할 수 있는 스트림의 끝점 메소드입니다. 예를 들어 18세 이상의 총 사람 수를 계산해 보겠습니다.
다음과 같이 코드 코드를 복사합니다.
int countOfAdult=persons.stream()
.filter(p -> p.getAge() > 18)
.map(사람 -> 새로운 성인(사람))
.세다();
3.1.4수집
Collect 메소드는 최종 결과를 수집할 수 있는 스트림의 엔드포인트 메소드이기도 합니다.
다음과 같이 코드 코드를 복사합니다.
성인목록=person.stream() 나열
.filter(p -> p.getAge() > 18)
.map(사람 -> 새로운 성인(사람))
.collect(Collectors.toList());
또는 특정 구현 클래스를 사용하여 결과를 수집하려는 경우:
다음과 같이 코드 코드를 복사합니다.
성인 목록 = 명 목록
.개울()
.filter(p -> p.getAge() > 18)
.map(사람 -> 새로운 성인(사람))
.collect(Collectors.toCollection(ArrayList::new));
지면의 제약으로 인해 다른 중간 방법과 최종점 방법을 하나씩 소개하지 않습니다. 위의 예를 읽은 후에는 이 두 방법의 차이점만 이해하면 되며 필요에 따라 사용하기로 결정하면 됩니다. 나중에.
3.2 순차 흐름과 병렬 흐름
각 스트림에는 순차 실행과 병렬 실행이라는 두 가지 모드가 있습니다.
시퀀스 흐름:
다음과 같이 코드 코드를 복사합니다.
List <Person> people = list.getStream.collect(Collectors.toList());
병렬 스트림:
다음과 같이 코드 코드를 복사합니다.
List <Person> people = list.getStream.parallel().collect(Collectors.toList());
이름에서 알 수 있듯이 순차 방법을 사용하여 순회하는 경우 다음 항목을 읽기 전에 각 항목을 읽습니다. 병렬 순회를 사용하면 배열이 여러 세그먼트로 나누어지고 각 세그먼트는 서로 다른 스레드에서 처리된 다음 결과가 함께 출력됩니다.
3.2.1 병렬 스트림 원리:
다음과 같이 코드 코드를 복사합니다.
List OriginalList = someData;
Split1 = originalList(0, mid);//데이터를 작은 부분으로 나눕니다.
Split2 = OriginalList(mid,end);
new Runnable(split1.process());//작은 부분에서 작업 실행
새로운 실행 가능(split2.process());
수정된 목록 목록 = 분할1 + 분할2;//결과 병합
3.2.2 순차 및 병렬 성능 테스트 비교
멀티 코어 머신인 경우 이론적으로 병렬 스트림은 순차 스트림보다 2배 빠릅니다. 다음은 테스트 코드입니다.
다음과 같이 코드 코드를 복사합니다.
긴 t0 = System.nanoTime();
//100만 범위의 정수 스트림을 초기화하고 2로 나눌 수 있는 숫자를 찾습니다. toArray()는 끝점 메서드입니다.
int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();
긴 t1 = System.nanoTime();
//위와 동일한 함수, 여기서는 병렬 스트림을 사용하여 계산합니다.
int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();
긴 t2 = System.nanoTime();
//내 로컬 컴퓨터의 결과는 직렬: 0.06초, 병렬 0.02초입니다. 이는 병렬 흐름이 실제로 순차 흐름보다 빠르다는 것을 증명합니다.
System.out.printf("직렬: %.2fs, 병렬 %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);
3.3 Folk/Join 프레임워크 정보
애플리케이션 하드웨어 병렬 처리는 Java 7에서 사용할 수 있습니다. java.util.concurrent 패키지의 새로운 기능 중 하나는 포크 조인(fork-join) 스타일 병렬 분해 프레임워크입니다. 이는 또한 매우 강력하고 효율적입니다. Stream.parallel()에 비해 저는 후자를 선호합니다.
4. 요약
람다가 없으면 Stream은 사용하기 매우 불편합니다. 위의 3.1.2map 예제와 같이 익명의 내부 클래스가 많이 생성됩니다. 기본 메서드가 없으면 컬렉션 프레임워크를 변경하면 필연적으로 많은 변경이 발생합니다. 따라서 람다+기본 방법은 jdk 라이브러리를 더욱 강력하고 유연하게 만들어줍니다. 스트림 및 컬렉션 프레임워크의 개선이 가장 좋은 증거입니다.