[Java] 스트림
스트림이란?
배열이나 Collections에 저장된 데이터를 핸들링하는 것은 for문이나 for-each 구문을 통해서 구현해왔다. 스트림은 자바8에서부터 도입된 개념으로 이러한 반복 작업을 간결하게 처리할 수 있다.
예제
Integer List에서 2보다 큰 수가 몇 개 존재하는지 출력하는 프로그램을 작성한다고 가정한다.
기존의 자바에서는 다음과 같이 값을 저장할 변수와 for문을 통한 반복으로 개수를 카운팅할 수 있다.
public class Test {
public static void main(String[] args) {
List<Integer> mList = new ArrayList<>();
mList.add(1);
mList.add(2);
mList.add(3);
mList.add(4);
mList.add(5);
int cnt = 0;
for(int cur : mList){
if(cur > 2){
cnt++;
}
}
System.out.println(cnt);
}
}
스트림을 사용하면 for문을 다음과 같이 한 줄로 대체할 수 있다.
mList.stream().filter(cur -> cur > 2).count()
스트림 활용
스트림은 아래와 같이 세 가지 영역으로 구분할 수 있다.
mList.stream() .filter(cur -> cur > 2) .count()
- ㅤ : 스트림의 생성
- ㅤ : 중개 연산(Intermediate Operations)
- ㅤ : 종료 연산(Terminal Operation)
스트림 생성 후, 중개 연산을 통해 적절한 가공을 하고 종료 연산을 통해 최종 결과를 도출해낸다. 아래에서 항목에 어떤 기능이 있는지 살펴본다.
-
스트림의 생성
// 배열 > Stream int[] myArr = {1, 2, 3}; Arrays.stream(myArr);
// Collection > Stream List<Integer> myList = new ArrayList<>(); ... myList.stream();
-
중개 연산
int[] arr = {3, 2, 1, 4, 5};
// filter : 조건에 맞는 데이터만 선택한다. Arrays.stream(arr).filter(c -> (c > 2)) // [3, 4, 5]
// map : 각 요소에 특정 연산을 해준다. Arrays.stream(arr).map(c -> c + 1) // [4, 3, 2, 5, 6]
// peek : 그냥 중간 결과를 확인하는 용도이며 단말 연산에 영향을 미치지 않는다. Arrays.stream(arr).peek(c -> System.out.println(c)).toArray(); // [3, 2, 1, 4, 5] 그대로 출력
// limit : 결과의 개수를 제한할 수 있음 Arrays.stream(arr).limit(2) // [3, 2]
// skip : 첫 번째 요소부터 주어진 수 만큼 무시 Arrays.stream(arr).skip(2) // [1, 4, 5]
// sorted : 현재 스트림의 내용을 정렬 한다. Arrays.stream(arr).sorted() // [1, 2, 3, 4, 5]
// distinct : 중복을 제거한다. int[] arr = {3, 2, 1, 4, 5, 1, 1}; Arrays.stream(arr).distinct() // [3, 2, 1, 4, 5]
// flatmap : 중첩 구조 제거 List<List<Integer>> mList = new ArrayList<>(); Integer[] arr1 = {1, 2}; mList.add(Arrays.asList(arr1)); Integer[] arr2 = {3, 4}; mList.add(Arrays.asList(arr2)); // 1차원으로 1, 2, 3, 4 출력 mList.stream().flatMap(_1List -> _1List.stream()).forEach(c-> System.out.println(c));
-
종료 연산
List<Integer> mList = new ArrayList<>(); mList.add(1); mList.add(2); mList.add(3); mList.add(4); mList.add(5);
// toArray : array로 결과 반환 int[] arr = mList.stream().mapToInt(n->n).toArray();
// collect : 값을 다시 모아서 적절한 형태로 준다. // Collectors 인터페이스에 정의된 toSet, toMap, toList 사용 가능하다. Set<Integer> mSet = mList.stream().collect(Collectors.toSet());
// count : 개수를 리턴한다 long cnt = mList.stream().count(); // 5
// IntStream, DoubleStream, LongStream에만 적용 // min : 최소 값 OptionalInt min = mList.stream().mapToInt(n->n).min(); min.ifPresent(System.out::println); // max : 최대 값 OptionalInt min = mList.stream().mapToInt(n->n).max(); min.ifPresent(System.out::println); // average : 평균 값 OptionalDouble min = mList.stream().mapToInt(n->n).average(); min.ifPresent(System.out::println);
// forEach : 순회 mList.stream().forEach(c-> System.out.println(c)); // forEachOrdered : 병렬 스트림을 사용했을 때 forEach는 순서를 보장하지 못함. mList.stream().forEachOrdered(c-> System.out.println(c));
// reduce : binaryOperator를 인자로 받아서 하나의 결과를 리턴한다. // 이 경우 1 - 2 - 3 - 4 - 5 >> -13 Optional<Integer> ret = mList.stream().reduce((a, b) -> a-b); ret.ifPresent(System.out::println);
// anyMatch : 만족하는 요소가 하나라도 존재하는가? boolean ret = mList.stream().anyMatch(c -> c>2); // true // allMatch : 모든 요소가 만족하는가? boolean ret = mList.stream().anyMatch(c -> c>2); // false boolean ret = mList.stream().anyMatch(c -> c>0); // true // noneMatch : 만족하는 요소가 하나도 없는가? boolean ret = mList.stream().anyMatch(c -> c>2); // false boolean ret = mList.stream().anyMatch(c -> c>10); // true
// findFirst : 만족하는 첫번째 값 반환 Optional<Integer> ret = mList.stream().filter(c -> c > 2).findFirst(); ret.ifPresent(System.out::println); // findAny : 만족하는 아무 값 반환 (싱글 스레드 환경에서는 findFirst와 같은 동작) Optional<Integer> ret = mList.stream().filter(c -> c > 2).findAny(); ret.ifPresent(System.out::println);
Leave a comment