Dev/Java

[Java] stream() 활용

pu3vig 2022. 9. 23. 16:24
728x90
  • target: 스트림을 활용한 필터링

 


  • method: 

1. 필터링

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3);

// 고유 요소 필터링
// filter에 해당하는(=Predicate) 요소만 전체 스트림에서 반환
// Predicate는 return 형태가 boolean 형태인 함수
List<Integer> evenList = numbers.stream()
				.filter(i -> i % 2 == 0)
				.distinct()
				.collect(toList());
// evenList = [2, 4, 6, 8]

List<Board> noticeList = boardList.stream()
				.filter(Board::noticeYn)
				.collect(toList());

※ distinct()의 경우, 중복을 제거하기 위해 추가됨


// 일반적인 고유 요소 필터링의 경우 전체 요소에 대해 Predicate를 적용
// 정렬이 되어져 있는 경우, false가 발생한 위치부터 중단할 수 있기에 크기가 큰 스트림의 경우 시간 절약이 가능(takeWhile, dropWhile 활용)

// takeWhile의 경우, Predicate에서 처음 false가 나온 요소 이전의 모든 요소를 반환하고 종료
List<Integer> numbers = Arrays.asList(1, 5, 7, 10, 15, 20, 3, 9);
List<Integer> list1 = numbers.stream()
                    	.takeWhile(n -> n < 10)
                    	.collect(toList());
// list1 = [1, 5, 7]

// dropWhile의 경우, Predicate에서 처음 false가 나온 요소를 모두 버리고, 처음 false가 나온 요소를 포함한 나머지 요소를 반환하고 종료
List<Integer> list2 = numbers.stream()
                    	.dropWhile(n -> n < 10)
                    	.collect(toList());
// list2 = [10, 15, 20, 3, 9]

// limit와 skip을 통해서도 필터링이 가능
List<Integer> numbers = Arrays.asList(1, 5, 7, 10, 15, 20, 3, 9);
List<Integer> list1 = numbers.stream()
                    	.takeWhile(n -> n < 10)
                        .limit(2)
                    	.collect(toList());      
// list1 = [1, 5]

List<Integer> numbers = Arrays.asList(1, 5, 7, 10, 15, 20, 3, 9);
List<Integer> list2 = numbers.stream()
                    	.takeWhile(n -> n < 10)
                        .skip(1)
                    	.collect(toList());
// list2 = [5, 7]

※ limit은 predicate에 부합하는 요소들의 수를 순차적으로 맨 앞에서 제한할 개수

※ skip은 predicate에 부합하는 요소들 중 순차적으로 건너뛸 개수


2. 매핑

// 스트림을 map형태로 변형하여 함수에 해당하는 새로운 리스트 생성
List<String> words = Arrays.asList("hi", "hello", "bye");
List<Integer> wordLengths = words.stream()
                        	.map(String::length)
                        	.collect(toList());
// [2, 5, 3]

// 객체를 Stream으로 받아 각 객체의 특정 변수정보를 추출 가능
List<String> boardTypeList = boardList.stream()
                            	.map(Board::getType)
                            	.distinct()
                            	.collect(toList());
// ["NOTICE", "NORMAL", "ANONYMOUS", "Q&A"]

// flatMap을 이용하여 map의 처리결과를 stream형태로 변환
List<String> words = Arrays.asList("hi", "hello", "bye");
List<Integer> lengths = words.stream()
                            .map(w -> w.split(""))		// Stream<String[]>
                            .flatMap(Arrays::stream)	// Stream<String>
                            .distinct()					// 중복제거
                            .collect(toList());			// List<String>
// ["h", "i", "e", "l", "o", "b", "y"]

3. 정렬

// sorted(Comparator.reverseOrder())와 동일
List<Integer> numbers = Arrays.asList(1, 5, 7, 10, 2, 3, 6, 4, 8, 9);
List<Integer> sortedList = numbers.stream()
                        	.sorted((n1, n2) -> Integer.compare(o2, o1))
                        	.collect(toList());
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

※ 클래스 구현 시, Comparable을 구현하지 않은 경우, ClassCastException 발생

※ Parameter를 주지 않으면, Comparable을 구현한대로 정렬되고, 구현하지 않거나, 다른 정렬방법을 사용하고 싶은 경우, Comparator를 구현


4. 루핑

// peek은 스트림을 순차적으로 전체 요소 검색하면서 추가작업 시 사용

// 중간 처리 메소드로 처리 이후 스트림 반환
List<Integer> numbers = Arrays.asList(1,2,3,4,5);
numbers.stream().forEach(System.out::println);

/* 출력
1
2
3
4
5
*/

// 중간 처리 메소드 이기에 마지막에 호출하면 스트림 동작 X
// numbers.stream().forEach(System.out::println).collect(toList());

5. 매칭

// anyMatch(적어도 1개)
// allMatch(모두)
// noneMatch(하나도)가 존재
// match 메소드들은 스트림 쇼트서킷 기법(&&, ||와 같은 논리연산)을 활용한다.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream().anyMatch(d -> d % 2 == 0);			// true (||)

numbers.stream().allMatch(d -> d % 2 == 0);			// false (&&)
numbers.stream().allMatch(d -> d < 6);				// true

numbers.stream().noneMatch(d -> d % 2 == 0);			// false (&&)
numbers.stream().noneMatch(d -> d > 6);				// true

※ &인 경우, 앞서 한개만 false여도 뒤의 연산을 처리하지 않고도 즉시 false 반환, || 인 경우, 앞서 한개만 true여도 바로 true 반환

위와 같이 모든 요소에 대해 조건을 대입해보지 않아도 즉시 결과 도출이 가능한 기법을 쇼트서킷 기법이라 함


