JAVA/JAVA8

Stream Operations

최-코드 2024. 8. 23. 17:15

map() : 기본적으로 스트림의 데이터를 가공해서 다른 데이터로 만드는 역할이다. 타입을 변환시킬 수도 있다.

 

FlatMap() : map()과 유사하지만, 각 요소에 들어있는 값이 컬렉션이 될 때 이들을 평탄화하는데에 사용된다. 모든 타입을 반환하는 map과 달리 stream 타입만을 리턴해야 한다.

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

private static List<String> getStudentActivities() {
    return StudentDataBase.getAllStudents().stream()
            .map(Student::getActivities)
            .flatMap(List::stream)
            .collect(Collectors.toList());
}
//출력 : [swimming, basketball, volleyball, swimming, gymnastics, soccer, swimming, 
//gymnastics, aerobics, swimming, gymnastics, soccer, swimming, dancing, football, 
//swimming, basketball, baseball, football]

 

distinct() : 스트림에서 중복을 제거해줘서 하나씩만 존재하도록 만들어준다.

public static void main(String[] args) {
    List<String> collect = StudentDataBase.getAllStudents().stream()
            .map(Student::getActivities)
            .flatMap(List::stream)
            .distinct()
            .collect(Collectors.toList());
    System.out.println(collect);
}
//출력 : [swimming, basketball, volleyball, gymnastics, soccer,
//aerobics, dancing, football, baseball]

 

count() : 스트림의 총 요소 수를 반환한다. count는 stream타입을 반환하는 것이 아닌 long 타입을 반환하므로 종단 연산이다.

public static void main(String[] args) {
    long count = StudentDataBase.getAllStudents().stream()
            .map(Student::getActivities)
            .flatMap(List::stream)
            .distinct()
            .count();
    System.out.println(count);
}
//출력 : 9

 

sorted()

  • 스트림의 요소들을 정렬한다. 기본적으로 오름차순이다.
public static void main(String[] args) {
    List<String> collect = StudentDataBase.getAllStudents().stream()
            .map(Student::getActivities)
            .flatMap(List::stream)
            .distinct()
            .sorted()
            .collect(Collectors.toList());
    System.out.println(collect);
}
//출력 : [aerobics, baseball, basketball, dancing, football, 
//gymnastics, soccer, swimming, volleyball]
  • Comparator를 통해 특정 속성을 기준으로 정렬할 수도 있고, 내리참순으로도 바꿀 수 있다.
public static void main(String[] args) {
    List<Student> collect1 = StudentDataBase.getAllStudents().stream()
            .sorted(Comparator.comparing(Student::getGpa))
            .collect(Collectors.toList());
    List<Student> collect2 = StudentDataBase.getAllStudents().stream()
            .sorted(Comparator.comparing(Student::getGpa).reversed())
            .collect(Collectors.toList());
    System.out.println(collect1);
    System.out.println(collect2);
}
//출력1 : [Student{name='Sophia', gradeLevel=4, gpa=3.5, gender='female', activities=[swimming, dancing, football]}, Student{name='Adam', gradeLevel=2, gpa=3.6, gender='male', activities=[swimming, basketball, volleyball]}, Student{name='Jenny', gradeLevel=2, gpa=3.8, gender='female', activities=[swimming, gymnastics, soccer]}, Student{name='Dave', gradeLevel=3, gpa=3.9, gender='male', activities=[swimming, gymnastics, soccer]}, Student{name='James', gradeLevel=4, gpa=3.9, gender='male', activities=[swimming, basketball, baseball, football]}, Student{name='Emily', gradeLevel=3, gpa=4.0, gender='female', activities=[swimming, gymnastics, aerobics]}]
//출력2 : [Student{name='Emily', gradeLevel=3, gpa=4.0, gender='female', activities=[swimming, gymnastics, aerobics]}, Student{name='Dave', gradeLevel=3, gpa=3.9, gender='male', activities=[swimming, gymnastics, soccer]}, Student{name='James', gradeLevel=4, gpa=3.9, gender='male', activities=[swimming, basketball, baseball, football]}, Student{name='Jenny', gradeLevel=2, gpa=3.8, gender='female', activities=[swimming, gymnastics, soccer]}, Student{name='Adam', gradeLevel=2, gpa=3.6, gender='male', activities=[swimming, basketball, volleyball]}, Student{name='Sophia', gradeLevel=4, gpa=3.5, gender='female', activities=[swimming, dancing, football]}]

 

