Stream Operations
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가 있다.