[Java 8] 10. Streams API

seony·2023년 4월 27일

java8

목록 보기
10/16

Stream이란

  • 주 목적은 Collections에서의 연산을 위한 것이다.
  • Stream API를 사용하면 스레드를 따로 생성하지 않고 쉽게 병렬처리를 할 수 있다.
  • Stream은 컬렉션 즉, List, Arrays 또는 I/O 자원에서 순차적으로 요소를 나열한 것이다.
  • parallelStream()을 사용하면 순차적이 아닌 병렬적으로 처리할 수도 있다.

<Stream API 연산>

Collections VS Streams

Collection

  • 추가, 수정이 어느 시점에서나 가능하다.
    • List -> list.add(요소)
  • element가 순서에 상관없이 접근이 가능하다.
  • 열심히(eagerly) 만들어진다.
  • 여러 번 순회할 수 있다.
  • 외부 반복을 통해 elements를 탐색한다.

Streams

  • 추가, 수정이 불가능하다. 고정된 데이터 셋이기 때문
  • 순차적인 접근만 가능하다.
  • 게으르게(lazily) 만들어진다.
  • 한 번만 순회할 수 있다.
  • 내부 반복을 통해 elements를 탐색한다.

<비교 예시>

package com.learn.java.streams;

import java.util.ArrayList;
import java.util.stream.Stream;

public class CollectionsVsStream {
    public static void main(String[] args) {

        ArrayList<String> names = new ArrayList<>();
        names.add("adam");
        names.add("jim");
        names.add("jenny");

        // 여러 번 순회 가능
        for (String name : names) {
            System.out.println(name);
        }

        for (String name : names) {
            System.out.println(name);
        }

        names.remove(0);
        System.out.println(names);

        Stream<String> nameStream = names.stream();
        nameStream.forEach(System.out::println);
        nameStream.forEach(System.out::println); // 에러 남
    }
}

Map()

  • 타입을 바꾸는데 사용한다.
package com.learn.java.streams;

import com.learn.java.data.StudentDataBase;

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

public class StreamsMapExample {

    public static List<String> namesList() {
        return StudentDataBase.getAllStudents().stream()
                // Student as an input -> Student Name
                .map(student -> student.getName())
                .map(String::toUpperCase)
                .collect(Collectors.toList()); // List<String>
    }


    public static Set<String> namesSet() {
        return StudentDataBase.getAllStudents().stream()
                // Student as an input -> Student Name
                .map(student -> student.getName())
                .map(String::toUpperCase) // Stream<String> -> uppercase operation on each input
                .collect(Collectors.toSet()); // List<String>
    }

    public static void main(String[] args) {
        System.out.println(namesList());
        System.out.println(namesSet());
    }
}

flatMap(), distinct(), count()

  • map과 마찬가지로 다른 타입으로 바꾸는 역할을 한다.
  • flatMap은 주로 중첩된 데이터 구조를 평탄화하여 하나의 스트림으로 변환하는 데 사용한다.

package com.learn.java.streams;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class StreamsFlatMapExample {

    public static List<String> printStudentActivities() {
         return StudentDataBase.getAllStudents().stream()
                .map(Student::getActivities)
                 .flatMap(List::stream)
                 .distinct() // 중복 제거
                 .sorted() // 순서 정렬
                .collect(toList());
    }

    public static Long getStudentActivitiesCount() {
        return StudentDataBase.getAllStudents().stream()
                .map(Student::getActivities)
                .flatMap(List::stream)
                .distinct() // with distinct functiokn performed
                .count();
    }


    public static void main(String[] args) {
        System.out.println("printStudentActivities : " + printStudentActivities());
        System.out.println("getStudentActivitiesCount : " + getStudentActivitiesCount());
    }
}

Comparator를 사용한 sorted()

