Stream1. 클래스 의 메서드 목록

이 클래스는 데이터 스트림 체인을 쉽게 구성Stream 할 수 있도록 만들어졌습니다 . 이를 달성하기 위해 클래스에는 새 개체를 반환하는 메서드가 있습니다 .Stream<T>Stream

이러한 각 데이터 스트림은 하나의 간단한 작업을 수행하지만 이를 체인으로 결합하고 흥미로운 람다 함수를 추가 하면 원하는 출력을 생성하는 강력한 메커니즘을 갖게 됩니다. 곧 당신은 스스로 보게 될 것입니다.

다음은 클래스의 메서드입니다 Stream(가장 기본적인 메서드만).

행동 양식 설명
Stream<T> of()
개체 집합에서 스트림을 만듭니다.
Stream<T> generate()
지정된 규칙에 따라 스트림을 생성합니다.
Stream<T> concat()
두 개의 스트림을 연결
Stream<T> filter()
지정된 규칙과 일치하는 데이터만 전달하여 데이터를 필터링합니다.
Stream<T> distinct()
중복을 제거합니다. 이미 발생한 데이터는 전달하지 않습니다.
Stream<T> sorted()
데이터 정렬
Stream<T> peek()
스트림의 각 요소에 대해 작업을 수행합니다.
Stream<T> limit(n)
지정된 제한보다 길지 않도록 잘린 스트림을 반환합니다.
Stream<T> skip(n)
처음 n개의 요소를 건너뜁니다.
Stream<R> map()
데이터를 한 유형에서 다른 유형으로 변환
Stream<R> flatMap()
데이터를 한 유형에서 다른 유형으로 변환
boolean anyMatch()
지정된 규칙과 일치하는 요소가 스트림에 하나 이상 있는지 확인합니다.
boolean allMatch()
스트림의 모든 요소가 지정된 규칙과 일치하는지 확인합니다.
boolean noneMatch()
스트림의 어떤 요소도 지정된 규칙과 일치하지 않는지 확인합니다.
Optional<T> findFirst()
규칙과 일치하는 첫 번째 요소를 반환합니다.
Optional<T> findAny()
규칙과 일치하는 스트림의 모든 요소를 ​​반환합니다.
Optional<T> min()
데이터 스트림에서 최소 요소를 검색합니다.
Optional<T> max()
데이터 스트림의 최대 요소를 반환합니다.
long count()
데이터 스트림의 요소 수를 반환합니다.
R collect()
스트림에서 모든 데이터를 읽고 컬렉션으로 반환합니다.

Stream2. 클래스 별 중간 및 최종 작업

보시다시피 위 표의 모든 메소드가 Stream. 이는 클래스의 메서드가 중간 ( 비터미널 이라고도 함 ) 메서드와 터미널 메서드 Stream로 나눌 수 있다는 사실과 관련이 있습니다.

중간 방법

중간 메서드는 인터페이스를 구현하는 개체를 반환하며 Stream함께 연결할 수 있습니다.

터미널 방법

터미널 메서드는 a 이외의 값을 반환합니다 Stream.

메서드 호출 파이프라인

따라서 원하는 수의 중간 메서드와 끝에 있는 단일 터미널 메서드 호출로 구성된 스트림 파이프라인을 빌드할 수 있습니다. 이 접근 방식을 사용하면 다소 복잡한 논리를 구현하면서 코드 가독성을 높일 수 있습니다.

메서드 호출 파이프라인

데이터 스트림 내부의 데이터는 전혀 변경되지 않습니다. 중간 메서드 체인은 터미널 메서드가 호출된 후 실행될 데이터 처리 파이프라인을 지정하는 매끄러운(선언적) 방법입니다.

즉, 터미널 메서드가 호출되지 않으면 데이터 스트림의 데이터가 어떤 식으로든 처리되지 않습니다. 터미널 메서드가 호출된 후에야 데이터가 스트림 파이프라인에 지정된 규칙에 따라 처리되기 시작합니다.

stream()
  .intemediateOperation1()
  .intemediateOperation2()
  ...
  .intemediateOperationN()
  .terminalOperation();
파이프라인의 일반적인 모습

중간 및 최종 방법의 비교:

중급 단말기
반환 유형 Stream 아니Stream
동일한 유형의 여러 메서드와 결합하여 파이프라인을 형성할 수 있습니다. 아니요
단일 파이프라인의 메서드 수 어느 하나 이하
최종 결과 생성 아니요
스트림의 데이터 처리를 시작합니다. 아니요

예를 들어 보겠습니다.

동물 애호가를 위한 클럽이 있다고 가정해 보겠습니다. 내일 클럽은 Ginger Cat Day를 기념합니다. 클럽에는 애완 동물 소유자가 있으며 각 소유자에게는 애완 동물 목록이 있습니다. 그들은 고양이에만 국한되지 않습니다.

