스트림 (Stream)

KIHYUK MOON·2023년 2월 8일
0
post-thumbnail
post-custom-banner

스트림을 활용하면 배열, 컬렉션 등의 자료를 일관성 있게 처리할 수 있다. 자료에 따라 기능을 새로 구현하는 것이 아니라, 처리해야 하는 자료가 무엇인지와 상관없이 같은 방식으로 메소드를 호출할 수 있다.

  • 컬렉션(배열 포함) 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 반복자
  • 다양한 데이터 소스를 표준화된 방법으로 다루기 위함

스트림 API 특징

  1. 자료의 대상과 관계없이 동일한 연산을 수행
  2. 외부 반복을 통해 작업하는 컬렉션과는 달리 내부 반복(internal iteration)을 통해 작업을 수행
  3. 재사용이 가능한 컬렉션과는 달리 단 한 번만 사용할 수 있다
  4. 원본 데이터를 변경하지 않습니다.
  5. 스트림의 연산은 필터-맵(filter-map) 기반의 API를 사용하여 지연(lazy) 연산을 통해 성능을 최적화한다
  6. parallelStream() 메소드를 통한 손쉬운 병렬 처리를 지원

스트림 생성, 중간연산, 최종연산

Integer[] arr = {1,2,3,4,5,6,7,8,9,10};
List<Integer> sList = new ArrayList<>(Arrays.asList(arr));
//스트림생성, 중간연산, 최종연산
sList.stream().filter(s->s.intValue() >= 5).forEach(s -> System.out.println(s));

배열과 스트림 비교

int[] arr = {1,2,3,4,5};
for(int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}
// 스트림 생성, 요소를 하나씩 꺼내서 출력하는 기능
Arrays.stream(arr).forEach(n -> System.out.println(n));

반복자 스트림

  • forEach() : 스트림 요소에 대한 순차접근 제공 (최종연산)
public static void main(String[]args) {
    // asList() : 일반 배열을 ArrayList 로 변경 한다.
    ArrayList<String> list = new ArrayList<>(Arrays.asList("고유림", "나희도", "백이진"));

    // Iterator 이용
    Iterator<String> iterator = list.iterator();
    while(iterator.hasNext()) {
        String name = iterator.next();
        System.out.println(name);
    }
    System.out.println();

    // Stream 이용
    Stream<String> stream = list.stream();
    stream.forEach(name -> System.out.println(name)); // 람다식
}

내부 반복자를 사용하므로 병렬 처리가 쉽다

 public static void main(String[] args) {
        List<String> list = Arrays.asList(
          "나희도", "고유림", "백이진", "구자경", "우영우"
        );
        // 순차 처리
        Stream<String> stream = list.stream();
        stream.forEach(Main :: print);
        System.out.println();

        // 병렬 처리
        Stream<String> parallelStream = list.parallelStream();
        parallelStream.forEach(Main :: print);
        System.out.println();
    }

    public static void print(String str) {
        System.out.println(str + " : " + Thread.currentThread().getName());
    }

중간처리와 최종처리를 할 수 있다
스트림은 컬렉션의 요소에 대해 중간 처리와 최종 처리를 수행 할 수 있는데, 중간 처리에서는 매핑, 필터링, 정렬을 수행하고 최종 처리에서는 반복, 카운팅, 평균, 총합 등의 집계 처리를 수행한다

public static void main(String[] args) {
        List<Student> stList = Arrays.asList(
                new Student("우영우", 99),
                new Student("최수연", 96),
                new Student("권민우", 92)
        );
        double avg = stList.stream()
                // 중간처리 (스트림을 IntStream으로 변환)
                .mapToInt(Student::getScore) // 메소드 참조
                // 최종 처리
                .average()
                .getAsDouble();

        System.out.println("평균 점수 : " + avg);
    }

컬렉션으로 부터 스트림

자바에서 제공하는 모든 컬렉션의 최고 상위 조상인 Collection 인터페이스에는 stream() 메소드 정의 되어 있습니다.

List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> stream = list.stream();
stream.forEach(System.out::println);

배열로 부터 스트림

배열에 관한 스트림을 생성하기 위해 Arrays 클래스에는 다양한 형태의 stream() 메소드가 클래스 메소드로 정의 되어 있습니다.

public static void main(String[] args) {
    String[] arr = {"고유림", "나희도", "백이진", "구자경", "우영우"};
    // 배열에서 스트림 생성
    Stream<String> stream1 = Arrays.stream(arr);
    stream1.forEach(e -> System.out.print(e + " "));
    System.out.println();

    // 배열의 특정 부분만을 이용한 스트림 생성
    Stream<String> stream2 = Arrays.stream(arr, 1, 3);
    stream2.forEach(e -> System.out.print(e + " "));
    System.out.println();
}

숫자 범위로 부터 스트림

1부터 100까지의 합을 구하기 위해 IntStream의 rangeClosed() 메소드를 이용

public static void main(String[] args) {
    IntStream stream = IntStream.rangeClosed(1, 100);
    stream.forEach(a -> sum += a);
    System.out.println("총합 : " + sum);
}

파일로부터 스트림 얻기

Files의 정적 메소드인 lines()와 BufferedReader의 Line() 메소드를 이용하여 문자 파일의 내용을 스트림을 통해 행 단위로 읽고 콘솔에 출력 합니다.

public static void main(String[] args) throws IOException {
    // 파일 내용을 소스로 하는 스트림
    Path path = Paths.get("src/스트림파일/list.txt");
    Stream<String> stream = Files.lines(path, Charset.defaultCharset());
    stream.forEach(System.out::println);
    System.out.println();
    
    // BufferedReader의 Line() 메소드 이용
    File file = path.toFile();
    FileReader fileReader = new FileReader(file);
    BufferedReader br = new BufferedReader(fileReader);
    stream = br.lines();
    stream.forEach(System.out::println);
}

디렉토리로 부터 스트림 얻기

Files의 정적 메소드인 list()를 이용해서 디렉토리의 내용을 스트림을 통해 읽고 콘솔로 출력 합니다.

public static void main(String[] args) throws IOException {
    Path path = Paths.get("src");
    Stream<Path> stream = Files.list(path);
    stream.forEach(p->System.out.println(p.getFileName()));
}

요소의 통계

count() 메소드는 해당 스트림의 요소의 총 개수를 long 타입의 값으로 반환합니다.

또한, max()와 min() 메소드를 사용하면 해당 스트림의 요소 중에서 가장 큰 값과 가장 작은 값을 가지는 요소를 참조하는 Optional 객체를 얻을 수 있습니다.

IntStream stream1 = IntStream.of(30, 90, 70, 10);
IntStream stream2 = IntStream.of(30, 90, 70, 10);
System.out.println(stream1.count());
System.out.println(stream2.max().getAsInt());

요소의 연산

IntStream이나 DoubleStream과 같은 기본 타입 스트림에는 해당 스트림의 모든 요소에 대해 합과 평균을 구할 수 있는 sum()과 average() 메소드가 각각 정의되어 있습니다.

이때 average() 메소드는 각 기본 타입으로 래핑 된 Optional 객체를 반환합니다.

IntStream stream1 = IntStream.of(30, 90, 70, 10);
DoubleStream stream2 = DoubleStream.of(30.3, 90.9, 70.7, 10.1);

System.out.println(stream1.sum());
System.out.println(stream2.average().getAsDouble());
profile
개발자 전직중..
post-custom-banner

0개의 댓글