6. 검색

/*
java.util.Optional<T>
값의 존재 여부를 표현하는 컨테이너 클래스로 아무 요소도 반환하지 않을 경우 null
java.lang.NullPointerException 처리 필요
*/

// findAny(임의의 요소 반환)/findFirst(Predicate에 해당하는 첫번째 요소 반환)
// 병렬성으로 인하여 두 메소드가 모두 존재하며, 순서가 중요치 않다면 제약이 적은 findAny 사용

List<Integer> numbers1 = Arrays.asList(2,5,3,4,1);
Optional<Integer> number1 = numbers.stream()
                        	.filter(d -> d > 3)
                        	.findAny();
// Optional[5]

List<Integer> numbers2 = Arrays.asList(1,2,3,4,5);
Optional<Integer> number2 = numbers.stream()
                        	.filter(d -> d > 3)
                        	.findAny();
// Optional[4]


// findFirst
// 생성된 스트림에서 논리적인 아이템의 순서가 명확한 경우, 첫번째 요소 반환
List<Integer> numbers1 = Arrays.asList(1,2,3,4,5);
Optional<Integer> number = numbers.stream()
                        	.filter(d -> d > 3)
                        	.findFirst();
// Optional{4]

7. 리듀싱

// 대량의 데이터를 가공해 축소하는 Reduction으로 fold라고 불림
// reduce([초기값], Operator)
// 초기값은 생략이 가능하며, 생략한 경우, 결과값은 Optional로 리턴

List<Integer> numbers = Arrays.asList(1,2,3,4);

int sum = numbers.stream()
		.reduce(0, (a, b) -> a + b);				// 10

// 초기값 설정 안한 경우
Optional<Integer> OptionalSum = numbers.stream()
					.reduce((a, b) -> a + b);	// Optional[10]
                
Optional<Integer> min = numbers.stream()
				.reduce(Math::min);			// Optional[1]

7.1. 집계 메소드

// 기본적인 집계 메소드
// sum(), count(), average(), max(), min() 등을 사용하여 집계

int sum = IntStream.range(0, 5).sum();	// sum({1,2,3,4}) = 10

7.2. 기본형 특화 스트림 (Primitive Stream Specialization)

// 일반적인 방식
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

int sum = numbers.stream()
		.reduce(0, Integer::sum);
        
// 위의 경우, Integer 타입을 기본형으로 언박싱하여 sum하기 때문에 박싱비용이 추가됨
// 스트림은 객체 타입을 사용하기 때문에, 객체의 합은 계산할 수 없기에 sum() 메소드를 제공하지 않음
// 이에 숫자 스트림을 처리할 수 있는 기본형 특화 스트림(Primitive Stream Specialization)을 제공
// 오직 박싱 효율성만 관련이 있고, 숫자 리듀싱 연산 수행 메서드만을 제공

※ 위의 경우, Integer 타입을 기본형으로 언박싱하여 sum을 실행하기에 박싱비용이 추가됨

※ 스트림은 객체 타입을 사용하기 때문에, sum() 메소드를 제공하지 않음

※ 숫자 스트림을 처리할 수 있는 기본형 특화 스트림을 제공

※ 오직 박싱 효율성만 관련이 있고, 숫자 리듀싱 연산 수행 메서드만을 제공

 

// 숫자 스트림으로 변환
// mapToInt, mapToDouble, mapToLong을 통해 기본형 특화 스트림으로 변환

int sum = numbers.stream()
		.mapToInt(Integer::parseInt)
		.sum();
        
// 객체 스트림으로 복원
// boxed()
숫자 스트림에서 다시 객체 스트림으로 변경하고 싶을 때 사용
numbers.stream()			// Stream<Integer>
	.mapToInt(Integer::parseInt)	// IntStream
	.boxed();			// Stream<Integer>

※ IntStream과 같은 기본형 배열을 source로 하는 스트림 생성 관련 정보는 아래에서 확인

https://pu3vig.tistory.com/74

 

자바의 정석 - 스트림(Stream)

target: 자바의 정석 - 스트림(Stream) & 람다식(Lambda Expression) method: 1. 자바의 정석 - 스트림(Stream) 자바의 정석(남궁성 저) 2권 학습내용 정리 1. 스트림 스트림은 데이터 소스를 추상화하고, 데이터를

pu3vig.tistory.com


8. 무한 스트림

  • Stream.iterate, Stream.generate를 이용하여 무한 스트림 생성
  • limit()와 함께 사용하는게 일반적

8.1. iterate

// Stream.iterate([초기값], Operator)
// 기존 결과에 의존해 순차적으로 연산하고, 끝이 없기 때문에 Unbound Stream 이라고 함

// Stream.iterate([초기값], Predicate, Operator)
// Predicate에 충족할 때까지 수행 (for문의 조건문과 동일)
// 쇼트서킷을 제공하는 메서드와 함께 사용하면, 불만족 시 즉시 종료되도록 사용 가능

Stream.iterate(0, n -> n <= 100, n -> n + 10);	// 0, 10, 20, ..., 100

8.2. generate

// Stream.generate(Supplier<T>)
// Supplier를 인자로 받아 새로운 값 생성
IntStream.generate(() -> 1);	// 1, 1, 1, 1, ...

 


  • source:

https://girawhale.tistory.com/127

 

[Java] 스트림(Stream) 활용

필터링 filter filter 메소드는 Predicate를 인수로 받아 true를 반환하는 요소만을 포함하는 스트림을 반환한다 List numbers = Arrays.asList(1, 2, 3, 4, 5); numbers.stream() .filter(i -> i % 2 == 0) .for..

girawhale.tistory.com

 

728x90