JavaRush /Java Blog /Random-KO /커피 브레이크 #177. Java 8의 Java Stream에 대한 자세한 가이드

커피 브레이크 #177. Java 8의 Java Stream에 대한 자세한 가이드

Random-KO 그룹에 게시되었습니다
출처: Hackernoon 이 게시물은 코드 예제 및 설명과 함께 Java Stream 작업에 대한 자세한 튜토리얼을 제공합니다. 커피 브레이크 #177.  Java 8 - 1의 Java Stream에 대한 자세한 안내서

Java 8의 Java 스레드 소개

Java 8의 일부로 도입된 Java Streams는 데이터 컬렉션 작업에 사용됩니다. 이는 데이터 구조 자체는 아니지만 최종 결과를 생성하기 위해 순서를 지정하고 파이프라인하여 다른 데이터 구조에서 정보를 입력하는 데 사용할 수 있습니다. 참고: 스트림과 스레드를 혼동하지 않는 것이 중요합니다. 러시아어에서는 두 용어가 종종 동일한 번역 "흐름"으로 언급되기 때문입니다. Stream은 작업(대개 데이터 전송 또는 저장)을 수행하기 위한 개체를 나타내는 반면, Thread(문자 그대로 번역 - 스레드)는 특정 프로그램 코드가 다른 코드 분기와 병렬로 실행될 수 있도록 하는 개체를 나타냅니다. Stream은 별도의 데이터 구조가 아니기 때문에 데이터 소스를 변경하지 않습니다. Java 스트림에는 다음과 같은 기능이 있습니다.
  1. Java Stream은 “java.util.stream” 패키지를 사용하여 사용할 수 있습니다. 다음 코드를 사용하여 스크립트로 가져올 수 있습니다.

    import java.util.stream.* ;

    이 코드를 사용하면 Java Stream에 여러 내장 기능을 쉽게 구현할 수도 있습니다.

  2. Java Stream은 Java의 컬렉션 및 배열과 같은 데이터 컬렉션의 입력을 받아들일 수 있습니다.

  3. Java Stream에서는 입력 데이터 구조를 변경할 필요가 없습니다.

  4. Java Stream은 소스를 변경하지 않습니다. 대신 적절한 파이프라인 방법을 사용하여 출력을 생성합니다.

  5. Java Streams에는 중간 및 터미널 작업이 적용되며 이에 대해서는 다음 섹션에서 설명합니다.

  6. Java Stream에서 중간 작업은 파이프라인으로 이루어지며 지연 평가 형식으로 발생합니다. 터미널 기능으로 끝납니다. 이는 Java Stream을 사용하기 위한 기본 형식을 형성합니다.

다음 섹션에서는 필요할 때 Java 스트림을 생성하기 위해 Java 8에서 사용되는 다양한 방법을 살펴보겠습니다.

Java 8에서 Java 스트림 생성

Java 스레드는 여러 가지 방법으로 생성할 수 있습니다.

1. Stream.empty() 메서드를 사용하여 빈 스트림 만들기

나중에 코드에서 사용하기 위해 빈 스트림을 만들 수 있습니다. Stream.empty() 메서드를 사용하면 값이 포함되지 않은 빈 스트림이 생성됩니다. 이 빈 스트림은 런타임 시 널 포인터 예외를 건너뛰려는 경우 유용할 수 있습니다. 이렇게 하려면 다음 명령을 사용할 수 있습니다.
Stream<String> str = Stream.empty();
위의 명령문은 내부에 요소가 없는 str 이라는 빈 스트림을 생성합니다 . 이를 확인하려면 str.count() 용어를 사용하여 스트림의 수나 크기를 확인하십시오 . 예를 들어,
System.out.println(str.count());
결과적으로 출력값은 0이 됩니다 .

2. Stream.Builder 인스턴스와 함께 Stream.builder() 메서드를 사용하여 스트림을 생성합니다.

Stream Builder를 사용하여 빌더 디자인 패턴을 사용하여 스트림을 생성 할 수도 있습니다 . 이는 객체의 단계별 구성을 위해 설계되었습니다. Stream Builder를 사용하여 스트림을 인스턴스화하는 방법을 살펴보겠습니다 .
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
이 코드를 사용하면 int 요소가 포함된 numStream 이라는 스트림을 생성할 수 있습니다 . 먼저 생성된 numBuilder 라는 Stream.Builder 인스턴스 덕분에 모든 작업이 매우 빠르게 완료됩니다 .

