728x90
Optional<T>
- T 타입 객체의 래퍼클래스
래퍼클래스 : Integer, Long,...
null을 직접 다루는 것은 위험 → 간접적으로 null 다루기
null 체크(if 문 필수) → 코드가 길어짐
=>Optional 객체에 null을 넣음
public final class Optional<T>{
private final T value; //T타입의 참조 변수. 모든 종류의 객체 저장 가능(null 가능)
...
}
Optional<T> 객체 생성하기
- Optional<T>객체를 생성하는 다양한 방법
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); //가능
- null 대신 빈 Optional<T> 객체를 사용
Optional<String> optVal = null; //null로 초기화. 바람직하지 않음
Optional<String> optVal = Optional.<String>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); //null이면 예외발생
- Optional객체의 값이 null이면 false, 아니면 true를 반환 - isPresent()
if(Optional.ofNullable(str).isPresent(){ //if(str!=null){
System.out.println(str);
}
package chapter14;
import java.util.Optional;
public class Study11_1 {
public static void main(String[] args) {
int[] arr = {};
int[] arr1 = new int[0];
// int[] arr2 = null;
System.out.println("arr.length = " + arr.length);
System.out.println("arr1.length = " + arr1.length);
// System.out.println("arr2.length = " + arr2.length); //NullPointerException 발생
Optional<String> opt = null; //가능은 하지만, 바람직하지 않음
Optional<String> opt1 = Optional.empty();
System.out.println("opt = " + opt);
System.out.println("opt1 = " + opt1);
// System.out.println("opt.get() = " + opt.get()); //NoSuchElementException 발생
// System.out.println("opt1.get() = " + opt1.get()); NoSuchElementException 발생
String str = "";
// str = opt1.get(); //예외발생 → try-catch문 이용
try {
str = opt1.get();
} catch (Exception e) {
str = ""; //예외가 발생하면 빈 문자열("")로 초기화
}
System.out.println("str = " + str);
//try-catch문을 이용하기 보다는 orElse() 메소드 사용
str = opt1.orElse("비어있음"); //Optional에 저장된 값이 null이면 ""반환
System.out.println("str = " + str);
str = opt1.orElseGet(String::new);
System.out.println("str = " + str);
}
}
OptionalInt, OptionalLong, OptionalDouble
- 기본형 값을 감싸는 래퍼클래스 → 성능 때문에 사용
public final class OptionalInt{
...
private final boolean isPresent; //값이 저장되어 있으면 true
private final int value; //int타입의 변수
- OptionalInt의 값 가져오기
Optional<T> T get()
OptionalInt int getAsInt()
OptionalLong long getAsLong()
OptionalDouble double getAsDouble() - 빈 Optional객체와의 비교
OptionalInt opt = OptionalInt.of(0); //OptionalInt에 0을 저장
OptionalInt opt2 = OptionalInt.empty(); //OptionalInt에 0을 저장
System.out.println(opt.isPresent()); //true
System.out.println(opt2.isPresent()); //false
System.out.println(opt.equals(opt2)); //false
package chapter14;
import java.util.Optional;
import java.util.OptionalInt;
public class Study11_2 {
public static void main(String[] args) {
Optional<String> optStr = Optional.of("abcde");
Optional<Integer> optInt = optStr.map(String::length);
System.out.println("optStr = " + optStr.get());
System.out.println("optInt = " + optInt.get());
int result1 = Optional.of("123")
.filter(x->x.length()>0)
.map(Integer::parseInt).get();
int result2 = Optional.of("")
.filter(x->x.length()>0)
.map(Integer::parseInt).orElse(-1);
System.out.println("result1 = " + result1);
System.out.println("result2 = " + result2);
Optional.of("456").map(Integer::parseInt)
.ifPresent(x->System.out.printf("result3 = %d%n", x));
OptionalInt optInt1 = OptionalInt.of(0); //0을 저장
OptionalInt optInt2 = OptionalInt.empty(); //빈 객체를 저장
System.out.println("optInt1.isPresent() = "+optInt1.isPresent());
System.out.println("optInt2.isPresent() = "+optInt2.isPresent());
System.out.println("optInt1.getAsInt() = "+optInt1.getAsInt());
// System.out.println("optInt2.getAsInt() = "+optInt2.getAsInt()); //NoSuchElementException
System.out.println("optInt1 = " + optInt1);
System.out.println("optInt2 = " + optInt2);
System.out.println("optInt1.equals(optInt2) = "+optInt1.equals(optInt2));
}
}
스트림의 최종연산 - forEach()
- 스트림의 모든 요소에 지정된 작업을 수행 - 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); //순서가 보장되지 않는 1~9까지 숫자
IntStream.range(1,10).parallel().forEachOrdered(System.out::print); //123456789
스트림의 최종연산 - 조건 검사
- 조건 검사 - allMatch(), anyMatch(), noneMatch()
boolean allMatch (Predicate<? super T> predicate) //모든 요소가 조건을 만족시키면 true
boolean anyMatch (Predicate<? super T> predicate) //한 요소라도 조건을 만족시키면 true
boolean noneMatch (Predicate<? super T> predicate) //모든 요소가 조건을 만족시키지 않으면 true
- 조건에 일치하는 요소 찾기 - findFirst(), findAny()
Optional<T> findFirst() //첫 번째 요소를 반환. 순차 스트림에 사용
Optional<T> findAny() //아무거나 하나를 반환. 병렬 스트림에 사용
//Optional인 이유? 결과가 null일 수 있어서
스트림의 최종연산 - reduce()
- 스트림의 요소를 하나씩 줄여가며 누적연산 수행
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
U reduce(U identity, BiFunction<U,T,U> accumulator, BinaryOperator<U> combiner)
//identity 초기값, accumulator 이전 연산결과와 스트림의 요소에 수행할 연산, combiner 병렬처리된 결과를 합치는데 사용할 연산(병렬 스트림)
int count = intStream.reduce(0,(a,b)->a+1); //요소개수
int sum = intStream.reduce(0,(a,b)->a+b); //=> int a = identity; for(int b : stream) a=a+b;
int max = intStream.reduce(Integer.MIN_VALUE,(a,b)->a>b?a:b);
int min = intStream.reduce(Integer.MAX_VALUE,(a,b)->a<b?a:b);
package chapter14;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Study12_1 {
public static void main(String[] args) {
String[] strArr = {"Inheritance","Java","Lambda","stream","IntStream","OptionalDouble","count","sum"};
Stream.of(strArr).forEach(System.out::println);
System.out.println();
boolean noEmptyStr = Stream.of(strArr).noneMatch(s->s.length()==0);
Optional<String> sWord = Stream.of(strArr).filter(s->s.charAt(0)=='s').findFirst();
System.out.println("noEmptyStr = " + noEmptyStr);
System.out.println("sWord = " + sWord);
System.out.println();
//Stream<String[]>을 IntStream으로 변환
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);
System.out.println("max.getAsInt() = " + max.getAsInt());
System.out.println("min = " + min);
System.out.println("min.getAsInt() = " + min.getAsInt());
}
}
collect()와 Collectors
- collect는 Collector를 매개변수로 하는 스트림의 최종 연산
Object collect(Collector collector) //Collector를 구현한 클래스의 객체를 매개변수로
- Collector는 수집(collect)에 필요한 메소드를 정의해 놓은 인터페이스
public interface Collector<T,A,R>>{ //T(요소)를 A에 누적한 다음, 결과를 R로 변환해서 반환
Supplier<A> supplier(); //StringBuilder::new 누적할 곳
BiConsumer<A,T> accumulator(); //(sb,s)->sb.append(s) 누적방법
BinaryOperator<A> combiner(); //(sb1,sb2)->sb1.append(sb2) 결합방법(병렬)
Function<A,R> finisher() //sb->sb.toString() 최종변환
Set<Characteristics> characteristics(); //컬렉터의 특성이 담긴 Set을 반환
...
}
- Collectors 클래스는 다양한 기능의 컬렉터(Collector를 구현한 클래스)를 제공
변환 : mapping(), toList(), toSet, toMap(), toCollection(),...
통계 : counting(), summingInt(), averagingInt(), maxBy(), minBy, summarizingInt(),..
문자열 결합 : joining()
리듀싱 : reducing()
그룹화와 분할 : groupingBy(), partitioningBy(), collectingAndThen()
스트림을 컬렉션, 배열로 변환
- 스트림을 컬렉션으로 변환 : toList(), toSet(), toMap(), toCollection()
- 스트림을 배열로 변환 : toArray()
Student[] stuNames = studentStream.toArray(Student[]::new); //가능
Student[] stuNames = studentStream.toArray(); //에러
Object[] stuNames = studentStream.toArray(); //가능
스트림의 통계
- 스트림의 통계정보 제공 : counting(), summingInt(), maxBy(), minBy(),...
스트림을 리듀싱
- 스트림을 그룹별 리듀싱 : reducing()
Collector reducing(BinaryOperator<T> op)
Collector reducing(T identity, BinaryOperator<T> op)
Collector reducing(U identity, Function<T,U> mapper ,BinaryOperator<T> op) //map+reduce
- 문자열 스트림의 요소를 모두 연결 : joining
스트림의 그룹화와 분할
- partitioningBy()는 스트림을 2분할
Collector partitioningBy(Predicate predicate)
Collector partitioningBy(Predicate predicate, Collector downstream)
- groupingBy()는 스트림을 n분할
Collector groupingBy(Function classifier)
Collector groupingBy(Function classifier, Collector downstream)
Collector groupingBy(Function classifier, Supplier mapFactory, Collector downstream)
package chapter14;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;
import static java.util.Comparator.*;
class Student14_1{
String name;
boolean isMale; //성별
int grade;
int ban;
int score;
Student14_1(String name, boolean isMale, int grade, int ban, int score) {
this.name = name;
this.isMale = isMale;
this.grade = grade;
this.ban = ban;
this.score = score;
}
String getName() {return name;}
boolean getIsMale() {return isMale;}
int getGrage() {return grade;}
int getBan() {return ban;}
int getScore() {return score;}
public String toString() {
return String.format("[%s, %s, %d학년 %d반, %3d점]", name,isMale?"남":"여",grade,ban,score);
}
//groupingBy()에서 사용
enum Level{HIGH,MID,LOW}
}
public class Study14_1 {
public static void main(String[] args) {
Student14_1[] stuArr = {
new Student14_1("A", false, 1, 1, 300),
new Student14_1("B", true, 1, 1, 200),
new Student14_1("C", false, 1, 1, 250),
new Student14_1("D", true, 1, 2, 100),
new Student14_1("E", true, 1, 2, 150),
new Student14_1("F", true, 1, 2, 300),
new Student14_1("G", true, 1, 2, 300),
new Student14_1("H", false, 1, 3, 300),
new Student14_1("I", false, 1, 3, 200),
new Student14_1("J", true, 1, 3, 180),
new Student14_1("K", true, 2, 1, 300),
new Student14_1("L", false, 2, 1, 190),
new Student14_1("M", false, 2, 1, 270),
new Student14_1("N", true, 2, 2, 230),
new Student14_1("O", true, 2, 2, 250),
new Student14_1("P", true, 2, 2, 200),
new Student14_1("Q", false, 2, 3, 170),
new Student14_1("R", false, 2, 3, 300),
new Student14_1("S", true, 2, 3, 180)
};
System.out.println("1. 단순그룹화(반별로 그룹화)");
Map<Integer, List<Student14_1>> stuByBan = Stream.of(stuArr)
.collect(groupingBy(Student14_1::getBan));
for(List<Student14_1> ban : stuByBan.values()) {
for(Student14_1 s:ban) {
System.out.println(s);
}
}
System.out.println();
System.out.printf("%n2. 단순그룹화(성적별로 그룹화)%n");
Map<Student14_1.Level, List<Student14_1>> stuByLevel = Stream.of(stuArr)
.collect(groupingBy(s-> {
if(s.getScore() >= 200) return Student14_1.Level.HIGH;
else if(s.getScore() >= 100) return Student14_1.Level.MID;
else return Student14_1.Level.LOW;
}));
TreeSet<Student14_1.Level> keySet = new TreeSet<>(stuByLevel.keySet());
for(Student14_1.Level key : keySet) {
System.out.println("["+key+"]");
for(Student14_1 s : stuByLevel.get(key))
System.out.println(s);
System.out.println();
}
System.out.printf("%n3. 단순그룹화 + 통계(성적별 학생수)%n");
Map<Student14_1.Level, Long> stuCntByLevel = Stream.of(stuArr)
.collect(groupingBy(s-> {
if(s.getScore() >= 200) return Student14_1.Level.HIGH;
else if(s.getScore() >= 100) return Student14_1.Level.MID;
else return Student14_1.Level.LOW;
}, counting()));
for(Student14_1.Level key : stuCntByLevel.keySet())
System.out.printf("[%s] - %d명, ", key, stuCntByLevel.get(key));
System.out.println();
/*
for(List<Student14_1> level : stuByLevel.values()) {
System.out.println();
for(Student s : level) {
System.out.println(s);
}
}
*/
System.out.printf("%n4. 다중그룹화(학년별, 반별)%n");
Map<Integer, Map<Integer, List<Student14_1>>> stuByGradeAndBan =
Stream.of(stuArr)
.collect(groupingBy(Student14_1::getGrage,
groupingBy(Student14_1::getBan)
));
for(Map<Integer, List<Student14_1>> hak : stuByGradeAndBan.values()) {
for(List<Student14_1> ban : hak.values()) {
System.out.println();
for(Student14_1 s : ban)
System.out.println(s);
}
}
System.out.printf("%n5. 다중그룹화 + 통계(학년별, 반별 1등)%n");
Map<Integer, Map<Integer, Student14_1>> topStuByHakAndBan = Stream.of(stuArr)
.collect(groupingBy(Student14_1::getGrage,
groupingBy(Student14_1::getBan,
collectingAndThen(
maxBy(comparingInt(Student14_1::getScore)),
Optional::get
)
)
));
for(Map<Integer, Student14_1> ban : topStuByHakAndBan.values())
for(Student14_1 s : ban.values())
System.out.println(s);
System.out.printf("%n6. 다중그룹화 + 통계(학년별, 반별 성적그룹)%n");
Map<String, Set<Student14_1.Level>> stuByScoreGroup = Stream.of(stuArr)
.collect(groupingBy(s-> s.getGrage() + "-" + s.getBan(),
mapping(s-> {
if(s.getScore() >= 200) return Student14_1.Level.HIGH;
else if(s.getScore() >= 100) return Student14_1.Level.MID;
else return Student14_1.Level.LOW;
} , toSet())
));
Set<String> keySet2 = stuByScoreGroup.keySet();
for(String key : keySet2) {
System.out.println("["+key+"]" + stuByScoreGroup.get(key));
}
}
}
728x90