Stream API란?
- (컬렉션) 데이터의 처리를 선언적으로 작성하도록 지원하는 도구이다.
- 데이터를 필터링하거나 변환하거나 집계하는 작업을 한다.
- 람다 표현식과 결합하여 직관적이고 간결한 코드를 작성한다.
- 컬렉션 데이터를 활용하여 filtering, Mapping, 축소 등의 작업을 처리한다
- 함수형 프로그래밍 기법과 함께 사용한다.
특징
1. 스트림은 데이터를 소스로부터 연속적으로 처리하는 파이프라인을 구축한다.
>>데이터 파이프라인 (Data Pipe Line) : 데이터를 사용하는 단계 흐름 절차를 나타낸다.
2. 데이터가 변경되지 않는다(불변성)
스트림은 원본 데이터를 변경하지 않고 새로운 값을 반환한다.
3. 지연처리 : 중간중간 처리가 지연되어 최정 처리가 호출될 때 한 번에 처리된다.
기본 구성 요소
리스트를 다음과 같이 생성했다고 가정했을 때 각각 다음 코드를 통하여 Stream API에 대해 더 자세히 알아보자
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.*;
import java.util.stream.Collectors;
@AllArgsConstructor
@Getter
@ToString
class StudentClass {
private String name;
private int age;
}
public class StreamExamplesNoGrade {
public static void main(String[] args) {
List<StudentClass> students = new ArrayList<>(Arrays.asList(
new StudentClass("Bergerac", 20),
new StudentClass("Cyrano", 38),
new StudentClass("JoeHyungGeun", 40),
new StudentClass("ChoiJeaRim", 39),
new StudentClass("KoEunSung", 34),
new StudentClass("NaHaNa", 32),
new StudentClass("KimSooYeon", 31),
new StudentClass("LeeJiSoo", 31),
new StudentClass("LimJunHeok", 36),
new StudentClass("ChaYoonHea", 30),
new StudentClass("LeeJungMin", 28)
));
1. 소스(Source)
- 스트림 생성 단계
- 컬렉션, 배열 또는 파일을 소스로 사용하게 된다.
데이터.stream()
2. 중간 연산
- 데이터를 Filtering, Mapping(변환)하는 과정이다.
- 연산은 연속적으로 사용이 가능하다.
- 스트림은 새로운 스트림을 반환하기 때문에 연속적으로 사용이 가능하다. ex) filter Map, Sort
- 스트림 소스에 원하는 기능을 사용가능하다.
- 전체 스트림을 순회하여 각 키워드의 기능을 수행한다.
***스트림 중간 연산자 내부의 람다식 : 스트림 내부의 요소값을 하나씩 매개변수에 담음
>> 구현부에서 인자로 해당 매개변수와 일치하는 값을 전달하는 경우 메서드 참조 가능
1) filter : 조건에 맞는 요소만 선택한다.
--> 여기에 있는 이름 중 이름이 L로 시작하는 경우에만 선택한다.
List<StudentClass> filteredStudents = students.stream()
.filter(student -> student.getName().startsWith("L"))
.collect(Collectors.toList());
System.out.println("Filtered Students: " + filteredStudents);
2) map : 요소 변환
-> 모든 학생의 이름을 대문자로 변환한다.
List<StudentClass> mappedStudents = students.stream()
.map(student -> new StudentClass(student.getName().toUpperCase(), student.getAge()))
.collect(Collectors.toList());
System.out.println("Mapped Students: " + mappedStudents);
3) sorted : 요소 정렬
->이름을 알파벳 순으로 정렬하기
List<StudentClass> sortedStudents = students.stream()
.sorted(Comparator.comparing(StudentClass::getName))
.collect(Collectors.toList());
System.out.println("Sorted StudentsbyAlphabet " );
sortedStudents.forEach(System.out::println);
<출력결과>
StudentClass(name=Bergerac, age=20)
StudentClass(name=ChaYoonHea, age=22)
StudentClass(name=ChoiJeaRim, age=39)
StudentClass(name=Cyrano, age=38)
StudentClass(name=JoeHyungGeun, age=40)
StudentClass(name=KimSooYeon, age=21)
StudentClass(name=KoEunSung, age=34)
StudentClass(name=LeeJiSoo, age=23)
StudentClass(name=LeeJungMin, age=28)
StudentClass(name=LimJunHeok, age=36)
StudentClass(name=NaHaNa, age=22)
->이름을 나이 순으로 정렬하기
List<StudentClass> sortedStudentsByAge = students.stream()
.sorted(Comparator.comparingInt(StudentClass::getAge).reversed())//나이 많은 순
// .sorted(Comparator.comparingInt(StudentClass::getAge))//나이 적은 순
.collect(Collectors.toList());
System.out.println("Sorted Students by Age" );
sortedStudentsByAge.forEach(System.out::println);
<출력결과>
Sorted Students by Age
StudentClass(name=JoeHyungGeun, age=40)
StudentClass(name=ChoiJeaRim, age=39)
StudentClass(name=Cyrano, age=38)
StudentClass(name=LimJunHeok, age=36)
StudentClass(name=KoEunSung, age=34)
StudentClass(name=LeeJungMin, age=28)
StudentClass(name=LeeJiSoo, age=23)
StudentClass(name=NaHaNa, age=22)
StudentClass(name=ChaYoonHea, age=22)
StudentClass(name=KimSooYeon, age=21)
StudentClass(name=Bergerac, age=20)
4) distinct : 중복 제거(중복이 있다고 가정)
List<StudentClass> distinctStudents = students.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("Distinct Students: " + distinctStudents);
5) limit : 처음부터 지정된 개수만큼 선택한다(5개)
List<StudentClass> limitedStudents = students.stream()
.limit(5)
.collect(Collectors.toList());
System.out.println("Limited Students: " + limitedStudents);
6) skip : 처음부터 지정된 개수만큼 건너뛴다(3개)
List<StudentClass> skippedStudents = students.stream()
.skip(3)
.collect(Collectors.toList());
System.out.println("Skipped Students: " + skippedStudents);
3. 최종 연산
모든 데이터를 처리한 후 결과를 반환하거나 출력하는 연산
해당 연산은 항상 맨 마지막에 나와야 한다.
Ex) collect, forEach, count
1) forEach
: 스트림의 모든 요소에 대하여 지정한 동작을 수행한다. (반환하지는 않는다.)
--> 위 예시에서 각 요소를 출력할 때 forEach 문을 사용했었다.
2) collect
: 스트림을 리스트, 세트 맵 등 여러가지 형태(컬렉션)으로 반환한다.
->2.2)의 예시에서 Mapping 한 뒤에 최종적으로 변환된 리스트를 받을 때 Collect 를 사용했었다.
3) reduce
: 모든 요소를 하나의 값으로 줄인다. (ex. 누적 합이나 최대최소값을 구하기 등에 활용된다.)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
위 경우에는 모든 요소를 합산한 결과를 반환한다.
4) count
: 스트림의 요소 개수를 long 형태로 반환한다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream()
.filter(n -> n % 2 == 0)
.count();
filter으로 2의 배수인 배열의 요소를 반환하여 개수를 세어주는 최종연산이다.
'JAVA' 카테고리의 다른 글
ENUM (열거형) 자료형 (1) | 2025.03.23 |
---|---|
Wrapper Class (1) | 2025.03.22 |
메서드 참조 (Lambda) (0) | 2025.03.20 |
람다식(Lambda) - 함수형 인터페이스 4가지(Predicate, Function, Consumer, Supplier) (1) | 2025.03.19 |
Lombok(롬복) (1) | 2025.03.18 |