3. Stream.of() 메소드를 사용하여 지정된 값으로 스트림을 생성합니다.

스트림을 생성하는 또 다른 방법은 Stream.of() 메서드를 사용하는 것입니다 . 이는 주어진 값으로 스트림을 생성하는 간단한 방법입니다. 스레드를 선언하고 초기화합니다. Stream.of() 메서드를 사용하여 스트림을 생성하는 예:
Stream<Integer> numStream = Stream.of(1, 2, 3);
이 코드는 Stream.Builder를 사용하여 이전 방법에서 했던 것처럼 int 요소를 포함하는 스트림을 생성합니다 . 여기서는 미리 정의된 값 [1, 2 및 3] 을 사용하여 Stream.of()를 사용하여 스트림을 직접 생성했습니다 .

4. Arrays.stream() 메서드를 사용하여 기존 배열에서 스트림을 만듭니다.

스레드를 생성하는 또 다른 일반적인 방법은 Java에서 배열을 사용하는 것입니다. 여기의 스트림은 Arrays.stream() 메서드를 사용하여 기존 배열에서 생성됩니다 . 모든 배열 요소는 스트림 요소로 변환됩니다. 좋은 예는 다음과 같습니다.
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
이 코드는 정수 배열인 arr이라는 배열의 내용을 포함하는 numStream 을 생성합니다 .

5. Stream.concat() 메서드를 사용하여 두 개의 기존 스트림 병합

스트림을 생성하는 데 사용할 수 있는 또 다른 방법은 Stream.concat() 메서드입니다 . 두 개의 스레드를 결합하여 단일 스레드를 만드는 데 사용됩니다. 두 스트림 모두 순서대로 결합됩니다. 이는 첫 번째 스레드가 먼저 오고 두 번째 스레드가 이어지는 식으로 진행됨을 의미합니다. 이러한 연결의 예는 다음과 같습니다.
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
위의 명령문은 첫 번째 스트림 numStream1 과 두 번째 스트림 numStream2 의 요소를 하나씩 포함하는 CombinedStream 이라는 최종 스트림을 생성합니다 .

Java Stream을 사용한 작업 유형

이미 언급했듯이 Java 8에서는 Java Stream을 사용하여 중간 작업과 터미널 작업이라는 두 가지 유형의 작업을 수행할 수 있습니다. 각각을 더 자세히 살펴보겠습니다.

중간 작업

중간 작업은 출력 스트림을 생성하고 터미널 작업이 발생할 때만 실행됩니다. 이는 중간 작업이 지연되고 파이프라인으로 실행되며 터미널 작업을 통해서만 완료될 수 있음을 의미합니다. 지연 평가와 파이프라인에 대해서는 나중에 배우게 됩니다. 중간 작업의 예로는 filter() , map() , Different() , peek() , sorted() 등의 메서드가 있습니다.

터미널 운영

터미널 작업은 중간 작업의 실행을 완료하고 출력 스트림의 최종 결과도 반환합니다. 터미널 작업은 지연 실행 및 파이프라이닝의 종료를 알리기 때문에 이 스레드는 터미널 작업을 거친 후에 다시 사용할 수 없습니다. 터미널 작업의 예로는 forEach() , Collect() , count() , Reduce() 등의 메서드가 있습니다.

Java Stream을 사용한 작업의 예

중간 작업

다음은 Java Stream에 적용할 수 있는 일부 중간 작업의 몇 가지 예입니다.

필터()

이 메소드는 Java의 특정 조건자와 일치하는 스트림의 요소를 필터링하는 데 사용됩니다. 이렇게 필터링된 항목은 새로운 스트림을 구성합니다. 더 잘 이해하기 위해 예를 살펴보겠습니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
결론:
[98]
설명: 이 예에서는 요소(2로 나눌 수 있음)도 filter() 메서드를 사용하여 필터링되고 정수 목록 numStream 에 저장되며 그 내용은 나중에 인쇄되는 것을 볼 수 있습니다 . 98은 스트림의 유일한 짝수이므로 출력에 인쇄됩니다.

지도()

이 메서드는 원래 입력 스트림의 요소에 매핑된 함수를 실행하여 새 스트림을 만드는 데 사용됩니다. 아마도 새 스트림의 데이터 유형이 다를 수 있습니다. 예는 다음과 같습니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
결론:
[86, 130, 2, 196, 126]
설명: 여기서는 map() 메소드가 numStream 스트림 의 각 요소를 단순히 두 배로 늘리는 데 사용되는 것을 볼 수 있습니다 . 출력에서 볼 수 있듯이 스트림의 각 요소가 성공적으로 두 배가 되었습니다.