작업: 내일의 "공휴일"을 위해 개인화 된 인사말 카드를 만들려면 모든 생강 고양이의 이름을 모두 식별해야 합니다. 인사말 카드는 고양이의 나이에 따라 가장 나이가 많은 고양이부터 가장 어린 고양이까지 정렬해야 합니다.

먼저 이 작업을 해결하는 데 도움이 되는 몇 가지 클래스를 제공합니다.

public enum Color {
   WHITE,
   BLACK,
   DARK_GREY,
   LIGHT_GREY,
   FOXY,
   GREEN,
   YELLOW,
   BLUE,
   MAGENTA
}
public abstract class Animal {
   private String name;
   private Color color;
   private int age;

   public Animal(String name, Color color, int age) {
      this.name = name;
      this.color = color;
      this.age = age;
   }

   public String getName() {
      return name;
   }

   public Color getColor() {
      return color;
   }

   public int getAge() {
      return age;
   }
}
public class Cat extends Animal {
   public Cat(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Dog extends Animal {
   public Dog(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Parrot extends Animal {
   public Parrot(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Pig extends Animal {
   public Pig(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Snake extends Animal {
   public Snake(String name, Color color, int age) {
      super(name, color, age);
   }
}
public class Owner {
   private String name;
   private List<Animal> pets = new ArrayList<>();

   public Owner(String name) {
      this.name = name;
   }

   public List<Animal> getPets() {
      return pets;
   }
}

Selector이제 지정된 기준에 따라 선택이 이루어지는 클래스를 살펴보겠습니다 .

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Selector {
   private static List<Owner> owners;

   private static void initData() {
      final Owner owner1 = new Owner("Ronan Turner");
      owner1.getPets().addAll(List.of(
            new Cat("Baron", Color.BLACK, 3),
            new Cat("Sultan", Color.DARK_GREY, 4),
            new Dog("Elsa", Color.WHITE, 0)
      ));

      final Owner owner2 = new Owner("Scarlet Murray");
      owner2.getPets().addAll(List.of(
            new Cat("Ginger", Color.FOXY, 7),
            new Cat("Oscar", Color.FOXY, 5),
            new Parrot("Admiral", Color.BLUE, 3)
      ));

      final Owner owner3 = new Owner("Felicity Mason");
      owner3.getPets().addAll(List.of(
            new Dog("Arnold", Color.FOXY, 3),
            new Pig("Vacuum Cleaner", Color.LIGHT_GREY, 8)
      ));

      final Owner owner4 = new Owner("Mitchell Stone");
      owner4.getPets().addAll(List.of(
            new Snake("Mr. Boa", Color.DARK_GREY, 2)
      ));

      final Owner owner5 = new Owner("Jonathan Snyder");
      owner5.getPets().addAll(List.of(
            new Cat("Fisher", Color.BLACK, 16),
            new Cat("Zorro", Color.FOXY, 14),
            new Cat("Margo", Color.WHITE, 3),
            new Cat("Brawler", Color.DARK_GREY, 1)
      ));

      owners = List.of(owner1, owner2, owner3, owner4, owner5);
   }
}

남은 것은 main메서드에 코드를 추가하는 것입니다. 현재 우리는 먼저 initData()클럽의 애완 동물 소유자 목록을 채우는 메서드를 호출합니다. 그런 다음 나이별로 내림차순으로 정렬된 생강 고양이의 이름을 선택합니다.

먼저 이 작업을 해결하기 위해 스트림을 사용하지 않는 코드를 살펴보겠습니다.

public static void main(String[] args) {
   initData();

   List<String> findNames = new ArrayList<>();
   List<Cat> findCats = new ArrayList<>();
   for (Owner owner : owners) {
      for (Animal pet : owner.getPets()) {
         if (Cat.class.equals(pet.getClass()) && Color.FOXY == pet.getColor()) {
            findCats.add((Cat) pet);
         }
      }
   }

   Collections.sort(findCats, new Comparator<Cat>() {
      public int compare(Cat o1, Cat o2) {
         return o2.getAge() - o1.getAge();
      }
   });

   for (Cat cat : findCats) {
      findNames.add(cat.getName());
   }

   findNames.forEach(System.out::println);
}

이제 대안을 살펴보겠습니다.

public static void main(String[] args) {
   initData();

   final List<String> findNames = owners.stream()
           .flatMap(owner -> owner.getPets().stream())
           .filter(pet -> Cat.class.equals(pet.getClass()))
           .filter(cat -> Color.FOXY == cat.getColor())
           .sorted((o1, o2) -> o2.getAge() - o1.getAge())
           .map(Animal::getName)
           .collect(Collectors.toList());

   findNames.forEach(System.out::println);
}

보시다시피 코드가 훨씬 더 간결합니다. 또한 스트림 파이프라인의 각 줄은 단일 작업이므로 영어 문장처럼 읽을 수 있습니다.

.flatMap(owner -> owner.getPets().stream())
Stream<Owner>a에서 a로 이동Stream<Pet>
.filter(pet -> Cat.class.equals(pet.getClass()))
데이터 스트림에 고양이만 유지
.filter(cat -> Color.FOXY == cat.getColor())
데이터 스트림에 생강 고양이만 유지
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
연령별로 내림차순으로 정렬
.map(Animal::getName)
이름을 얻으십시오
.collect(Collectors.toList())
결과를 목록에 넣기

3. 스트림 생성

클래스 Stream에는 아직 다루지 않은 세 가지 메서드가 있습니다. 이 세 가지 방법의 목적은 새 스레드를 만드는 것입니다.

Stream<T>.of(T obj)방법

이 메서드는 단일 요소로 구성된 스트림을of() 만듭니다 . 예를 들어 함수가 개체를 인수로 사용하지만 개체만 있는 경우 일반적으로 필요합니다. 그런 다음 단일 요소 로 구성된 스트림을 가져오는 방법을 쉽고 간단하게 사용할 수 있습니다 .Stream<T>Tof()

예:

Stream<Integer> stream = Stream.of(1);

Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)방법

메서드 는 전달된 요소 로 구성된 스트림을of() 만듭니다 . 모든 수의 요소가 허용됩니다. 예:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

Stream<T> Stream.generate(Supplier<T> obj)방법

generate()메서드를 사용하면 요청 시 스트림의 다음 요소를 생성하는 데 사용할 규칙을 설정할 수 있습니다. 예를 들어 매번 임의의 숫자를 제공할 수 있습니다 .

예:

Stream<Double> s = Stream.generate(Math::random);

Stream<T> Stream.concat(Stream<T> a, Stream<T> b)방법

이 메서드 concat() 전달된 두 스트림을 하나로 연결합니다 . 데이터를 읽을 때 첫 번째 스트림에서 먼저 읽은 다음 두 번째 스트림에서 읽습니다. 예:

Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.of(10, 11, 12, 13, 14);
Stream<Integer> result = Stream.concat(stream1, stream2);

4. 데이터 필터링

또 다른 6가지 방법은 새로운 데이터 스트림을 생성하여 스트림을 다양한 복잡성의 체인(또는 파이프라인)으로 결합할 수 있습니다.

Stream<T> filter(Predicate<T>)방법

이 메서드는 전달된 규칙 에 따라 소스 데이터 스트림을 필터링하는 새 데이터 스트림을 반환합니다 . 메서드는 형식이 인 개체 에서 호출해야 합니다 .Stream<T>

컴파일러가 개체로 변환하는 람다 함수를 사용하여 필터링 규칙을 지정할 수 있습니다 Predicate<T>.

예:

연결된 스트림 설명
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x < 3));

3보다 작은 숫자만 유지
Stream<Integer> stream = Stream.of(1, -2, 3, -4, 5);
Stream<Integer> stream2 = stream.filter(x -> (x > 0));

0보다 큰 숫자만 유지

Stream<T> sorted(Comparator<T>)방법

이 메서드는 소스 스트림 의 데이터를 정렬하는 새 데이터 스트림을 반환합니다 . 데이터 스트림의 두 요소를 비교하기 위한 규칙을 설정하는 비교기를 전달합니다 .

Stream<T> distinct()방법

이 메서드는 원본 데이터 스트림고유한 요소만 포함하는 새 데이터 스트림을 반환합니다 . 모든 중복 데이터는 삭제됩니다. 예:

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.distinct(); // 1, 2, 3, 4, 5

Stream<T> peek(Consumer<T>)방법

이 메서드는 새 데이터 스트림을 반환 하지만 그 안에 있는 데이터는 소스 스트림과 동일합니다. 그러나 다음 요소가 스트림에서 요청되면 메서드 에 전달한 함수가peek() 함께 호출됩니다.

System.out::println메서드 에 함수를 전달하면 peek()스트림을 통과할 때 모든 개체가 표시됩니다.

Stream<T> limit(int n)방법

이 메서드는 소스 데이터 스트림 의 첫 번째 요소 포함하는 새 데이터 스트림을 반환합니다 . 다른 모든 데이터는 폐기됩니다. 예:n

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3

Stream<T> skip(int n)방법

이 메소드는 소스 스트림 과 동일한 모든 요소를 ​​포함 하지만 첫 번째 요소를 건너뛰는 (무시) 새 데이터 스트림을 반환합니다 . 예:n

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.skip(3); // 4, 5, 2, 2, 2, 3, 4