스트림에 대해

SionBackEnd·2022년 7월 19일
0

자바 스터디

목록 보기
14/21
post-thumbnail

스트림

다양한 데이터소스(컬렉션, 배열)를 표준화된 방법으로 다루기 위한 것
스트림 이전에는 데이터 소스의 사용방법이 달라서 각각 처리해주어야하지만, 스트림을 이용하면 스트림 방식으로 처리가능

단계

  1. 스트림 만들기 (1번)
  2. 중간 연산 (n번)
  3. 최종 연산 (1번)
stream.distinct().limit(5).sorted().forEach(System::println)
(스트림 만들기)           (중간 연산)           (최종 연산)

특징

  1. 스트림은 데이터 소스로부터 데이터를 읽기만 하고 변경하지 않음.
  2. 스트림은 Iterator처럼 일회용.(필요하면 다시 스트림을 생성해야 함)
  3. 최종 연산 전까지 중간연산이 수행되지 않음.
  4. 스트림 작업을 내부 반복으로 처리함
(strea.forEach(System.out::println);
// 풀어서 쓰면
for(String str : strList)
	System.out.println(str);
  1. 스트림의 작업을 병렬로 처리 -병렬스트림
  2. 기본형 스트림 -intStream, LongStream,DoubleStream ...
    6-1. 덕분에 오토박싱 & 언박싱의 비효울이 제거됨.
    6-2. 기본형 스트림은 숫자와 관련된 유용한 메서드를 Stream<T> 보다 더 많이 제공함.

스트림 생성

컬렉션 객체

// List로부터 스트림을 생성
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> listStream = list.stream();
listStream.forEach(System.out::prinln); //스트림의 모든 요소를 출력.

배열 객체

// 배열로부터 스트림을 생성
Stream<String> stream = Stream.of("a", "b", "c"); //가변인자
Stream<String> stream = Stream.of(new String[] {"a", "b", "c"});
Stream<String> stream = Arrays.stream(new String[] {"a", "b", "c"});
Stream<String> stream = Arrays.stream(new String[] {"a", "b", "c"}, 0, 3); //end 범위 미포함

원시 자료형

IntStream stream = IntStream.range(4, 10);
IntStream intArrStream = Arrays.stream(new int[] {1,2,3,4,5})

정리

업로드중..

중간연산 메소드

필터링[ filter(), distinct() ]

  • distinct() : Stream의 요소들에 중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용.
  • filter() : Stream에서 조건에 맞는 데이터만을 정제하여 더 작은 컬렉션을 만들어냄. filter() 메서드에는 매개값으로 조건(Predicate)이 주어지고, 조건이 참이 되는 요소만 필터링함.
import java.util.Arrays;
import java.util.List;


class Main {
    public static void main(String[] args) {
        List<String> language = Arrays.asList("자바", "파이썬", "스위프트", "코틀린", "자바", "스위프트");

        language.stream()
                .distinct()
                .forEach(n -> System.out.println(n));
        System.out.println();

        language.stream()
                .filter(n -> n.startsWith("자"))
                .forEach(System.out::println);
    }
}

매핑[ map() ]

  • map() : map은 기존의 Stream 요소들을 대체하는 요소로 구성된 새로운 Stream을 형성하는 연산이다.
List<String> names = Arrays.asList("kimcoding", "javalee", "ingikim", "kimcoding");
    names.stream()
				 .map(s -> s.toUpperCase())
				 .forEach(n->System.out.println(n));

mapToInt(), mapToLong(), mapToDouble() 등등

map() 메서드는 작업을 하다 보면 일반적인 Stream 객체를 원시 Stream으로 바꾸거나 그 반대로 하는 작업이 필요한 경우에 쓰인다. 반대로 원시 객체는 mapToObject를 통해 일반적인 Stream 객체로 바꿀 수 있다.

정렬[sorted() ]

Stream의 요소들을 정렬하기 위해서는 sorted를 사용해야 하며, 파라미터로 Comparator를 넘길 수도 있음.
Comparator 인자 없이 호출할 경우에는 오름차순으로 정렬이 되며, 내림차순으로 정렬하기 위해서는 Comparator의 reverseOrder를 이용함.

Comparable을 기본적으로 구현한 클래스가 아니라면 comparing()메소드를 사용해서 비교할수 있음

최종연산

최종연산은 한번만 연산이 가능함

연산 결과 확인 [ forEach() ]

forEach는 최종 연산 메서드이기 때문에 파이프라인 마지막에서 요소를 하나씩 연산.

forEach값을 출력할 때도 사용하지만, 이메일 발송, 스케줄링 등 리턴 값이 없는 작업에서도 많이 사용함.

intStream
	.filter(a -> a%2 ==0)
	.forEach(n -> System.out.println(n));

매칭 [ match() ]

Stream의 요소들이 특정한 조건을 충족하는지 검사하고 싶은 경우에는 match() 메서드를 이용하고 검사 결과를 boolean으로 반환함.

종류

  • allMatch() : 모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하는지 조사
  • anyMatch() : 최소한 한 개의 요소가 매개값으로 주어진 Predicate의 조건을 만족하는지 조사
  • noneMatch() : 모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하지 않는지 조사
public class MatchesExample {
    public static void main(String[] args) throws Exception {
        int[] intArr = {2,4,6};
        boolean result = Arrays.stream(intArr).allMatch(a->a%2==0);
        System.out.println("모두 2의 배수인가? " + result);

        result = Arrays.stream(intArr).anyMatch(a->a%3==0);
        System.out.println("하나라도 3의 배수가 있는가? " + result);

        result = Arrays.stream(intArr).noneMatch(a->a%3==0);
        System.out.println("3의 배수가 없는가? " + result);
    }
}

/*
모두 2의 배수인가? true
하나라도 3의 배수가 있는가? true
3의 배수가 없는가? false
*/

기본 집계(sum(), count(), average(), max(), min())

집계는 최종 연산 기능으로 요소들을 카운팅, 합계, 평균값, 최대값, 최소값 등으로 연산하여 하나의 값으로 산출하는 것을 의미. 기본 집계의 리턴타입은 optional< T > 이기 때문에 타입을 getAsInt,getAsDouble 등등으로 변환해 주어야 함.

  public class AggregateExample {
    public static void main(String[] args) throws Exception {
        int[] intArr = {1,2,3,4,5};

        long count = Arrays.stream(intArr).count();
        System.out.println("intArr의 전체 요소 개수 " + count);

        long sum = Arrays.stream(intArr).sum();
        System.out.println("intArr의 전체 요소 합 " + sum);

        double avg = Arrays.stream(intArr).average().getAsDouble();
        System.out.println("전체 요소의 평균값 " + avg);

        int max = Arrays.stream(intArr).max().getAsInt();
        System.out.println("최대값 " + max);

        int min = Arrays.stream(intArr).min().getAsInt();
        System.out.println("최소값 " + min);

        int first = Arrays.stream(intArr).findFirst().getAsInt();
        System.out.println("배열의 첫번째 요소 " + first);

    }
}

/*
intArr의 전체 요소 개수 5
intArr의 전체 요소 합 15
전체 요소의 평균값 3.0
최대값 5
최소값 1
배열의 첫번째 요소 1
*/

reduce()

앞서 본 집계 메서드인 sum, average, count, max, min 외에도 다양한 집계 결과물을 만들 수 있는 reduce 메서드가 있다. 앞서 살펴본 count()와 sum() 등 집계 메서드는 내부적으로 모두 reduce()를 사용한다.

reduce는 누적하여 하나로 응축(reduce)하는 방식으로 동작한다. 앞의 두 요소의 연산 결과를 바탕으로 다음 요소와 연산함.

종류

Accumulator: 각 요소를 계산한 중간 결과를 생성하기 위해 사용
Identity: 계산을 수행하기 위한 초기값
Combiner: 병렬 스트림(Parlallel Stream)에서 나누어 계산된 결과를 하나로 합치기 위한 로직

public class ReduceExample {
    public static void main(String[] args) throws Exception {
        int[] intArr = {1,2,3,4,5};

        long sum = Arrays.stream(intArr).sum();
        System.out.println("intArr의 전체 요소 합 " + sum);

        int sum1 = Arrays.stream(intArr)
                        .map(el -> el*2)
                        .reduce((a,b) -> a+b)
                        .getAsInt();
        System.out.println("초기값 없는 reduce " + sum1);

        int sum2= Arrays.stream(intArr)
                        .map(el -> el*2)
                        .reduce(0, (a,b) -> a+b);
        System.out.println("초기값 존재하는 reduce " + sum2)
    }
}

/*
intArr의 전체 요소 합 15
초기값 없는 reduce 30
초기값 존재하는 reduce 30
*/

collect()

Stream의 요소들을 List나 Set, Map, 등 다른 종류의 결과로 수집하고 싶은 경우에는 collect 메서드를 사용한다. collect 메서드는 어떻게 Stream의 요소들을 수집할 것인가를 정의한 Collector 타입을 인자로 받고, 이는 Collector 인터페이스를 구현한 클래스이다.

일반적으로 List로 Stream의 요소들을 수집하는 경우가 많다. 자주 사용하는 작업은 Collectors 객체에서 static 메서드로 제공하고 있고, 원하는 것이 없는 경우에는 Collector 인터페이스를 직접 구현하여 사용할 수도 있다.

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

class Student {
    public enum Gender {Male, Female};
    private String name;
    private int score;
    private Gender gender;

    public Student(String name, int score, Gender gender) {
        this.name = name;
        this.score = score;
        this.gender = gender;
    }

    public Gender getGender(){
        return gender;
    }

    public String getName(){
        return name;
    }

    public int getScore(){
        return score;
    }
}
public class CollectExample {
    public static void main(String[] args) throws Exception {
        List<Student> totalList = Arrays.asList(
            new Student("김코딩", 10, Student.Gender.Male),
            new Student("김인기", 8, Student.Gender.Male),
            new Student("이자바", 9, Student.Gender.Female),
            new Student("최민선", 10, Student.Gender.Female)
        );

        List<Student> maleList = totalList.stream()
        .filter(s -> s.getGender() == Student.Gender.Male)
        .collect(Collectors.toList());

        maleList.stream().forEach(n->System.out.println(n.getName()));

        Set<Student> femaleSet = totalList.stream()
        .filter(s -> s.getGender() == Student.Gender.Female)
        .collect(Collectors.toCollection(HashSet :: new));

        femaleSet.stream().forEach(n->System.out.println(n.getName()));
    }
}

profile
많은 도움 얻어가시길 바랍니다!

0개의 댓글