별개의()

이 방법은 중복 항목을 필터링하여 스트림의 개별 요소만 검색하는 데 사용됩니다. 동일한 예는 다음과 같습니다.
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
결론:
[43, 65, 1, 98, 63]
설명: 이 경우 numStream 에 대해 Different() 메소드가 사용됩니다 . 스트림에서 중복 항목을 제거하여 numList 의 모든 개별 요소를 검색합니다 . 출력에서 볼 수 있듯이 처음에 두 개의 중복 항목(63과 1)이 있었던 입력 스트림과 달리 중복 항목이 없습니다.

몰래 엿보다()

이 방법은 터미널 작업을 실행하기 전에 중간 변경 사항을 추적하는 데 사용됩니다. 이는 peek()를 사용하여 스트림의 각 요소에 대한 작업을 수행하여 추가 중간 작업을 수행할 수 있는 스트림을 생성할 수 있음을 의미합니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
결론:
매핑됨: 430 매핑됨: 650 매핑됨: 10 매핑됨: 980 매핑됨: 630 [430, 650, 10, 980, 630]
설명: 여기서는 map() 메서드가 스트림 요소에 적용되므로 중간 결과를 생성하는 데 peek () 메서드가 사용됩니다 . 여기서 우리는 print 문 에서 목록의 최종 내용을 인쇄하기 위해 Collect() 터미널 작업을 사용하기 전에도 각 스트림 요소 매핑에 대한 결과가 미리 순차적으로 인쇄된다는 것을 알 수 있습니다.

정렬()

sorted() 메소드는 스트림의 요소를 정렬하는 데 사용됩니다. 기본적으로 요소는 오름차순으로 정렬됩니다. 특정 정렬 순서를 매개변수로 지정할 수도 있습니다. 이 메서드의 구현 예는 다음과 같습니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
결론:
1 43 ​​​​63 65 98
설명: 여기서 sorted() 메소드 는 기본적으로 (특정 순서가 지정되지 않았기 때문에) 스트림의 요소를 오름차순으로 정렬하는 데 사용됩니다. 출력물에 인쇄된 항목이 오름차순으로 정렬된 것을 확인할 수 있습니다.

터미널 운영

다음은 Java 스트림에 적용할 수 있는 일부 터미널 작업의 몇 가지 예입니다.

각각()

forEach() 메서드는 스트림의 모든 요소를 ​​반복하고 각 요소에 대해 하나씩 함수를 실행하는 데 사용됩니다. 이는 for , while 등과 같은 루프 문에 대한 대안으로 작동합니다 . 예:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
결론:
43 65 1 98 63
설명: 여기서 forEach() 메소드 는 스트림의 각 요소를 하나씩 인쇄하는 데 사용됩니다.

세다()

count() 메서드는 스트림에 있는 전체 요소 수를 검색하는 데 사용됩니다. 이는 컬렉션의 총 요소 수를 결정하는 데 자주 사용되는 size() 메서드와 유사합니다. Java Stream과 함께 count() 메소드를 사용하는 예는 다음과 같습니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
결론:
5
설명: numStream에는 5개의 정수 요소가 포함되어 있으므로 count() 메서드를 사용하면 5가 출력됩니다.

모으다()

Collect() 메서드는 스트림 요소의 변경 가능한 축소를 수행하는 데 사용됩니다. 처리가 완료된 후 스트림에서 콘텐츠를 제거하는 데 사용할 수 있습니다. 축소를 수행하기 위해 Collector 클래스를 사용합니다 .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
결론:
[43, 65, 1, 63]
설명: 이 예에서는 스트림의 모든 홀수 요소가 필터링되어 홀수 라는 목록으로 수집/축소됩니다 . 마지막에는 이상한 것의 목록이 인쇄됩니다.

최소()최대()

min() 메서드 는 이름에서 알 수 있듯이 스트림에서 최소 요소를 찾는 데 사용할 수 있습니다. 마찬가지로 max() 메서드를 사용하여 스트림에서 최대 요소를 찾을 수 있습니다. 예를 들어 어떻게 사용할 수 있는지 이해해 봅시다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
결론:
가장 작은 요소: 1 가장 큰 요소: 98
설명: 이 예에서는 min () 메소드를 사용하여 numStream에서 가장 작은 요소를 인쇄 하고 max() 메소드를 사용하여 가장 큰 요소를 인쇄했습니다 . 여기서는 max() 메소드를 사용하기 전에 numStream 에 요소를 다시 추가했습니다 . 이는 min() 이 터미널 작업이고 원본 스트림의 내용을 파괴하여 최종 결과(이 경우 "가장 작은" 정수)만 반환하기 때문입니다.

