Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 코드로 시작하는 자바 첫걸음
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch3
- @Aspect
- 게시글 목록 api
- 자바의 정석 기초편 ch11
- 자바의 정석 기초편 ch6
- 스프링 db2 - 데이터 접근 기술
- 스프링 mvc2 - 검증
- 타임리프 - 기본기능
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch12
- 스프링 입문(무료)
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch8
- 자바의 정석 기초편 ch2
- 스프링 mvc1 - 스프링 mvc
- 자바의 정석 기초편 ch9
- 자바의 정석 기초편 ch5
- 자바의 정석 기초편 ch4
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch7
- jpa 활용2 - api 개발 고급
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch14
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch1
- 스프링 고급 - 스프링 aop
- 스프링 mvc2 - 타임리프
Archives
- Today
- Total
나구리의 개발공부기록
자바의 정석 기초편 ch14 - 35 ~ 44 [Optional, 최종연산(reduce까지)] 본문
유튜브 공부/JAVA의 정석 기초편(유튜브)
자바의 정석 기초편 ch14 - 35 ~ 44 [Optional, 최종연산(reduce까지)]
소소한나구리 2023. 12. 22. 09:131) Optional<T>
- T타입 객체의 래퍼클래스이며 간접적으로 null을 다루고 코드를 간결하게 함
- null을 직접 다루는 것은 위험(NullPointerException 발생) 하기 때문에 null체크을 항상 체크해야하는데 if문으로 항상 null을 체크하게되면 코드가 지저분해지게 되어 Optional을 반환타입으로 자주 사용함
public final class Optional<T> {
private final T value // 모든 종류의 객체를 저장 가능(참조형), null포함
...
}
(1) Optional<T>객체를 생성하는 다양한 방법
- of는 null을 허용하지 않고 ofNullable은 null을 허용
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
(2) null대신 빈 Optional<T>객체를 사용
- ofNullable로 null을 초기화하는 것은 권장하지 않으므로 값이 없다면 empty()로 빈 Optional로 초기화하는 것을 권장함
Optional<String> optVal = null; // 널 초기화 -> 바람직하지 않음
Optional<String> optVal = Optional.<String>empty(); // 빈 객체로 초기화, 제네릭스 생략가능
(3) Optional<T>객체의 값 가져오기
- 전부 값을 반환하지만 null일때의 동작이 다름
- get() : null일때는 예외발생하여 잘 사용하지 않음
- orElse() : null일때 인수에 입력한 값을 반환함
- orElseGet() : null일때의 동작을 람다식이나 메서드참조를 입력하여 작성
- orElseThrow() : null일 때 인수의 예외를 발생시킬 수 있음, 람다식 or 메서드참조로 작성
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); // 값이 없으면 예외발생
// 메서드 선언부
T orElseGet(Supplier<? extends T> other)
T orElseThrow(Supplier<? extends X> exceptionSupplier)
(4) isPresent() - null 여부 확인
- Optional객체의 값이 null이면 false, 아니면 true를 반환
if(Optional.ofNullable(str).isPresent()) { // if(str!=null) {
System.out.println(str);
}
// ifPresnt(Consumer) - 널이 아닐때만 작업 수행, 널이면 아무 일도 안 함
Optional.ofNullable(str).ifPresent(System.out::println);
(5) 예제
- 다양한 Optional 객체 생성 방법
import java.util.Optional;
public class Ex14_OptionalBasic {
public static void main(String[] args) {
// int[] arr = null; // 에러발생(NullPointerException)
int[] arr= new int[0];
System.out.println("arr.length=" +arr.length);
// Optional<String> opt = null; // 에러는 발생하지 않지만 바람직하지 않음
Optional<String> opt = Optional.empty(); // 빈 Optional 객체 생성
Optional<String> opt2 = Optional.of("abc"); // 값이 "abc"인 opt객체 생성
System.out.println("opt2=" +opt2);
// System.out.println("opt=" +opt.get()); // Optional이 null이면 에러 발생
String str = "";
str = opt.orElse(""); // Optional에 저장된 값이 null이면 "" 반환
// str = opt.orElseGet(() -> new String()); // 람다식 사용
str = opt.orElseGet(String::new); // 메서드참조로 변경(위와 같음)
System.out.println("str="+str);
}
}
2) OptionalInt, OptionalLong, OptionalDouble
- 기본형 값을 감싸는 래퍼클래스, 기본형스트림과 마찬가지로 성능향상을 위해 사용
- 값의 저장 여부와 값을 담을 변수가 멤버로 있음
// OptionalInt 내부 구조 일부
public final class OptionalInt {
...
private final boolean isPresent; // 값이 저장되어 있으면 true
private final int value; // int타입의 변수(기본형)
...
}
(1) 값 반환
- OptionalInt : getAsInt()
- OptionalLong : getAsLong()
- OptionalDouble : getAsDouble()
(2) 빈 Optional객체와의 비교
- isPresent()로 옵셔널에 값이 저장되어있는지 확인할 수 있음, 값이 null이면 false
- 옵셔널을 equals로 비교하면 값이 같아도 isPresent의 결과까지 true여야 equals의 결과가 true가 됨
// opt1과 opt2를 어떻게 구분? isPresent 활용(값이 있으면 true)
OptionalInt opt1 = OptionalInt.of(0); // OptionalInt에 0을 저장
OptionalInt opt2 = OptionalInt.empty(); // 빈 OptionalInt객체. OptionalInt에 0이 저장됨
System.out.println(opt1.isPresent()); // true
System.out.println(opt2.isPresent()); // false
System.out.println(opt1.equals(opt2)); // false, value는 같지만 isPresent값까지 같아야 true
(3) 예제
package ch14;
import java.util.*;
class Ex14_8 {
public static void main(String[] args) {
Optional<String> optStr = Optional.of("abcdef");
// Optional<Integer> optInt = Optional.of(6);
// Optional<Integer> optInt = optStr.map((i) -> i.length());
Optional<Integer> optInt = optStr.map(String::length); // optStr에 저장된 요소의 길이로 옵셔널 생성
System.out.println("optStr = " + optStr.get());
System.out.println("optInt = " + optInt.get());
int result1 = Optional.of("123") // 길이가 3인 문자열
.filter((s) -> s.length() > 0) // 0보다 큰 문자열의 길이를 반환
.map(Integer::parseInt).get(); // Integer로 변환 후 값을 저장
int result2 = Optional.of("") // 길이가 0인 문자열
.filter((s) -> s.length() > 0)
.map(Integer::parseInt).orElse(-1);
// 값이 없음(null) -> null이면 -1 출력
System.out.println("result1 = " + result1);
System.out.println("result2 = " + result2);
// 참조변수에 저장하지 않고 바로 출력
Optional.of("너굴너굴").map(String::valueOf)
.ifPresent( x -> System.out.printf("result3 = %s%n",x));
Optional.of("456").map(Integer::parseInt)
.ifPresent( x -> System.out.printf("result4 = %s%n",x));
OptionalInt optInt1 = OptionalInt.of(0); // 값이 0인 OptionalInt
OptionalInt optInt2 = OptionalInt.empty(); // 빈객체를 생성
//isPresent() : 객체의 값이 null이면 false 아니면 true
System.out.println(optInt1.isPresent()); // 값이 0 -> true
System.out.println(optInt2.isPresent()); // 값이 null -> false
System.out.println(optInt1.getAsInt()); // 값 가져오기
// 값 가져오기 -> 값이 없으면 에러발생(NoSuchElementException)
// System.out.println(optInt2.getAsInt());
System.out.println("optInt1 = " + optInt1);
System.out.println("optInt2 = " + optInt2);
System.out.println(optInt1.equals(optInt2)); // isPresent값이 일치하지 않음 -> false
}
}
/*
출력결과
optStr = abcdef
optInt = 6
result1 = 123
result2 = -1
result3 = 너굴너굴
result4 = 456
true
false
0
optInt1 = OptionalInt[0]
optInt1 = OptionalInt.empty
false
*/
3) 스트림의 최종연산
(1) 반복 실행
- 스트림의 모든 요소에 지정된 작업을 수행
- forEach() - 병렬 스트림의 실행 순서를 보장하지 않음(성능은 더 빠름)
- forEachOrdered() - 병렬 스트림인 경우에도 순서가 보장됨
void forEach(Consumer<? super T> action) // 병렬스트림인 경우순서가 보장되지 않음
void forEachOrdered(Consumer<? super T> action) // 병렬스트림인 경우에도 순서가 보장됨
//.sequential -> 직렬스트림 (기본적으로 스트림은 직렬이라서 생략 가능)
IntStream.range(1, 10).sequential().forEach(System.out::print); // 123456789
IntStream.range(1, 10).sequential().forEachOrdered(System.out::print); // 123456789
//.parallel() -> 병렬스트림 (순서 보장 안됨)
IntStream.range(1, 10).parallel().forEach(System.out::print); // 683295714
IntStream.range(1, 10).parallel().forEachOrdered(System.out::print); // 123456789
(2) 조건검사
- allMatch() - 모든요소가 조건을 만족 하면 true
- anyMatch() - 한 요소라도 조건을 만족 하면 true
- noneMatch() - 모든 요소가 조건을 만족시키지 않으면 true
// predicate(조건식)을 받아서 불리언으로 반환
boolean allMatch (Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키면 true
boolean anyMatch (Predicate<? super T> predicate) // 한 요소라도 조건을 만족시키면 true
boolean noneMatch(Predicate<? super T> predicate) // 모든 요소가 조건을 만족시키지 않으면 true
// ex) 100점 이하일 때 = 낙제자 -> 낙제가가 있는지 확인
boolean hasFailedStu = stuStream.anyMatch(s-> s.getTotalScore()<=100); // 낙제자가 있는지?
(3) 조건에 일치하는 요소 찾기
- .filter()와 함께 사용
- findFirst() - 순차 스트림에 사용 -> 첫 번째 요소를 반환
- findAny() - 병렬 스트림에 사용 -> 아무거나 요소를 반환
Optional<T> findFirst() // 첫 번째 요소를 반환. 순차 스트림에 사용
Optional<T> findAny() // 아무거나 하나를 반환. 병렬 스트림에 사용
//조건을 만족하는 첫번째 요소를 반환 -> 직렬
Optional<Student> result = stuStream.filter(s-> s.getTotalScore() <= 100).findFirst();
//조건을 만족하는 요소를 아무거나 반환 -> 병렬
Optional<Student> result = parallelStream.filter(s-> s.getTotalScore() <= 100).findAny();
(4) 누적연산
- 스트림의 요소를 하나씩 줄여가며 누적연산을(accumulator) 수행
- reduce()의 파라미터 설명
- identity - 초기값(핵심)
- accumulator - 이전 연산결과와 스트림의 요소에 수행할 연산(핵심)
- combiner - 병렬처리된 결과를 합치는데 사용할 연산(병렬 스트림일 때 사용)
Optional<T> reduce(BinaryOperator<T> accumulator) // identity 생략
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner)
// int reduce(int identity, IntBinaryOperator op)
int count = intStream.reduce(0, (a,b) -> a + 1); // count()
int sum = intStream.reduce(0, (a,b) -> a + b); // sum()
int max = intStream.reduce(Integer.MIN_VALUE,(a,b)-> a > b ? a : b); // max()
int min = intStream.reduce(Integer.MAX_VALUE,(a,b)-> a < b ? a : b); // min()
// 위의 sum 스트림의 람다식을 풀어서 작성하면 아래처럼 작성할 수 있음
int a = identity;
for(int b : stream)
a = a + b; // sum()
(5) 실습
import java.util.*;
import java.util.stream.*;
class Ex14_9 {
public static void main(String[] args) {
String[] strArr = {
"Inheritance", "Java", "Lambda", "stream",
"OptionalDouble", "IntStream", "count", "sum"
};
Stream.of(strArr)
.parallel() // 병렬로 처리 -> 순서가 계속 달라짐(forEachOrdered로 출력하면 순서가 유지)
.forEach(System.out::println);
System.out.println();
// 문자열 길이가 0인게 하나도 없는지 확인
boolean noEmptyStr = Stream.of(strArr).noneMatch(s->s.length()==0);
// s로 시작하는 첫번째 요소 찾기(병렬 처리 후 findAny()로 찾으면 아무거나 먼저 찾음)
Optional<String> sWord = Stream.of(strArr)
.filter(s->s.charAt(0)=='s').findFirst();
System.out.println("noEmptyStr="+noEmptyStr);
System.out.println("sWord="+ sWord.get());
// Stream<String>을 Stream<Integer>으로 변환 (s) -> s.length()
Stream<Integer> intStream = Stream.of(strArr).map(String::length);
// Stream<String[]>을 IntStream으로 변환.
// IntStream으로 변환할 때 mapToInt()를 사용(성능이 향상 됨)
// 최종연산은 1번밖에 못써서 Stream을 많이 생성
IntStream intStream1 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream2 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream3 = Stream.of(strArr).mapToInt(String::length);
IntStream intStream4 = Stream.of(strArr).mapToInt(String::length);
int count = intStream1.reduce(0, (a,b) -> a + 1);
int sum = intStream2.reduce(0, (a,b) -> a + b);
OptionalInt max = intStream3.reduce(Integer::max);
OptionalInt min = intStream4.reduce(Integer::min);
System.out.println("count="+count);
System.out.println("sum="+sum);
System.out.println("max="+ max.getAsInt());
System.out.println("min="+ min.getAsInt());
}
}
** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브