a List
와 그 stream()
방법을 사용할 때 방금 질문을 받았습니다. 내가 알고 있지만 방법 을 사용하여, 나는 확신에 대한 아니에요 때 사용할 수 있습니다.
예를 들어, 다른 위치에 대한 다양한 경로가 포함 된 목록이 있습니다. 이제 주어진 단일 경로에 목록에 지정된 경로가 포함되어 있는지 확인하고 싶습니다. boolean
조건이 충족되었는지 여부에 따라 를 반환하고 싶습니다 .
물론 이것은 어려운 작업이 아닙니다. 하지만 스트림을 사용해야하는지, 아니면 for (-each) 루프를 사용해야하는지 궁금합니다.
목록
private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{
"my/path/one",
"my/path/two"
});
예-스트림
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream()
.map(String::toLowerCase)
.filter(path::contains)
.collect(Collectors.toList())
.size() > 0;
}
예-For-Each 루프
private boolean isExcluded(String path){
for (String excludePath : EXCLUDE_PATHS) {
if(path.contains(excludePath.toLowerCase())){
return true;
}
}
return false;
}
참고 것을 path
매개 변수는 항상 소문자 .
내 첫 번째 추측은 for-each 접근 방식이 더 빠르다는 것입니다. 조건이 충족되면 루프가 즉시 반환되기 때문입니다. 필터링을 완료하기 위해 스트림은 모든 목록 항목을 계속 반복합니다.
내 가정이 맞습니까? 그렇다면 왜 (또는 오히려 언제 ) 사용 stream()
합니까?
답변
당신의 가정이 맞습니다. 스트림 구현이 for 루프보다 느립니다.
이 스트림 사용은 for 루프만큼 빠릅니다.
EXCLUDE_PATHS.stream()
.map(String::toLowerCase)
.anyMatch(path::contains);
이것은 항목을 반복 String::toLowerCase
하고 항목에 하나씩 적용 하고 필터를 적용 하고 첫 번째 항목에서 종료합니다. 일치 .
collect()
& 둘 다 anyMatch()
터미널 작업입니다. anyMatch()
그러나 collect()
모든 항목을 처리해야하는 동안 처음 발견 된 항목에서 종료 됩니다.
답변
Streams 사용 여부는 성능 고려가 아니라 가독성에 따라 결정해야합니다. 실제로 성능과 관련하여 다른 고려 사항이 있습니다.
당신으로 .filter(path::contains).collect(Collectors.toList()).size() > 0
접근, 당신은 모든 요소를 처리하고 임시로 수집List
하고 크기를 비교하기 전에 하지만 두 요소로 구성된 스트림에는 거의 문제가되지 않습니다.
.map(String::toLowerCase).anyMatch(path::contains)
요소 수가 상당히 많은 경우을 사용 하면 CPU주기와 메모리를 절약 할 수 있습니다. 그래도 String
일치 항목이 발견 될 때까지 각각 을 소문자 표현으로 변환합니다 . 분명히, 사용에 요점이 있습니다
private static final List<String> EXCLUDE_PATHS =
Stream.of("my/path/one", "my/path/two").map(String::toLowerCase)
.collect(Collectors.toList());
private boolean isExcluded(String path) {
return EXCLUDE_PATHS.stream().anyMatch(path::contains);
}
대신. 따라서를 호출 할 때마다 소문자로의 변환을 반복 할 필요가 없습니다 isExcluded
. EXCLUDE_PATHS
문자열 의 요소 수 또는 길이가 정말 커지면 다음을 사용하는 것이 좋습니다.
private static final List<Predicate<String>> EXCLUDE_PATHS =
Stream.of("my/path/one", "my/path/two").map(String::toLowerCase)
.map(s -> Pattern.compile(s, Pattern.LITERAL).asPredicate())
.collect(Collectors.toList());
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream().anyMatch(p -> p.test(path));
}
LITERAL
플래그를 사용 하여 문자열을 정규식 패턴으로 컴파일하면 일반 문자열 작업처럼 동작하지만 엔진이 예를 들어 Boyer Moore 알고리즘을 사용하여 준비하는 데 약간의 시간을 소비하여 실제 비교와 관련하여 더 효율적입니다.
물론 이것은 준비에 소요되는 시간을 보상 할 수있는 충분한 후속 테스트가있는 경우에만 효과가 있습니다. 이 작업이 성능에 매우 중요한지 여부를 묻는 첫 번째 질문 외에 실제 성능 고려 사항 중 하나입니다. Streams를 사용할지 아니면for
루프 .
그건 그렇고, 위의 코드 예제는 원래 코드의 논리를 유지하므로 나에게는 의심스러워 보입니다. 귀하의 isExcluded
방법을 반환 true
, 그것은 반환하도록 지정된 경로가,리스트 내의 요소 중 하나를 포함하는 경우 true
에 대한 /some/prefix/to/my/path/one
뿐만 아니라,로 my/path/one/and/some/suffix
또는 /some/prefix/to/my/path/one/and/some/suffix
.
심지어 dummy/path/onerous
는 contains
문자열 로 기준을 충족하는 것으로 간주됩니다 my/path/one
.
답변
네. 당신이 옳습니다. 스트림 접근 방식에는 약간의 오버 헤드가 있습니다. 그러나 다음과 같은 구성을 사용할 수 있습니다.
private boolean isExcluded(String path) {
return EXCLUDE_PATHS.stream().map(String::toLowerCase).anyMatch(path::contains);
}
스트림을 사용하는 주된 이유는 코드를 더 간단하고 읽기 쉽게 만들기 때문입니다.
답변
Java 스트림의 목표는 병렬 코드 작성의 복잡성을 단순화하는 것입니다. 함수형 프로그래밍에서 영감을 얻었습니다. 직렬 스트림은 코드를 더 깔끔하게 만드는 것입니다.
성능을 원한다면, 설계된 parallelStream을 사용해야합니다. 일반적으로 시리얼은 더 느립니다.
좋은에 대해 읽을 수있는 글이 , 및 실적 ForLoop
Stream
ParallelStream
.
코드에서 종료 방법을 사용하여 첫 번째 일치에서 검색을 중지 할 수 있습니다. (anyMatch …)
답변
다른 사람들이 많은 좋은 점을 언급했듯이 스트림 평가에서 지연 평가 를 언급하고 싶습니다 . map()
소문자 경로의 스트림을 생성 할 때 전체 스트림을 즉시 생성하지 않고 대신 스트림이 느리게 구성 되므로 성능이 기존 for 루프와 동일해야합니다. 그것은 전체 검사를 수행하지 않는, map()
그리고 anyMatch()
같은 시간에 실행됩니다. anyMatch()
true를 반환 하면 단락됩니다.
답변
