[Java] Stream 스트림
배열, 컬렉션의 저장 요소를 하나씩 참조해 람다식으로 처리할 수 있도록 하는 반복자
List, Set, Map 등 다양한 데이터 소스로부터 스트림을 만들 수 있음
// 명령형 방식
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
int sum = 0;
for(int number : numbers){
if(number > 4 && (number % 2 == 0)){
sum += number;
}
}
// 선언형 방식
List<Integer> numbers = List.of(1, 3, 6, 7, 8, 11);
int sum =
numbers.stream()
.filter(number -> number > 4 && (number % 2 == 0))
.mapToInt(number -> number)
.sum();
배열 스트림 생성
Arrays
클래스의 stream()
메서드 또는 Stream
클래스의 of()
메서드를 사용할 수 있다.
Arrays.stream()
// 문자열 배열 선언 및 할당
String[] arr = new String[]{"김코딩", "이자바", "박해커"};
// 문자열 스트림 생성
Stream<String> stream = Arrays.stream(arr);
// 출력
stream.forEach(System.out::println);
Stream.of()
// 문자열 배열 선언 및 할당
String[] arr = new String[]{"김코딩", "이자바", "박해커"};
// 문자열 스트림 생성
Stream<String> stream = Stream.of(arr);
// 출력
stream.forEach(System.out::println);
배열로 스트림을 생성할 시에는 Arrays.stream()
와 Stream.of()
둘 중 더 편한 메서드를 선택하여 사용할 수 있다.
컬렉션 스트림 생성
List, Set 등의 경우, stream()
메서드를 사용하여 스트림을 생성할 수 있다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Stream<Integer> stream = list.stream();
stream.forEach(System.out::print);
중간 연산
필터링
distinct()
중복된 데이터가 존재하는 경우, 중복을 제거하기 위해 사용한다.filter()
조건에 맞는 데이터만 정제하여 더 작은 컬렉션을 만든다.
List<String> names = Arrays.asList("가가", "나나", "다다", "가가", "잉잉");
names.stream()
.distinct() //중복제거
.filter(element -> element.startsWith("김")) // 김씨 성을 가진 요소만 필터링
.forEach(element -> System.out.println(element));
매핑
원하는 필드만 추출하거나 특정 형태로 변환할 때 사용한다.
**.map()**
List<String> names = Arrays.asList("aa", "bb", "cc");
names.stream()
.map(element -> element.toUpperCase()) // 요소들을 하나씩 대문자로 변환
.forEach(element->System.out.println(element));
List<Integer> list = Arrays.asList(1, 3, 6, 9);
// 각 요소에 3을 곱한 값을 반환
list.stream().map(number -> number * 3).forEach(System.out::println);
**.flatMap()**
중첩 구조를 제거하고 단일 컬렉션으로 만들어주는 역할을 한다.
// map() 사용
Arrays.stream(namesArray)
.map(inner -> Arrays.stream(inner))
.forEach(System.out::println);
// 출력값
java.util.stream.ReferencePipeline$Head@3cb5cdba
java.util.stream.ReferencePipeline$Head@56cbfb61
// flatMap()
Arrays.stream(namesArray).flatMap(Arrays::stream).forEach(System.out::println);
// 출력값
aa
bb
cc
정렬
정렬할 때 사용한다.
List<String> animals = Arrays.asList("T", "L", "M", "D", "H", "C");
animals.stream().sorted().forEach(System.out::println);
// 출력값
C
D
H
L
M
T
역순으로 정렬
List<String> animals = Arrays.asList("T", "L", "M", "D", "H", "C");
animals.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
// 출력값
T
M
L
H
D
C
기타
skip()
- 스트림의 일부 요소들을 건너뛴다.limit()
- 스트림의 일부를 자른다.-
peek()
-forEach()
와 마찬가지로, 요소들을 순회하며 특정 작업을 수행한다peek()
는 중간 연산자이기 때문에 여러번 연결하여 사용할 수 있는 반면,forEach()
는 최종 연산자이기 때문에 한번만 사용 가능하다.
스트림 최종 연산
기본 집계
sum()
, count()
, average()
, max()
, min()
int[] intArray = {1,2,3,4,5};
// 배열의 첫 번째 요소
int first = Arrays.stream(intArray).findFirst().getAsInt(); // 1
// 최소값
int min = Arrays.stream(intArray).min().getAsInt(); // 1
// 최대값
int max = Arrays.stream(intArray).max().getAsInt(); // 5
// 합계
long sum = Arrays.stream(intArray).sum(); // 15
// 평균
double average = Arrays.stream(intArray).average().getAsDouble(); // 3.0
// 카운팅
long count = Arrays.stream(intArray).count(); // 5
최종 연산자 뒤에 getAsInt()
, getAsDouble()
메서드가 있는 이유
OptionalDouble average = Arrays.stream(intArr).average(); // OptionalDouble[3.0]
double result = average.getAsDouble(); // 3.0
매칭
allMatch()
, anyMatch()
, noneMAtch()
allMatch()
- 모든 요소들이 조건을 만족하는 지 여부를 판단한다.noneMatch()
- 모든 요소들이 조건을 만족하지 않는 지 여부를 판단한다.anyMatch()
- 하나라도 조건을 만족하는 요소가 있는 지 여부를 판단한.
int[] intArray = {2,4,6};
boolean result = Arrays.stream(intArray).allMatch(element-> element % 2 == 0);
//true
result = Arrays.stream(intArray).anyMatch(element-> element % 3 == 0);
// true
result = Arrays.stream(intArray).noneMatch(element -> element % 3 == 0);
//false
요소 소모
스트림의 요소를 줄여나가면서 연산을 수행하고 최종적인 결과를 반환한다.
**reduce()**
int[] intArray = {1,2,3,4,5};
int sum1 = Arrays.stream(intArray)
.map(element -> element * 2)
.reduce((a , b) -> a + b)
.getAsInt();
// 30
- .map(각 요소에 2를 곱해줌)
- .reduce((a , b) -> a + b) (a: 누적 값, b: 새로 더해질 값)
- 2+4 → a: 6, b: 6
- 6+6 → a:12, b: 8
- 12+8 → a:20, b: 10
- 20+10 → 최종 결과 30
요소 수집
-
**[collect()](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html)
** https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.htmlCollector 인터페이스 타입의 인자를 받아서 처리 한다.
collect() 메서드는 요소를 수집하는 기능 외에도 요소 그룹핑 및 분할 등 다른 기능들을 제공한다.
public class TerminalOperationExample {
public static void main(String[] args) {
// Student 객체로 구성된 배열 리스트 생성
List<Student> totalList = Arrays.asList(
new Student("김코딩", 100, Student.Gender.Male),
new Student("박해커", 80, Student.Gender.Male),
new Student("이자바", 90, Student.Gender.Female),
new Student("나미녀", 60, Student.Gender.Female)
);
// 스트림 연산 결과를 Map으로 반환
Map<String, Integer> maleMap = totalList.stream()
.filter(s -> s.getGender() == Student.Gender.Male)
.collect(Collectors.toMap(
student -> student.getName(), // Key
student -> student.getScore() // Value
));
// 출력
System.out.println(maleMap);
}
}
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 String getName() {
return name;
}
public int getScore() {
return score;
}
public Gender getGender() {
return gender;
}
}
// 출력값
{김코딩=100, 박해커=80}
댓글남기기