매핑(mapping)은 중간 처리 기능으로 스트림의 요소를 다른 요소로 대체하는 작업을 말한다.
스트림에서 제공하는 매핑 메소드는 flatXXX()
, mapXXX()
, asDoubleStream()
, asLongStream()
, boxed()
가 있다.
flatMapXXX()
메소드는 요소를 대체하는 복수 개의 요소들로 구성된 새로운 스트림을 리턴한다.
스트림에서 A라는 요소는 A1, A2 요소로 대체되고, B라는 요소는 B1, B2로 대체된다고 가정했을 경우,
A1, A2, B1, B2 요소를 가지는 새로운 스트림이 생성된다.
입력된 데이터(요소)들이 List<String>
에 저장되어 있다고 가정하고, 요소별로 단어를 뽑아 단어 스트림으로 재생성한다. 만약 입력된 데이터들이 숫자라면 숫자를 뽑아 숫자 스트림으로 재생성한다.
import java.util.Arrays;
import java.util.List;
public class FlatMapExample {
public static void main(String[] args) {
List<String> inputList1 = Arrays.asList("java8 lambda", "stream mapping");
inputList1.stream()
.flatMap(data -> Arrays.stream(data.split(" ")))
.forEach(word -> System.out.println(word));
System.out.println();
List<String> inputList2 = Arrays.asList("10, 20, 30", "40, 50, 60");
inputList2.stream()
.flatMapToInt(data -> {
String[] strArr = data.split(",");
int[] intArr = new int[strArr.length];
for (int i = 0; i < strArr.length; i++) {
intArr[i] = Integer.parseInt(strArr[i].trim());
}
return Arrays.stream(intArr);
})
.forEach(number -> System.out.println(number));
}
}
java8
lambda
stream
mapping
10
20
30
40
50
60
mapXXX()
메소드는 요소를 대체하는 요소로 구성된 새로운 스트림을 리턴한다.
A 요소는 C 요소로 대체되고, B 요소는 D 요소로 대체된다고 했을 경우,
C, D 요소를 가지는 새로운 스트림이 생성된다.
학생 List에서 학생의 점수를 요소로 하는 새로운 스트림을 생성하고, 점수를 순차적으로 콘솔에 출력한다.
import java.util.Arrays;
import java.util.List;
public class MapExample {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
new Student("김", 10),
new Student("이", 20),
new Student("박", 30)
);
studentList.stream()
.mapToInt(Student :: getScore)
.forEach(score -> System.out.println(score));
}
}
10
20
30
asDoubleStream()
메소드는 IntStream의 int 요소 또는 LongStream의 long 요소를 double 요소로 타입 변환해서 DoubleStream을 생성한다. asLongStream()
메소드는 IntStream의 int 요소를 long 요소로 타입 변환해서 LongStream을 생성한다.boxed()
메소드는 int, long, double 요소를 Integer, Long, Double 요소로 박싱해서 Stream을 생성한다.int[]
배열로부터 IntStream을 얻고 난 다음 int 요소를 double 요소로 타입 변환해서 DoubleStream을 생성한다.
또한 int 요소를 Integer 객체로 박싱해서 Stream<Integer>
를 생성한다.
import java.util.Arrays;
import java.util.stream.IntStream;
public class AsDoubleStreamAndBoxedExample {
public static void main(String[] args) {
int[] intArray = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(intArray);
intStream
.asDoubleStream()
.forEach(d -> System.out.println(d));
System.out.println();
intStream = Arrays.stream(intArray);
intStream
.boxed()
.forEach(obj -> System.out.println(obj.intValue()));
}
}
1.0
2.0
3.0
4.0
5.0
1
2
3
4
5
스트림은 요소가 최종 처리되기 전에 중간 단계에서 요소를 정렬해서 최종 처리 순서를 변경할 수 있다.
객체 요소일 경우에는 클래스가 Comparable을 구현하지 않으면 sorted() 메소드를 호출했을 때 ClassCastException이 발생하기 때문에 Comparable을 구현한 요소에서만 sorted() 메소드를 호출해야 한다.
점수를 기준으로 Student 요소를 오름차순으로 정렬하기 위해 Comparable을 구현했다.
public class Student implements Comparable<Student> {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public int getScore() {
return score;
}
@Override
public int compareTo(Student o) {
return Integer.compare(score, o.score);
}
}
sorted();
sorted( (a, b) -> a.compareTo(b) );
sorted( Comparator.naturalOrder() );
sorted( (a, b) -> a.compareTo(b) );
sorted( Comparator.reverseOrder() );
sorted( (a, b) -> { ... } )
중괄호 {}
안에는 a와 b를 비교해서
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.IntStream;
public class SortingExample {
public static void main(String[] args) {
//숫자 요소일 경우
IntStream intStream = Arrays.stream(new int[] {5, 3, 2, 1, 4});
intStream
.sorted()
.forEach(n -> System.out.print(n + ", "));
System.out.println();
//객체 요소일 경우
List<Student> studentList = Arrays.asList(
new Student("김", 30),
new Student("이", 10),
new Student("박", 20)
);
studentList.stream()
.sorted()
.forEach(s -> System.out.print(s.getScore() + ", "));
System.out.println();
studentList.stream()
.sorted(Comparator.reverseOrder())
.forEach(s -> System.out.print(s.getScore() + ", "));
}
}
1, 2, 3, 4, 5,
10, 20, 30,
30, 20, 10,
루핑(looping)은 요소 전체를 반복하는 것을 말한다.
루핑하는 메소드에는 peek(), forEach()가 있다. 이 두 메소드는 루핑한다는 기능에서는 동일하지만, 동작 방식은 다르다. peek()은 중간 처리 메소드이고, forEach()는 최종 처리 메소드이다.
peek()은 중간 처리 단계에서 전체 요소를 루핑하면서 추가적인 작업을 하기 위해 사용된다.
최종 처리 메소드가 실행되지 않으면 지연되기 때문에 반드시 최종 처리 메소드가 호출되어야 동작한다.
예를 들어 필터링 후 어떤 요소만 남았는지 확인하기 위해 다음과 같이 peek()를 마지막에서 호출할 경우, 스트림은 전혀 동작하지 않는다.
intStream
.filter(a -> a % 2 == 0)
.peek(a -> System.out.print(a))
요소 처리의 최종 단계가 합을 구하는 것이라면, peek() 메소드 호출 후 sum()을 호출해야만 peek()가 정상적으로 동작한다.
intStream
.filter(a -> a % 2 == 0)
.peek(a -> System.out.print(a))
.sum()
forEach()는 최종 처리 메소드이기 때문에 파이프라인 마지막에 루핑하면서 요소를 하나씩 처리한다.
forEach()는 요소를 소비하는 최종 처리 메소드이므로 이후에 sum()과 같은 다른 최종 메소드를 호출하면 안된다.
import java.util.Arrays;
public class LoopingExample {
public static void main(String[] args) {
int[] intArr = {1, 2, 3, 4, 5};
System.out.println("[peek()를 마지막에 호출한 경우]");
Arrays.stream(intArr)
.filter(a -> a % 2 == 0)
.peek(n -> System.out.println(n)); //동작하지 않는다.
System.out.println("[최종 처리 메소드를 마지막에 호출한 경우]");
int total = Arrays.stream(intArr)
.filter(a -> a % 2 == 0)
.peek(n -> System.out.println(n)) //동작한다.
.sum(); //최종 메소드
System.out.println("총합: " + total);
System.out.println("[forEach()를 마지막에 호출한 경우]");
Arrays.stream(intArr)
.filter(a -> a % 2 == 0)
.forEach(n -> System.out.println(n)); //최종 메소드로 동작한다.
}
}
[peek()를 마지막에 호출한 경우]
[최종 처리 메소드를 마지막에 호출한 경우]
2
4
총합: 6
[forEach()를 마지막에 호출한 경우]
2
4
이것이 자바다 책