스트림의 연산
- 스트림이 제공하는 기능 - 중간 연산과 최종 연산
중간 연산 : 연산결과가 스트림인 연산. 반복적으로 적용가능
최종 연산 : 연산 결과가 스트림이 아닌 연산. 단 한번만 적용 가능(스트림의 요소를 소모)
String[] strArr = { "dd","aaa","CC","cc","b" };
Stream<String> stream = Stream.of(strArr); // 문자열 배열이 소스인 스트림
// 중간 연산
Stream<String> filteredStream = stream.filter(); // 걸러내기(중간 연산)
Stream<String> distinctedStream = stream.distinct(); // 중복제거(중간 연산)
Stream<String> sortedStream = stream.sort(); // 정렬(중간 연산)
Stream<String> limitedStream = stream.limit(5); // 스트림 자르기(중간 연산)
//최종 연산
int total = stream.count(); // 요소 개수 세기(최종연산)
스트림의 중간 연산
중간 연산 | 설명 | |
Stream<T> distinct() | 중복을 제거 | |
Stream<T> filter(Predicate<? super T> predicate) | 조건에 안 맞는 요소 제외 - 조건에 맞지 않는 요소 제거 |
|
Stream<T> limit(long maxSize) | 스트림의 일부를 잘라낸다. - maxSize 이후의 요소는 잘라냄 |
|
Stream<T> skip(long n) | 스트림의 일부를 건너뛴다. - 앞에서부터 n개 건너뛰기 |
|
Stream<T> peek(Consumer<? super T> action) | 스트림의 요소에 작업 수행 - 중간 연산(스트림을 소비X) |
|
Stream<T> sorted() Stream<T> sorted(Comparator<? super T> comparator) |
스트림의 요소를 정렬한다. - 스트림 요소의 기본 정렬(Comparable)로 정렬 - 지정된 Comparator로 정렬 |
|
Stream<R> IntStream LongStream DoubleStream |
map(Function<T, R> mapper) mapToInt(ToIntFunction<T> mapper) mapToLong(ToLongFunction<T> mapper) mapToDouble(ToDoubleFunction<T> mapper) |
스트림의 요소를 변환한다. |
flatmap(Function<T, Stream<R>> mapper) flatmapToInt(Function<T, IntStream> mapper) flatmapToLong(Function<T, LongStream> mapper) flatmapToDouble(Function<T, DoubleStream> mapper) |
스트림 자르기
Stream<T> skip(long n) // 앞에서부터 n개 건너뛰기
Stream<T> limit(long maxSize) // maxSize 이후의 요소는 잘라냄
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.skip(3).limit(5).forEach(System.out::print); // 45678
스트림의 요소 걸러내기
Stream<T> filter(Predicate<? super T> predicate) // 조건에 맞지 않는 요소 제거
Stream<T> distinct()
IntStream intStream = IntStream.of(1,2,2,3,3,3,4,5,5,6);
intStream.distinct().forEach(System.out::print); // 123456
IntStream intStream = IntStream.rangeClosed(1, 10); // 12345678910
intStream.filter(i->i%2==0).forEach(System.out::print); // 246810
스트림의 요소 정렬하기
Stream<T> sorted() // 스트림 요소의 기본 정렬(Comparable)로 정렬
Stream<T> sorted(Comparator<? super T> comparator) //지정된 Comparator로 정렬
- Comparator의 comparing()으로 정렬 기준을 제공
public static <T, U> Comparator<T> comparing(Function keyExtractor, Comparator keyComparator)
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function keyExtractor)
studentStream.sorted(Comparator.comparing(Student::getBan)).forEach(Sysetm.out::println); // 반별로 정렬
- 추가 정렬 기준을 제공할 때는 thenComparing()을 사용
default Comparator<T> thenComparing(Comparator other)
default <U> Comparator<T> thenComparing(Function keyExtractor, Comparator keyComparator)
default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function keyExtractor)
studentStream.sorted(Comparator.comparing(Student::getBan) // 반별로 정렬
.thenComparing(Student::getTotalScore) // 총점별로 정렬
.forEach(Sysetm.out::println);
스트림의 요소 변환하기
Stream<R> map(Function<? super T,? extends R> mapper) //Stream<T>→Stream<R>
- 스트림의 각 요소를 식을통해 변환한 값을 가지는 스트림을 반환한다.
Stream<File> fileStream = Stream.of(
new File("Ex1.java"),
new File("Ex1"),
new File("Ex1.bak"),
new File("Ex2.java"),
new File("Ex1.txt"));
// 익명 클래스
Stream<String> filenameStream = fileStream.map(new Function<File, String>() {
@Override
public String apply(File f) {
return f.getName();
}
});
// 람다식
Stream<String> filenameStream = fileStream.map((f) -> f.getName());
// 메서드 참조
Stream<String> filenameStream = fileStream.map(File::getName);
filenameStream.forEach(System.out::println); // 스트림의 모든 파일의 이름을 출력
- mapToInt(), mapToLong() mapToDouble()은 스트림을 기본형 스트림으로 변환하기 위해 사용한다.
mapToInt(ToIntFunction<T> mapper)
mapToLong(ToLongFunction<T> mapper)
mapToDouble(ToDoubleFunction<T> mapper)
// Stream<String>을 Stream<Integer>으로 변환
Stream<Integer> intStream = Stream.of(strArr).map(String::length);
// Stream<String>을 IntStream으로 변한. IntStream은 기본형 스트림
IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
스트림의 요소를 소비하지 않고 엿보기
Stream<T> peek(Consumer<? super T> action) // 중간 연산(스트림을 소비X)
void forEach(Consumer<? super T> action) // 최종 연산(스트림을 소비O)
- 중간 작업 결과를 확인하는데 사용한다.(디버깅 용도)
fileStream.map(File::getName) // Stream<File> → Stream<String>
.filter(s -> s.indexOf('.')!=-1) // 확장자가 없는 것은 제외
.peek(s->System.out.printf("filename=%s%n", s)) // 파일명을 출력한다.
.map(s -> s.substring(s.indexOf('.')+1)) // 확장자만 추출
.peek(s->System.out.printf("extension=%s%n", s)) // 확장자를 출력한다.
.forEach(System.out::println); // 최종연산 스트림을 소비.
요소가 배열인 스트림을 스트림으로 변환
flatmap()메서드는 스트림의 형태가 배열과 같을 때, 모든 원소를 단일 원소 스트림으로 반환할 수 있다.
Stream<String[]> strArrStrm = Stream.of(
new String[]{"abc", "def", "ghi" },
new String[]{"ABC", "GHI", "JKLMN"});
- 여러개의 문자열 배열을 요소로 가지는 스트림을 문자를 요소로 가지는 스트림으로 변환
Stream<String> strStrStrm = strArrStrm.flatMap(Arrays::stream); // Arrays.stream(T[])
flatMap은 결과를 단일 원소 스트림으로 반환하기 때문에 flatMap의 결과를 가지고 바로 forEach 메서드를 통해 모든 요소를 출력할 수 있다.
반면에 map은 결과로 Stream<Stream<String>>와 같은 스트림의 스트림이기 때문에 map의 결과를 가지고 forEach메서드로 루프를 진행한 후 다시 한 번 forEach 메서드를 통해 루프를 진행한 후 모든 요소를 출력할 수 있다.
String[][] namesArray = new String[][]{ {"kim", "taeng"}, {"mad", "play"}};
Stream<String[]> linStream = Arrays.stream(namesArray);
//flatmap
linStream
.flatMap(inner -> Arrays.stream(inner)) // flatmap은 결과를 단일 스트림으로 반환
.forEach(System.out::println);
//map
linStream
.map(inner -> Arrays.stream(inner)) // map은 단일 요소로 리턴
.forEach(names -> names.forEach(System.out::println));
Optinal<T>
Integer,Long와 같이 기본형 값을 감싸는 클래스, 기본형을 객체로 다뤄야할 때 사용하는 클래스를 래퍼클래스라고 한다.
public final class Optional<T> {
...
private final T value;
...
}
Optional은 T 타입 객체의 래퍼클래스로 클래스 안에 T 타입의 참조변수를 가지고 있어 모든 종류의 객체를 저장할 수 있다이를 통해 null을 간접적으로 다룰수 있게 된다.
- null을 직접 다루는 것은 예상치 못한 NullPointerException이 발생할 수 있다는 위험이 있다.
Object result = getResult()
예를 들어 getResult() 메서드가 객체를 반환한다 했을 때 객체의 반환 값은 null이거나 객체인데 만약 반환 값이 null일때 result.toString()과 같이 메서드를 사용하면 nullPointerException발생한다.
if(result != null) {
System.out.println(result.toString());
}
따라서 직접 null을 다루게 된다면 예외를 방지하기 위해 항상 위와 같이 if문으로 null인지 체크하는 과정이 필요해지며 이 과정은 코드를 지저분해지게 한다.
- Optional<T> 클래스를 사용한다면 반환된 결과가 null이더라도 NullPointerException 를 방지할 수 있다.
Optional 클래스는 value에 값을 저장하기 때문에 값이 null이더라도 바로 NullPointerException이 발생하지 않으며, 매번 null 체크를 위해 if문을 사용하는 과정 대신 각종 메소드를 제공해주기 때문에 코드가 간결해 진다.
Optinal<T>객체 생성하기
Optional<T>객체를 생성하는 다양한 방법 - of(), ofNullable()
- 참조변수의 값이 null일 가능성이 있다면, of()대신 ofNullable을 사용한다.
String str = "abc";
Optional<String> optVal = Optional.of(str);
Optional<String> optVal = Optional.of("abc");
Optional<String> optVal = Optional.of(null); // NullPointerException발생
Optional<String> optVal = Optional.ofNullable(null); // OK
null대신 빈 Optional<T>객체를 사용하자.
Optional<String> optVal = null; // null로 초기화. 바람직하지 않음
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화, <String> 생략 가능
- String을 초기화 할때 ""로 초기화 하는 것과 같이 null로 초기화 하는 것보다 empty()로 초기화 하는 것이 바람직하다.
Optional<T>객체의 값 가져오기
Optional객체의 값 가져오기 – get( ), orElse( ), orElseGet( ), orElseThrow( )
Optional<String> optVal = Optional.of("abc");
String str1 = optVal.get(); // optVal에 저장된 값을 반환. null이면 예외발생
String str2 = optVal.orElse(""); // optVal에 저장된 값이 null일 때는, ""를 반환
String str3 = optVal.orElseGet(String::new); // 람다식 사용가능 () -> new String()
String str4 = optVal.orElseThrow(NullPointerException::new); // 널이면 예외발생
- 매개변수인 함수형 인터페이스Supplier<T>은 매개변수는 없고, 반환값만 있다.
T orElseGet(Supplier<? extends T> other)
T orElseThrow(Supplier<? extends X> exceptionSupplier)
- isPresent() – Optional객체의 값이 null이면 false, 아니면 true를 반환
if(Optional.ofNullable(str).isPresent()) { // if(str!=null) {
System.out.println(str);
}
- ifPresnt(Consumer) - Optional객체의 값이 널이 아닐때만 작업 수행, 널이면 아무 일도 안 함
// void ifPresent(Consumer<? super T> action)
Optional.ofNullable(str).ifPresent(System.out::println);
예시
Optional<String> optStr = Optional.of("abcde"); // abcde
// Optional클래스의 map메서드 사용
Optional<Integer> optInt = optStr.map(String::length); // 5
// Optional클래스의 filter메서드 사용
int result2 = Optional.of("")
.filter(x -> x.length() > 0) // filter의 조건식을 충족하지 못해 empty()반환(null)
.map(Integer::parseInt).orElse(-1);
// value의 값이 null이 아니기 때문에 작업 수행
Optional.of("456").map(Integer::parseInt)
.ifPresent(x -> System.out.printf("result3=%d%n", x));
OptionalInt, OptionalLong, OptionalDouble
기본형 값을 감싸는 래퍼클래스 – OptionalInt, OptionalLong, OptionalDouble
public final class OptionalInt {
...
private final boolean isPresent; // 값이 저장되어 있으면 true
private final int value; // int타입의 변수
- 람다와 스트림이 모든걸 감싸고 있고 모든걸 객체로 다루기 때문에 성능이 떨어진다. Optional<T>를 사용해도 무방하지만 성능을 높이기 위해 사용한다.
OptionalInt의 값 가져오기 – int getAsInt( )
빈 Optional객체의 비교
- value에 0을 저장하면 value가 0이되고 아무것도 저장 안해도 기본값이 0이라 value는 0인데 빈 객체인지 어떻게 어떻게 구별할까?
OptionalInt opt1 = OptionalInt.of(0); // OptionalInt에 0을 저장
OptionalInt opt2 = OptionalInt.empty(); // 빈 OptionalInt객체. OptionalInt에 0이 저장됨
빈 Optional 객체의 비교를 위해 boolean 타입의 isPresent 변수가 존재한다.
System.out.println(opt1.isPresent()); //true
System.out.println(opt2.isPresent()); //false
System.out.println(opt1.equals(opt2)); // false
똑같이 0이더라도 isPresent가 true면 값이 존재, false면 빈 객체를 의미한다.
System.out.println(opt1.getAsInt()); // 0
System.out.println(opt2.getAsInt()); // NoSuchElementException
System.out.println(opt1); // OptionalInt[0]
System.out.println(opt2); // OptionalInt.empty
'Java' 카테고리의 다른 글
[JAVA] 스트림(Stream) API(4, Collectors, 그룹화와 분할) (0) | 2024.03.14 |
---|---|
[JAVA] 스트림(Stream) API(3, 최종 연산) (0) | 2024.03.09 |
[JAVA] 스트림(Stream) API(1) (0) | 2024.02.25 |
[JAVA] 람다 표현식(Lambda Expression) (0) | 2024.01.28 |
[JAVA] 표준 입출력과 File 클래스, 직렬화(Serialization) (0) | 2024.01.18 |