reduce()

  • 종단 연산으로 스트림의 요소들을 하나의 요소(값)로 축약하는데 사용된다.
  • 예를 들어 모든 요소를 더하거나 곱해서 하나의 값으로 만들고 싶을 때 사용할 수 있다. 또는 가장 높은 값을 찾는 등의 작업에서도 유용하다.
  • 인자로는 초기값과 이진 연산자를 받는다. 
    • 초기값 : 연산을 시작할 때 사용할 기본값
    • 이진 연산자 : 두 값을 받아 하나의 결과를 반환하는 연산이다. 즉, 두 입력값을 받아 같은 타입의 결과값을 반환하는 함수이다.
  • 초기값을 안 주면 Optional이 리턴타입으로 설정된다. 리스트가 비어있을 시에 Optional.empty()를 반환한다. 이와 반대로 어떤 이진 연산자이더라도 초기값은 있는데 리스트가 비어있으면 초기값을 그대로 반환한다.
public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1,3,5,7);

        int result1 = performMultipleication(numbers);
        System.out.println(result1);
        Optional<Integer> result2 = performMultipleicationWithoutIdentity(numbers);
        System.out.println(result2.orElseThrow());
        Optional<Student> highestGPAStudent = getHighestGPAStudent();
        System.out.println(highestGPAStudent.orElseThrow());
    }

    private static int performMultipleication(List<Integer> numbers) {
        return numbers.stream()
                .reduce(3, (a,b)->a*b);
    }

    private static Optional<Integer> performMultipleicationWithoutIdentity(List<Integer> numbers) {
        return numbers.stream()
                .reduce((a,b)->a*b);
    }

    private static Optional<Student> getHighestGPAStudent() {
        return StudentDataBase.getAllStudents().stream()
                .reduce((s1, s2) -> s1.getGpa()>s2.getGpa()?s1:s2);
    }
//출력 : 305 105 Student{name='Emily', gradeLevel=3, gpa=4.0, gender='female', activities=[swimming, gymnastics, aerobics]}

 

limit() : 스트림의 처음 N개의 요소만 처리한다. 예를 들어 10개의 요소를 가진 리스트에서 limit(5)를 사용하면 처음 5개의 요소만 처리된다.

skip() : 처음 N개의 요소를 건너뛰고 나머지 요소를 처리한다. 예를 들어 10개의 요소를 가진 리스트에서 skip(3)을 사용하면 처음 3개의 요소를 건너뛴다.

public static void main(String[] args) {
    List<Integer> integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    Optional<Integer> result = sumSkipAndLimit(integers);
    System.out.println(result.orElseThrow());
}

private static Optional<Integer> sumSkipAndLimit(List<Integer> integers) {
    return integers.stream()
            .skip(5)
            .limit(1)
            .reduce(Integer::sum);
}
//출력 : 6

 

allMatch() : 스트림의 모든 요소가 주어진 조건을 만족하는지 검사한다. 모든 요소가 조건을 만족하면 true, 아니면 false 반환한다.

anyMatch() : 스트림의 요소 중 하나라도 주어진 조건을 만족하면 true, 아니면 false를 반환한다.

noneMath() : 스트림의 모든 요소가 주어진 조건을 만족하지 않을 때 true, 하나라도 조건을 만족하면 false 반환한다.

public static void main(String[] args) {
    boolean allMatchResult = allMatch();
    System.out.println(allMatchResult);
    boolean anyMatchResult = anyMatch();
    System.out.println(anyMatchResult);
    boolean noneMatchResult = noneMatch();
    System.out.println(noneMatchResult);
}

private static boolean allMatch() {
    return StudentDataBase.getAllStudents().stream()
            .allMatch(student -> student.getGpa()>=3.5);
}

private static boolean anyMatch() {
    return StudentDataBase.getAllStudents().stream()
            .anyMatch(student -> student.getGpa()==4.0);
}

private static boolean noneMatch() {
    return StudentDataBase.getAllStudents().stream()
            .noneMatch(student -> student.getGpa()==4.1);
}
//출력 : true true true

 

findFirst() : 스트림에서 조건을 만족하는 첫 번째 요소를 반환한다. 병렬 스트림에서도 컬렉션 순서에 대해 조건을 만족하는 첫 번째 요소를 반환한다.

findAny() : 스트림에서 조건을 만족하는 임의의 요소를 반환한다. 순차 스트림에서는 findFirst와 동일하게 첫 번째 요소를 반환하지만, 병렬 스트림에서는 첫 번째 요소가 아닐 수도 있다.

위의 두 함수 모두 Optional 객체를 반환하며, 비어 있거나 조건을 만족하는 요소가 없을 경우 Optional.empty()를 반환한다.

 

Short Circuit 

  • 이 용어는 단축 평가라고 부르는데, 예를 들어 and 연산자가 있을 때, 앞쪽에 하나를 평가했는데 true이면 뒤의 식을 평가하지 않는 것이다. 추가적으로 or일 때는 앞의 식을 평가했을 때 true이면 뒤의 식을 평가하지 않는 것을 말한다.
  • 스트림에서도 마찬가지로 단축평가가 이뤄지는데, 이는 모든 스트림 요소들을 거치지 않는다는 의미이다.
  • 단축 평가를 사용하는 함수로는 limit, findFirst, findAny, anyMatch, allMatch, noneMatch가 있다.