findAny()findFirst()

findAny()는 스트림의 모든 요소를 ​​Optional 로 반환합니다 . 스트림이 비어 있으면 비어 있는 Optional 값도 반환됩니다. findFirst()는 스트림의 첫 번째 요소를 Optional 로 반환합니다 . findAny() 메서드 와 마찬가지로 findFirst() 메서드도 해당 스트림이 비어 있는 경우 빈 Optional 매개변수를 반환합니다. 이러한 방법을 기반으로 다음 예제를 살펴보겠습니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
결론:
선택 사항[43] 선택 사항.비어 있음
설명: 여기서 첫 번째 경우 findFirst() 메소드는 스트림의 첫 번째 요소를 Optional 로 반환합니다 . 그런 다음 스레드가 빈 스레드로 재할당되면 findAny() 메서드는 빈 Optional 을 반환합니다 .

allMatch() , anyMatch()noneMatch()

allMatch() 메서드는 스트림의 모든 요소가 특정 조건자와 일치하는지 확인하고 일치하면 부울 값 true를 반환하고 , 그렇지 않으면 false를 반환하는 데 사용됩니다 . 스트림이 비어 있으면 true 를 반환합니다 . anyMatch() 메소드는 스트림의 요소가 특정 조건자와 일치하는지 확인하는 데 사용됩니다. 그렇다면 true를 반환하고 , 그렇지 않으면 false를 반환합니다 . 스트림이 비어 있으면 false 를 반환합니다 . noneMatch() 메서드는 스트림에 조건자와 일치하는 요소가 없으면 true를 반환하고 , 그렇지 않으면 false를 반환합니다 . 이를 설명하는 예는 다음과 같습니다.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
결론:
거짓 참 거짓
설명: 1을 요소로 포함하는 스트림 numStream 의 경우 모든 요소가 1이 아니고 그 중 하나만 이기 때문에 allMatch() 메소드는 false를 반환합니다 . 요소 중 하나 이상이 1이기 때문에 anyMatch() 메서드는 true를 반환합니다 . noneMatch() 메서드는 1이 실제로 이 스트림의 요소로 존재하기 때문에 false를 반환합니다 .

Java Stream의 지연 평가

지연 평가는 Java 8에서 Java Streams로 작업할 때 최적화로 이어집니다. 주로 터미널 작업이 발생할 때까지 중간 작업을 지연시키는 것과 관련됩니다. 게으른 평가는 결과가 실제로 필요할 때까지 계산에 불필요한 리소스 낭비를 방지하는 역할을 합니다. 중간 작업의 결과인 출력 스트림은 터미널 작업이 완료된 후에만 생성됩니다. 지연 평가는 Java 스트림의 모든 중간 작업에서 작동합니다. 무한 스트림으로 작업할 때 지연 평가가 매우 유용하게 사용됩니다. 이렇게 하면 불필요한 처리가 많이 방지됩니다.

Java Stream의 파이프라인

Java 스트림의 파이프라인은 입력 스트림, 차례로 정렬된 0개 이상의 중간 작업, 마지막으로 터미널 작업으로 구성됩니다. Java Streams의 중간 작업은 느리게 수행됩니다. 이로 인해 파이프라인 중간 작업이 불가피해졌습니다. 기본적으로 중간 작업이 순서대로 결합된 파이프라인을 사용하면 지연 실행이 가능해집니다. 파이프라인은 최종적으로 터미널 작업이 발생한 후 수행해야 하는 중간 작업을 추적하는 데 도움이 됩니다.

결론

이제 오늘 배운 내용을 요약해 보겠습니다. 이 기사에서는:
  1. 우리는 Java Streams가 무엇인지 간략하게 살펴보았습니다.
  2. 그런 다음 Java 8에서 Java 스레드를 생성하는 다양한 기술을 배웠습니다.
  3. Java 스트림에서 수행할 수 있는 두 가지 주요 작업 유형(중간 작업과 터미널 작업)을 배웠습니다.
  4. 그런 다음 중간 작업과 터미널 작업의 몇 가지 예를 자세히 살펴보았습니다.
  5. 우리는 지연 평가에 대해 더 많이 배우고 마침내 Java 스레드의 파이프라인에 대해 배웠습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION