1.12 Stream

yeonseong Jo·2023년 5월 3일
0

SEB_BE_45

목록 보기
19/47
post-thumbnail

누군가 과제로 제출한 제트스트림 광고

오늘은
Annotation, Lambda, Stream에 대해 공부하였다.
Annotation과 lambda는
python 특히 django를 사용하면서 많이 써봤기에
익숙한 편이었다.
(python에선 annotation이 아니라 decorator)

하지만, Stream에 대해선
처음 들어보았기에 공부를 좀 더 하였다.
(하지만 이 조차도 이미 python에 있는..)


Stream

Stream은 lambda식을 활용해서
데이터를 쉽고 빠르게 처리할 수 있도록 하는 반복자이다.


특징


Stream은 배열, 리스트, 맵 등의 자료형으로 부터
생성 한 이후 중간 연산과 최종 연산을 거쳐 완성된다.
중간 연산에는 filter, distinct, sorted, map 등이 있고,
최종 연산에는 foreach, **Match, collect 등이 있다.


장점

Stream에 있는 중간 연산, 최종 연산 모두
반복문을 통해 구현이 가능하긴 하다.
그럼에도 불구하고 Stream을 쓰는 이유가 따로 있다.

public sumWithIterator(List<Integer> list){
    
    Iterator<Integer> iter = list.iterator();
    int sum = 0;

    while (it.hasNext()){
        int num = it.next();
        sum += num;
    }
}
public sumWithStream(List<Integer> list){
	Stream<Integer> st = list.stream();
    int sum = st.sum()
}

위의 두 method를 보면 한눈에 알 수 있듯이,
일단 직관적이다.
이는 또 코드를 짜는데 쉽다는 것이다.

그리고 이보다 더 큰 장점이 있는데,
바로 메모리를 덜 쓴다는 것이다.

위의 코드를 예시로,
list변수는 별개로 두고,
iterator로 iter변수를 선언 할 때
stack영역에 iter의 주소값을,
heap영역에 iter의 데이터를 저장하는 반면,

stream을 사용하면,
Stream으로 st변수를 선언하지만,
사실 따로 메모리 할당을 하지는 않고,
list변수를 가지고 실행한다.

이는 stream을 사용할 때
lambda식을 사용하기 때문인거 같다.

강의 때 그림을 그리시면서 까지 설명을 해 주셨는데,

실제로 stream을 쓸 때와 안 쓸 때를 비교하자면
메모리 사용량이 4배 정도 차이가 난다고 한다.


Stream 생성

Stream 생성에도 여러 방식이 존재하는데,
숫자형이 아닌 String 등의 배열, 리스트일 때,

// String Array
String[] strArr = {...};

Stream<String> strSt1 = Arrays.stream(strArr);
Stream<String> strSt2 = Stream.of(strArr);

// String ArrayList
List<String> strList = new ArrayList<>(Arrays.asList(strArr));

Stream<String> strSt3 = strList.stream();
Stream<String> strSt4 = Stream.of(strList);

Stream.of는 배열, 리스트 상관없이 공통적으로 사용 가능하고,
배열의 경우 Arrays.stream( )을 이용,
리스트의 경우 listInstance.stream( )을 이용한다.

double, 몇몇 정수형의 경우
따로 관련 Stream이 존재한다.

DoubleStream
IntStream
LongStream

reduce

reduce는 최종 연산 기능 중 하나이다.
스트림에 있는 요소를 하나씩 줄여가면서 연산을 수행하는데,
강의자료에서 있던 예시로는 이걸 왜 사용하는지 이해가 안갔었다.

int[] intArr = {1,2,3,4,5};

int sum1 = Arrays.stream(intArr).sum();
// 15
int sum2 = Arrays.stream(intArr).reduce((a, b) -> a+b).getAsInt();
// 15

(초기값 설정이 가능하다고 하지만, 그 값을 sum1에 더해주면 그만)

그래서 물어봤다.

나:
java의 stream에 이미 max, min, average, sum등의 기능이 존재하는데, reduce를 왜 쓰지?

GPT:
Stream API에서 제공하는 max, min, average, sum과
같은 메서드는 스트림에서 각각 최댓값, 최솟값, 평균값,
합계를 계산하는 데 유용한 기능을 제공합니다.
이러한 메서드를 사용하면 코드가 간결해지고,
가독성이 향상됩니다.

하지만 때로는 위 메서드들만으로는 원하는 결과를
얻을 수 없는 경우가 있습니다. 예를 들어,
max 메서드를 사용하여 최대값을 찾을 때,
최대값을 가진 요소의 위치를 찾을 수는 없습니다.
마찬가지로, average 메서드를 사용하여 평균값을
계산할 때, 계산된 값을 가지고 추가적인 작업을
수행할 수 없습니다.

예시도 있다.

public class Student {
    private String name;
    private int score;

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

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }
}
List<Student> students = Arrays.asList(
        new Student("Alice", 85),
        new Student("Bob", 90),
        new Student("Charlie", 80),
        new Student("Dave", 95)
);

Optional<Student> topStudent = students.stream()
        .max(Comparator.comparingInt(Student::getScore));
        
Optional<String> topStudentName = students.stream()
        .reduce((s1, s2) -> s1.getScore() > s2.getScore() ? s1 : s2)
        .map(Student::getName);

stream 사용 부분을 보면
max를 사용할 때에는 students 리스트에서 점수의 최대값만 보여줄 뿐, 해당 학생의 이름은 알 수 없다.

하지만, reduce를 사용하면, 후에 map을 추가하여
해당 학생의 이름을 반환한다.


후기

Stream에 대해 공부하고 나니
중간 연산은 다 python에도 있는 기능이었고,
사용해봤던 기억이 났다.
특히 코테를 준비할 때 많이 사용했었는데,
이 Stream또한 코테 때 많이 사용할 거 같다.

profile
뒤(back)끝(end)있는 개발자

0개의 댓글