package com.learn.java.streams;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StreamsComparatorExample {

	// 이름으로 정렬
    public static List<Student> sortStudentsByName() {

        return StudentDataBase.getAllStudents().stream()
                .sorted(Comparator.comparing(Student::getName))
                .collect(Collectors.toList());
    }

    // GPA 오름차순으로 정렬 (기본)
    public static List<Student> sortStudentByGpa() {

        return StudentDataBase.getAllStudents().stream()
                .sorted(Comparator.comparing(Student::getGpa))
                .collect(Collectors.toList());
    }

	// GPA 내림차순으로 정렬
    public static List<Student> sortStudentByGpaDesc() {

        return StudentDataBase.getAllStudents().stream()
                .sorted(Comparator.comparing(Student::getGpa).reversed())
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        System.out.println("Students sorted by Name : ");
        sortStudentsByName().forEach(System.out::println);

        System.out.println("Students sorted by GPA : ");
        sortStudentByGpa().forEach(System.out::println);

        System.out.println("Students sorted by GPA DESC: ");
        sortStudentByGpaDesc().forEach(System.out::println);
    }
}

filter()

package com.learn.java.streams;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.List;
import java.util.stream.Collectors;

public class StreamsFilterExample {

    public static List<Student> filterStudents() {
        return StudentDataBase.getAllStudents().stream() // Stream<Students>
                .filter((student -> student.getGender().equals("female"))) // Stream<Students>
                // filters and sends only the students whose gender is female
                .filter(student -> student.getGpa() >= 3.9)
                .collect(Collectors.toList());
    }

    public static void main(String[] args) {
        filterStudents().forEach(System.out::println);
    }
}

reduce()

  • 두 개의 파라미터를 인풋으로 받음
    • First Parameter: 디폴트값 or 초깃값
    • Second Parameter: BinaryOperator<T>
  • 초깃값을 주지 않으면 Optional
package com.learn.java.streams;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class StreamReduceExample {
	
    // 곱셈 연산하기
    public static int performMultiplication(List<Integer> integerList) {
        return integerList.stream()
                // 1, 3, 5, 7
                // a = 1, b = 1 (from stream) => result 1 is returned
                // a = 1, b = 3 (from stream) => result 3 is returned
                // a = 3, b = 5 (from stream) => result 15 is returned
                // a = 15, b = 7 (from stream) => result 105 is returned
                // 105
                .reduce(1, (a, b) -> a * b);
    }

	// identity 없이 곱셈 연산하기
    public static Optional<Integer> performMultiplicationWithoutIdentity(List<Integer> integerList) {
        return integerList.stream()
                // 1, 3, 5, 7
                .reduce((a, b) -> a * b);
    }

    public static Optional<Student> getHighestGPAStudent() {
        return StudentDataBase.getAllStudents().stream()
                // students one by one
                .reduce((s1, s2) -> s1.getGpa() > s2.getGpa() ? s1 : s2);
    }

    public static void main(String[] args) {

        List<Integer> integers = Arrays.asList(1, 3, 5, 7);
        System.out.println(performMultiplication(integers));

        // 배열 값이 제대로 들어 있을 경우
        Optional<Integer> result = performMultiplicationWithoutIdentity(integers);

        System.out.println(result.isPresent());
        System.out.println(result.get());

        // 빈 배열 넘겨줬을 경우
        Optional <Integer> result1 = performMultiplicationWithoutIdentity(new ArrayList<>());
        if (result1.isPresent()) {
            System.out.println(result1.get());
        }

        Optional<Student> studentOptional = getHighestGPAStudent();
        if (studentOptional.isPresent()) {
            System.out.println(studentOptional.get());
        }

    }
}

reduce()를 사용하여 min, max 구하기

