자바 8 스트림 : limit ()와 skip ()의 차이점
Stream이 코드를 실행할 때 s에 대해 이야기 하면
public class Main {
public static void main(String[] args) {
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x->System.out.print("\nA"+x))
.limit(3)
.peek(x->System.out.print("B"+x))
.forEach(x->System.out.print("C"+x));
}
}
이 출력을 얻습니다.
A1B1C1
A2B2C2
A3B3C3
스트림을 처음 세 개의 구성 요소로 제한하면 A , B 및 C 작업이 세 번만 실행되도록 강제 하기 때문입니다.
skip()메서드 를 사용하여 마지막 세 요소에 대해 유사한 계산을 수행하려고 하면 다른 동작이 나타납니다.
public class Main {
public static void main(String[] args) {
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x->System.out.print("\nA"+x))
.skip(6)
.peek(x->System.out.print("B"+x))
.forEach(x->System.out.print("C"+x));
}
}
이것을 출력
A1
A2
A3
A4
A5
A6
A7B7C7
A8B8C8
A9B9C9
이 경우 A1 ~ A6 작업 이 실행되는 이유는 무엇 입니까? 그것은 사실과 함께 할 수있는 뭔가가 있어야 한계 A는 단락 상태 중간 동작을 하면서, 스킵이 되지 않습니다,하지만 난이 속성의 실제 의미를 이해하지 않습니다. " 한도 이전의 모든 사람이 실행되지 않는 동안 건너 뛰기 전 모든 작업 이 실행되는 "것입니까?
여기있는 것은 두 개의 스트림 파이프 라인입니다.
이러한 스트림 파이프 라인은 각각 소스, 여러 중간 작업 및 터미널 작업으로 구성됩니다.
그러나 중간 작업은 게으르다. 즉, 다운 스트림 작업에 항목이 필요하지 않으면 아무 일도 일어나지 않습니다. 이 경우 중간 작업은 필요한 항목을 생성하는 데 필요한 모든 작업을 수행 한 다음 다른 항목이 요청 될 때까지 다시 대기합니다.
터미널 작업은 일반적으로 "열망"합니다. 즉, 완료하는 데 필요한 스트림의 모든 항목을 요청합니다.
따라서 파이프 라인은 forEach뒤에있는 스트림에 다음 항목을 요청하는 것으로 생각해야합니다. 그 스트림은 뒤에있는 스트림에 소스까지 계속 요청합니다.
이를 염두에두고 첫 번째 파이프 라인에 대해 살펴 보겠습니다.
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x->System.out.print("\nA"+x))
.limit(3)
.peek(x->System.out.print("B"+x))
.forEach(x->System.out.print("C"+x));
그래서 forEach첫 번째 항목을 요구합니다. 즉, "B" peek가 항목을 필요로하고 이에 대한 limit출력 스트림 limit을 요청합니다 peek. 즉, 소스로 이동 하는 "A"를 요청해야 함을 의미 합니다. 항목이 주어지고.까지 올라가면 forEach첫 번째 줄이 표시됩니다.
A1B1C1
는 forEach또 다른 다음, 다른 항목을 요청합니다. 그리고 매번 요청이 스트림으로 전파되고 수행됩니다. 그러나 forEach네 번째 항목을 요청할 때 요청이에 도달하면 limit제공 할 수있는 모든 항목을 이미 제공했음을 알고 있습니다.
따라서 다른 항목에 대해 "A"엿보기를 요구하지 않습니다. 즉시 해당 항목이 소진되었음을 나타내므로 더 이상 작업이 수행되지 않고 forEach종료됩니다.
두 번째 파이프 라인에서는 어떻게됩니까?
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x->System.out.print("\nA"+x))
.skip(6)
.peek(x->System.out.print("B"+x))
.forEach(x->System.out.print("C"+x));
다시, forEach첫 번째 항목을 요청합니다. 이것은 다시 전파됩니다. 그러나에 도착하면 skip하나의 다운 스트림을 통과하기 전에 업스트림에서 6 개 항목을 요청해야한다는 것을 알고 있습니다. 따라서 "A"에서 요청을 업스트림 peek하고 다운 스트림을 전달하지 않고 소비하고 다른 요청을 만드는 등의 작업을 수행합니다. 따라서 "A"peek는 항목에 대해 6 개의 요청을 받고 6 개의 인쇄물을 생성하지만 이러한 항목은 전달되지 않습니다.
A1
A2
A3
A4
A5
A6
의 7 번째 요청 skip에서 항목이 "B"엿보기로 전달되고 여기에서로 전달 forEach되므로 전체 인쇄가 완료됩니다.
A7B7C7
그럼 전과 같습니다. 이제 skip의지는 요청을받을 때마다 항목을 업스트림으로 요청하고 해당 항목을 이미 건너 뛰는 작업을 완료했음을 "알고"다운 스트림으로 전달합니다. 따라서 나머지 인쇄물은 소스가 고갈 될 때까지 전체 파이프를 통과합니다.
스트리밍 파이프 라인의 유창한 표기법이 이러한 혼란의 원인입니다. 다음과 같이 생각해보십시오.
limit(3)
"파이프 라인 실행" 을 트리거 forEach()하는 터미널 작업 인을 제외하고 모든 파이프 라인 작업은 느리게 평가 됩니다.
파이프 라인이 실행될 때 중간 스트림 정의는 "이전" 또는 "이후" 발생에 대해 어떠한 가정도하지 않습니다 . 그들이하는 일은 입력 스트림을 출력 스트림으로 변환하는 것입니다.
Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9);
Stream<Integer> s2 = s1.peek(x->System.out.print("\nA"+x));
Stream<Integer> s3 = s2.limit(3);
Stream<Integer> s4 = s3.peek(x->System.out.print("B"+x));
s4.forEach(x->System.out.print("C"+x));
s19 개의 다른Integer값을 포함 합니다.s2전달하는 모든 값을 들여다보고 인쇄합니다.s3처음 3 개의 값을 전달하고s4세 번째 값 이후의 파이프 라인을 중단합니다. 에서 더 이상 값을 생성하지 않습니다s3. 이것은 더 이상 값이 파이프 라인에 없다는 것을 의미하지 않습니다.s2여전히 더 많은 값을 생성 (인쇄)하지만 아무도 해당 값을 요청하지 않으므로 실행이 중지됩니다.s4다시 전달하는 모든 값을 들여다보고 인쇄합니다.forEachs4전달되는 모든 것을 소비하고 인쇄 합니다.
이렇게 생각해보십시오. 전체 스트림이 완전히 게으르다. 터미널 작업 만이 파이프 라인에서 새 값을 적극적으로 가져옵니다 . 이 세 값에서 가져온 후 s4 <- s3 <- s2 <- s1, s3더 이상 새로운 값을 생성하지 않습니다 그것은 더 이상에서 모든 값을 가져옵니다 s2 <- s1. s1 -> s2여전히를 생성 할 수 있지만 4-9이러한 값은 파이프 라인에서 가져 오지 않으므로 s2.
skip(6)
와 skip()같은 일이 발생 :
Stream<Integer> s1 = Stream.of(1,2,3,4,5,6,7,8,9);
Stream<Integer> s2 = s1.peek(x->System.out.print("\nA"+x));
Stream<Integer> s3 = s2.skip(6);
Stream<Integer> s4 = s3.peek(x->System.out.print("B"+x));
s4.forEach(x->System.out.print("C"+x));
s19 개의 다른Integer값을 포함 합니다.s2전달하는 모든 값을 들여다보고 인쇄합니다.s3는 처음 6 개 값을 사용합니다. "건너 뛰기" 는 처음 6 개 값이에 전달되지 않고s4후속 값만 전달됨을 의미합니다 .s4again peeks at all values that pass it and prints them.forEachconsumes and prints whatevers4passes to it.
The important thing here is that s2 is not aware of the remaining pipeline skipping any values. s2 peeks at all values independently of what happens afterwards.
Another example:
Consider this pipeline, which is listed in this blog post
IntStream.iterate(0, i -> ( i + 1 ) % 2)
.distinct()
.limit(10)
.forEach(System.out::println);
When you execute the above, the program will never halt. Why? Because:
IntStream i1 = IntStream.iterate(0, i -> ( i + 1 ) % 2);
IntStream i2 = i1.distinct();
IntStream i3 = i2.limit(10);
i3.forEach(System.out::println);
Which means:
i1generates an infinite amount of alternating values:0,1,0,1,0,1, ...i2consumes all values that have been encountered before, passing on only "new" values, i.e. there are a total of 2 values coming out ofi2.i3passes on 10 values, then stops.
This algorithm will never stop, because i3 waits for i2 to produce 8 more values after 0 and 1, but those values never appear, while i1 never stops feeding values to i2.
It doesn't matter that at some point in the pipeline, more than 10 values had been produced. All that matters is that i3 has never seen those 10 values.
To answer your question:
Is it just that "every action before skip is executed while not everyone before limit is"?
Nope. All operations before either skip() or limit() are executed. In both of your executions, you get A1 - A3. But limit() may short-circuit the pipeline, aborting value consumption once the event of interest (the limit is reached) has occurred.
It is complete blasphemy to look at steam operations individually because that is not how a stream is evaluated.
Talking about limit(3), it is a short circuit operation, which makes sense because thinking about it, whatever operation is before and after the limit, having a limit in a stream would stop iteration after getting n elements till the limit operation, but this doesn't mean that only n stream elements would be processed. Take this different stream operation for an example
public class App
{
public static void main(String[] args) {
Stream.of(1,2,3,4,5,6,7,8,9)
.peek(x->System.out.print("\nA"+x))
.filter(x -> x%2==0)
.limit(3)
.peek(x->System.out.print("B"+x))
.forEach(x->System.out.print("C"+x));
}
}
would output
A1
A2B2C2
A3
A4B4C4
A5
A6B6C6
which seem right, because limit is waiting for 3 stream elements to pass through the operation chain, although 6 elements of stream are processed.
All streams are based on spliterators, which have basically two operations: advance (move forward one element, similar to iterator) and split (divide oneself in arbitrary position, which is suitable for parallel processing). You can stop taking input elements at any moment you like (which is done by limit), but you cannot just jump to the arbitrary position (there's no such operation in Spliterator interface). Thus skip operation need to actually read the first elements from the source just to ignore them. Note that in some cases you can perform actual jump:
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
list.stream().skip(3)... // will read 1,2,3, but ignore them
list.subList(3, list.size()).stream()... // will actually jump over the first three elements
Maybe this little diagram helps to get some natural "feeling" for how the stream is processed.
The first line =>8=>=7=...=== depicts the stream. The elements 1..8 are flowing from the left to the right. There are three "windows":
- In the first window (
peek A) you see everything - In the second window (
skip 6orlimit 3) a kind of filtering is done. Either the first or the last elements are "eliminated" - means not passed on for further processing. - In the third window you see only those items that were passed on
┌────────────────────────────────────────────────────────────────────────────┐ │ │ │▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸ ▸▸▸▸▸▸▸▸▸▸▸ ▸▸▸▸▸▸▸▸▸▸ ▸▸▸▸▸▸▸▸▸ │ │ 8 7 6 5 4 3 2 1 │ │▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸▸ ▲ ▸▸▸▸▸▸▸▸▸▸▸ ▲ ▸▸▸▸▸▸▸▸▸▸ ▲ ▸▸▸▸▸▸▸▸▸ │ │ │ │ │ │ │ │ skip 6 │ │ │ peek A limit 3 peek B │ └────────────────────────────────────────────────────────────────────────────┘
Probably not everything (maybe not even anything) in this explanation is technically completely correct. But when I see it like this it's quite clear to me what items reach which of the concatenated instructions.
참고URL : https://stackoverflow.com/questions/32414088/java-8-stream-difference-between-limit-and-skip
'IT TIP' 카테고리의 다른 글
| web.xml의 세션 시간 초과 (0) | 2020.11.30 |
|---|---|
| PHP는 상대 경로를 포함합니다 (0) | 2020.11.30 |
| 동일한 Xcode 프로젝트에서 Swift, Objective-C, C 및 C ++ 파일을 가질 수 있습니까? (0) | 2020.11.30 |
| MySQL SELECT를 사용하여 가상 열을 만드는 방법은 무엇입니까? (0) | 2020.11.29 |
| C 코드 블록을 중괄호로 묶는 이유는 무엇입니까? (0) | 2020.11.29 |