[Java] java8 stream
java8부터 지원이 된 stream은 람다함수형식으로 간결하고 직관적으로 요소들을 처리가 가능한 기능이다.
이러한 stream을 이용해 for문 등 직관적이지 않은 처리들을 깔끔하게 처리할 수 있다.
Stream의 구조
stream의 구조는 크게 세가지로 볼 수 있다.
1. stream의 생성
2. 중개 연산
3. 최종 연산
세가지로 구성되있으며, 중개연산은 연산결과를 stream형태로 반환하기 때문에 연속적으로 사용할 수 있다.
대략적인 구조는
데이터소스객체집합.stream생성().중개연산().최종연산();
이다.
Stream 생성
스트림 api는 다음과 같은 다양한 종류의 소스들에 사용할 수 있다.
1. 컬렉션
2. 배열
3. 가변 매개변수
4. 지정된 범위의 연속된 정수
5. 특정 타입의 난수
6. 람다 표현식
7. 파일
8. 빈 스트림
중개 연산
중개 연산은 stream을 받아 stream형식으로 리턴하기 때문에 중개 연산을 연속으로 사용할 수 있다.
stream의 중개연산은 필터-맵(filter-map)기반의 api를 사용함으로서 지연(lazy) 연산을 통해 성능을 최적화할 수 있다.
대표적인 중개 연산들은
1. stream 필터링 : filter(), distinct()
2. stream 변환 : map(), flatMap()
3. stream 제한 : limit(), skip()
4. stream 정렬 : sorted()
5. stream 연산 결과 확인 : peek()
예시
ArrayList<Int> list = new ArrayList<>(Arrays.asList(1, 5, 3, 5, 4, 5));
//distinct 중복 제거
list.stream().distinct().forEach(System.out::print);
// 1534
//filter 해당하는것만 필터링
list.stream().filter(n -> n % 2 != 0).forEach(e -> System.out.print(e + " "));
// 1 5 3 5 5
ArrayList<String> list = new ArrayList<>(Arrays.asList("Drogba", "Essien", "Terry", "Lampard"));
//map 스트림의 요소들을 인수로 전달하여, 반환값들로 새로운 스트림을 변환
list.stream().map(String::toUpperCase()).forEach(System.out::println);
// DROGBA ESSIEN TERRY LAMPARD
ArrayList<String> list = new ArrayList<>(Arrays.asList(
"I love Drogba", "I want to be Essien", "Terry is nice guy", "Lampard is best coach"));
//flatmap 이차배열이나, 여럿문자열이 저장된 스트림을 단일스트림으로 변환
list.stream().map(String::toUpperCase()).forEach(System.out::print);
//IloveDrogbaIwanttobeEssienTerryisnoceguyLampardisbestcoach
ArrayList<Int> list = new ArrayList<>(Arrays.asList(1, 5, 3, 5, 4, 5));
//limit 앞요소 제거
list.stream().skip(2).forEach(System.out::print);
// 3545
//filter 뒷요소 제거
list.stream().limit(3).forEach(System.out::print);
// 153
최종 연산
최종 연산은 앞서 중개 연산을 통해 만들어진 stream에 있는 요소들에 대해 마지막으로 각 요소를 소모하며 최종 결과를 보여준다. 즉 지연(lazy)되었던 모든 중개연산들이 최종 연산 시에 모두 수행된다. 이렇게 최종 연산시에는 모든 요소를 소모한 해당 stream은 더이상 사용할수없다.
대표적인 최종연산 메소드들은 다음과 같다.
1. 요소의 출력 : forEach()
2. 요소의 소모 : reduce()
3. 요소의 검색 : findFirst(), findAny()
4. 요소의 검사 : anyMatch(), allMatch(), noneMatch()
5. 요소의 통계 : count(), min(), max()
6. 요소의 연산 : sum(), average()
7. 요소의 수집 : collet()
예시
//findFirst(), findAny() output 형식 Optional
ArrayList<String> list = new ArrayList<>(Arrays.asList("넷", "둘", "하나", "셋"));
Optional<String> first = list.stream().filter(s -> s.stratsWith("넷")).findFirst();
//first.get() -> 넷
Optional<String> any = list.stream().filter(s -> s.stratsWith("넷")).findAny();
//any.get() -> 넷
//findFirst, findAny 차이 -> findAny는 병렬처리 첫번째값 리턴으로 매 리턴마다 값이 다를 수 있다.
//allMatch(), anyMatch(), noneMatch() output 형식 boolean
ArrayList<Int> list = new ArrayList<>(Arrays.asList(3, 7, 5, 4, 4, 2, 9));
boolean result = list.stream().allMatch(a -> a%2 == 0);
//false 모든값이 해당하면 true
boolean result = list.stream().anyMatch(a -> a%2 == 0);
//true 해당값 하나라도 있으면 true
boolean result = list.stream().noneMatch(a -> a%6 == 0);
// true 모든값이 해당되지 않으면 true
//collect()
ArrayList<Int> list = new ArrayList<>(Arrays.asList(3, 7, 5, 4, 4, 2, 9));
List<String> list = list.stream().filter(n -> n % 2 ! = 0).collect(Collectors.toList()); // 스트림을 리스트로 변환
//Collectors 클래스의 partitioningBy()
ArrayList<String> list = new ArrayList<>(Arrays.asList("넷", "둘", "하나", "셋"));
//해당 stream의 각 요소별 글자 수에 따라 홀수와 짝수로 나누어 저장
Map<Boolean, List<String>> partition = list.stream().collect(Collectors.partitioningBy(s -> (s.length() % 2) == 0));
List<String> oddLengthList = patition.get(false);//넷, 둘, 셋
List<String> evenLengthList = patition.get(true);//하나