package com.learn.java.streams;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class StreamsMinMaxExample {

    public static int findMaxValue(List<Integer> integerList) {
        return integerList.stream()
                // 6 -> y
                // 7 -> y
                // 8 -> y
                // 9 -> y
                // 10 -> y
                // x variable holds the max value for each element in the iteration
                .reduce(0, (x, y) -> x > y ? x : y);
    }

    public static Optional<Integer> findMinValue(List<Integer> integerList) {
        return integerList.stream()
                // 6 -> y
                // 7 -> y
                // 8 -> y
                // 9 -> y
                // 10 -> y
                // x variable holds the min value for each element in the iteration
                .reduce((x, y) -> x < y ? x : y);
    }


    // identity를 안주면 Optional 로 return 함
    public static Optional<Integer> findMaxValueOptional(List<Integer> integerList) {
        return integerList.stream()
                // 6 -> y
                // 7 -> y
                // 8 -> y
                // 9 -> y
                // 10 -> y
                // x variable holds the max value for each element in the iteration
                .reduce((x, y) -> x > y ? x : y);
    }

    public static void main(String[] args) {

        List<Integer> integerList = Arrays.asList(6, 7, 8, 9, 10);
        List<Integer> emptyList = new ArrayList<>();

        int maxValue = findMaxValue(integerList);
        System.out.println("max value is : " + maxValue);

        // 빈 리스트이기 때문에 identity 값으로 0이 return됨
        // 원래는 값이 나오면 안됨
        System.out.println("max value is : " + findMaxValue(emptyList));

		// ===================Optional로 max 값 구하기 ==================
        Optional<Integer> maxValueOptional = findMaxValueOptional(integerList);
        System.out.println("Optional Max is : " + maxValueOptional);
        if (maxValueOptional.isPresent()) {
            System.out.println("MaxValue using optional : " + maxValueOptional.get() );
        } else {
            System.out.println("Input list is empty.");
        }

        /**
         * ========================min 예시=======================================
         */

        Optional<Integer> minValueOptional = findMinValue(emptyList); // 여기에 integerList 나 emptyList 넣어보며 테스트 가능
        System.out.println("minValueOptional : " + minValueOptional);

        if (minValueOptional.isPresent()) {
            System.out.println("The minimum value is : " + minValueOptional.get());
        } else {
            System.out.println("No Input is passed");
        }
    }
}

limit() 과 skip()

  • limit(n) : n 개의 요소만
  • skip(n) : n 개 요소 skip하고 그 이후의 요소만
package com.learn.java.streams;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class StreamsLimitSkipExample {

    // limit
    public static Optional<Integer> limit(List<Integer> integers) {
        return integers.stream()
                .limit(3) // 6, 7, 8
                .reduce(Integer::sum);
    }

    // skip
    public static Optional<Integer> skip(List<Integer> integers) {
        return integers.stream()
                .skip(3) // 9, 10
                .reduce(Integer::sum);
    }


    public static void main(String[] args) {

        List<Integer> integers = Arrays.asList(6, 7, 8, 9, 10);

        Optional<Integer> limitResult = limit(integers);
        if (limitResult.isPresent()) {
            System.out.println("The limit result is : " + limitResult.get());
        } else {
            System.out.println("No input is passed");
        }


        Optional<Integer> skipResult = skip(integers);
        if (skipResult.isPresent()) {
            System.out.println("The skip result is : " + skipResult.get());
        } else {
            System.out.println("No input is passed");
        }


    }
}

anyMatch(), allMatch(), noneMatch()

Predicate를 인자로 받고 Boolean을 return 한다.

  • anyMatch(): 하나라도 일차하면 true
  • allMatch(): 전부 일치하면 true
  • noneMatch(): 전부 일치하지 않으면 true

findFirst(), findAny()

둘 다 Optional을 return 한다.

  • 병렬처리를 할 때 그 차이를 느낄 수 있다.
package com.learn.java.streams;

import com.learn.java.data.Student;
import com.learn.java.data.StudentDataBase;

import java.util.Optional;

public class StreamFindAnyFirstExample {

    public static Optional<Student> findAnyStudent() {
        return StudentDataBase.getAllStudents().stream()
                // adam
                // jenny
                // emily
                .filter(student -> student.getGpa() >= 3.9)
                .findAny();
    }

    public static Optional<Student> findFirstStudent() {
        return StudentDataBase.getAllStudents().stream()
                // adam
                // jenny
                // emily
                .filter(student -> student.getGpa() >= 4.1)
                .findFirst();
    }
    
    public static void main(String[] args) {

		// findAny
        Optional<Student> studentOptionalFindAny = findAnyStudent();
        if (studentOptionalFindAny.isPresent()) {
            System.out.println("Found the student : " + studentOptionalFindAny.get());
        } else {
            System.out.println("Student Not Found ! ");
        }


        // findFirst
        Optional<Student> studentOptionalFindFirst = findFirstStudent();
        if (studentOptionalFindFirst.isPresent()) {
            System.out.println("Found the student : " + studentOptionalFindFirst.get());
        } else {
            System.out.println("Student Not Found ! ");
        }
    }
}

0개의 댓글