Stream
List<String> names = new ArrayList<>();
names.add("Kang");
names.add("Min");
names.add("Hyeong");
- Sequence of elements supporting sequential and parallel aggregate operations
- 데이터를 담고 있는 저장소(컬렉션)이 아니다
- Funtional in nature, 스트림이 처리하는 데이터 소스를 변경하지 않는다
Stream<String> stringNames1 = names.stream().map(String::toUpperCase);
names.forEach(System.out::println);
- 스트림으로 처리하는 데이터는 오직 한번만 처리한다
Stream<String> stringNames1 = names.stream().map(String::toUpperCase);
stringNames1.forEach(System.out::println);
stringNames1.forEach(System.out::println);
- 무제한일 수도 있다 -> short circuit 메소드를 사용해서 제한할 수 있다.
- 중개 오퍼레이션은 근본적으로 lazy하다.
Stream<String> stringNames2 = names.stream().map(s -> {
System.out.println(s); // 출력이 되지 않음
return s.toUpperCase();
});
List<String> stringNames3 = names.stream().map(s -> {
System.out.println(s); // 출력이 됨
return s.toUpperCase();
}).collect(Collectors.toList()); // 종료형 오퍼레이터를 실행한 이후에는 stream 안에 내용이 처리가 된다.
- 손쉽게 병렬처리할 수 있다.
List<String> collect = names.parallelStream().map(s -> {
System.out.println(s + " " + Thread.currentThread().getName());
return s.toUpperCase();
}).collect(Collectors.toList()); // 병렬처리
System.out.println("=================================================");
List<String> collect2 = names.stream().map(s -> {
System.out.println(s + " " + Thread.currentThread().getName());
return s.toUpperCase();
}).collect(Collectors.toList());
Stream 파이프라인
- 0 또는 다수의 중개 오퍼레이션과 한개의 종료 오퍼레이션으로 구성한다.
- Stream의 데이터 소스는 오직 터미널 오퍼레이션을 실행할 때에만 처리한다.
중개 오퍼레이션
- Stream을 리턴한다.
- Stateless / Stateful 오퍼레이션으로 더 상세하게 구분할 수도 있다. -> 대부분은 Stateless지만 distinct나 sorted처럼 이전 소스 데이터를 참조해야 하는 오퍼레이션은 Stateful 오퍼레이션이다
- filter, map, limit, skip, sorted, ...
종료 오퍼레이션
- Stream을 리턴하지 않는다.
- collect, allMatch, count, forEach, min, max, ...
Stream API
걸러내기 | filter |
변경하기 | map, flatMap |
생성하기 | generate, Iterate |
제한하기 | limit, skip |
조건확인 | anyMatch, allMatch, nonMatch |
개수 새기 | count |
스트림 데이터를 하나로 뭉치기 | reduce, collect, sum, max, min |
Stream을 이용한 예제
public class StreamApp2 {
public static void main(String[] args) {
List<OnlineClass> springClasses = new ArrayList<>();
springClasses.add(new OnlineClass(1, "spring boot", true));
springClasses.add(new OnlineClass(2, "spring data jpa", true));
springClasses.add(new OnlineClass(3, "spring mvc", false));
springClasses.add(new OnlineClass(4, "spring core", false));
springClasses.add(new OnlineClass(5, "rest api development", false));
System.out.println("spring 으로 시작 하는 수업");
springClasses.stream()
.filter(oc -> oc.getTitle().startsWith("spring"))
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("close 되지 않은 수업");
springClasses.stream()
.filter(oc -> !oc.isClosed())
// .filter(Predicate.not(OnlineClass::isClosed))
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("수업 이름만 모아서 스트림 만들기");
springClasses.stream()
.map(OnlineClass::getTitle)
.forEach(System.out::println);
List<OnlineClass> javaClasses = new ArrayList<>();
javaClasses.add(new OnlineClass(6, "The Java, Test", true));
javaClasses.add(new OnlineClass(7, "The Java, Code manipulation", true));
javaClasses.add(new OnlineClass(8, "The Java, 8 to 11", false));
List<List<OnlineClass>> keesunEvents = new ArrayList<>();
keesunEvents.add(springClasses);
keesunEvents.add(javaClasses);
System.out.println("두 수업 목록에 들어있는 모든 수업 아이디 출력");
keesunEvents.stream()
.flatMap(Collection::stream)
.forEach(oc -> System.out.println(oc.getId()));
System.out.println("10부터 1씩 증가하는 무제한 스트림 중에서 앞에 10개 빼고 최대 10개 까지만");
Stream.iterate(10, i -> i + 1)
.skip(10)
.limit(10)
.forEach(System.out::println);
System.out.println("자바 수업중에 Test가 들어있는 수업이 있는지 확인");
boolean test = javaClasses.stream()
.anyMatch(oc -> oc.getTitle().contains("Test"));
System.out.println(test);
System.out.println("스프링 수업 중에 제목에 spring이 들어간 제목만 모아서 List로 만들기");
List<String> spring = springClasses.stream()
.map(OnlineClass::getTitle)
.filter(title -> title.contains("spring"))
.collect(Collectors.toList());
spring.forEach(System.out::println);
}
}
Optional
오직 값 한 개가 들어있을 수도 없을 수도 있는 컨테이너
자바 프로그래밍에서 NullPointerException을 종종 보게되는 이유 -> null을 리턴함 && null 체크를 깜박해서
메소드에서 작업중 특별한 상황에서 값을 제대로 리턴할 수 없는 경우 선택할 수 있는 방법
- 예외를 던진다 -> 비싸다 / stackTrace를 찍기때문
- null을 리턴한다 -> 비용문제는 없지만 그 코드를 사용하는 클라이언트 코드가 주의해야함
- Optional을 리턴한다 -> 코드에 명시적으로 빈 값일수도 있다는 걸 알려주고 빈값인 경우데 대한 처리를 강제한다
주의점
- 리턴값으로만 쓰기를 권장한다 -> 메소드 매개변수 타입, 맵의 키 타입, 인스턴스 필드 타입으로 쓰지 말자
private Optional<Progress> progress2; // 이런건 쓰지 말자
- Optional을 리턴하는 메소드에서 null을 리턴하지 말자
OnlineClass spring_boot = new OnlineClass(1, "spring boot", false);
Duration studyDuration1 = spring_boot.getProgress().getStudyDuration(); // 에러 발생 -> null이 리턴되기 때문
- 프리미티브 타입용 Optional은 따로있다. -> OptionalInt, OptionalLong
- Collection, Map, Stream Array, Optional은 Optional로 감싸지 말 것
Optional API
Optional 만들기 | Optional.of(), Optional.ofNullable(), Optional.empty() |
Optional에 값이 있는지 없는지 확인 | isPresent(), isEmpty() |
Optional에 있는 값 가져오기 | get() |
Optional에 있는 값이 있는 경우 그 값을 가지고 ~~을 하라 | ifPresent(Consumer) |
Optional에 값이 있느면 가져오고 없는 경우에 ~~을 하라 | orElseGet(Supplier) |
Optional에 값이 있으면 가져오고 없는 경우에 ~~을 리턴하라 | orElse(T) |
Optional에 값이 있으면 가져오고 업는 경우에 에러를 던져라 | orElseThrow() |
Optional에 들어있는 값 걸러내기 | Optional filter(Predicate) |
Optional에 들어있는 값 변환하기 | Optional map(Function), Optional flatMap(Function): Optional |
Date와 Time API
Java 8에 새로운 날짜와 시간 API가 생긴 이유
- Java 8 이전에 사용하던 Date 클래스는 mutable 하기 때문에 thread safe하지 않다
date.setTime(1032412412434L); // 시간을 다시 설정함
- 클래스 이름이 명확하지 않다 -> Date인데 시간까지 다룬다
Date date = new Date();
System.out.println(date);
System.out.println(date.getTime()); // Date인데 time을 가져옴...
- 버그가 발생할 여지가 많다 -> 타입 안정성이 없고 월이 0부터 시작..
Calendar birthDate1 = new GregorianCalendar(1992, 9, 26); // 1992년 10월 26일, type safe하지 않다
Calendar birthDate2 = new GregorianCalendar(1992, Calendar.OCTOBER, 26);
System.out.println(birthDate2.getTime()); // date가 나옴...
- 날자 시간 처리가 복잡한 애플리케이션에서는 보통 Joda Time을 쓰곤 했다.
주요 API
- 기계용 시간과 사람용 시간으로 나눌 수 있다.
- 기계용 시간은 EPOCK(1970-01-01 00:00:00)부터 현재까지의 타임스탬프를 표현한다
- 사람용 시간은 연,월,일,시,분,초 등을 표현한다
- 타임스탬프는 Instant를 사용한다
- 특정 날짜(LocalDate), 시간(LocalTime), 일시(LocalDateTime)를 사용 할 수 있다
- 기간을 표현할 때는 Duration(시간 기반)과 Period(날짜 기반)를 사용 할 수 있다
- DateTimeFormatter를 사용해서 일시를 특정한 문자열로 포매팅 할 수 있다.
Date와 Time API
지금 이 순간을 기계 시간으로 표현
Instant instant = Instant.now();
System.out.println(instant); // 기준시 UTC, GMT
System.out.println(instant.atZone(ZoneId.of("UTC"))); // 기준시 UTC, GMT
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());
System.out.println(zonedDateTime);
인류용 일시를 표현하는 방법
LocalDateTime now = LocalDateTime.now(); // 내 local에 맞게 설정 -> 만약 서버를 다른 지역에 올리면 다른 시간이 뜰거임
System.out.println(now);
ZonedDateTime nowInKorea = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
System.out.println(nowInKorea);
LocalDate today = LocalDate.now();
LocalDate thisYearBirthday = LocalDate.of(2021, Month.OCTOBER, 26); // 특정 일시를 리턴
기간을 표현하는 방법
Period period = Period.between(today, thisYearBirthday);
System.out.println(period.getDays());
Period until = today.until(thisYearBirthday);
System.out.println(until.get(ChronoUnit.DAYS));
파싱, 포매팅
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter MMddyyyy = DateTimeFormatter.ofPattern("MM/dd/yyyy");
System.out.println(dateTime.format(MMddyyyy));
LocalDate parse = LocalDate.parse("10/26/1992", MMddyyyy);
System.out.println(parse);
레거시 API 지원
- GregorianCalendar와 Date 타입의 인스턴스를 Instant나 ZonedDateTime으로 변환 가능
- java.util.Timezone에서 java.time.ZonedId로 상호 변환 가능
GregorianCalendar gregorianCalendar = new GregorianCalendar();
ZonedDateTime dateTime1 = gregorianCalendar.toInstant().atZone(ZoneId.systemDefault());
System.out.println(dateTime1);
GregorianCalendar from = GregorianCalendar.from(dateTime1);
System.out.println(from.getTimeZone().getDisplayName());
ZoneId zoneId = TimeZone.getTimeZone("PST").toZoneId();
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
System.out.println(timeZone.getDisplayName());
참고자료
더 자바, Java 8 - 인프런 | 강의
자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이
www.inflearn.com
'java' 카테고리의 다른 글
Design Patterns - Singleton (0) | 2021.11.11 |
---|---|
함수형 인터페이스를 이용한 Builder (0) | 2021.05.29 |
더 자바(java 8) - CompletableFuture (0) | 2021.05.26 |
더 자바(java 8) - 인터페이스의 변화 (0) | 2021.05.11 |
더 자바(java 8) - 함수형 인터페이스와 람다 (0) | 2021.05.10 |
댓글