프로그래밍 언어/Java

[Java] 자바 스트림 중간 연산, 최종 연산

happy_life 2022. 7. 6. 15:38

자바 스트림 연산

 

스트림의 연산

 스트림은 다양한 연산으로 복잡한 작업을 간단하게 처리할 수 있게 해준다. 스트림의 연산에는 중간 연산과 최종 연산이 있는데 중간 연산은 연산결과를 스트림으로 반환해 계속 연속으로 연결할 수 있다. 반면 최종 연산은 스트림의 요소를 소모하면서 연산을 하므로 단 한번만 연산이 가능하다. 스트림의 연산은 "지연된 연산"이라는 특징을 갖는다. 최종 연산이 수행되기 전까지는 중간 연산이 수행되지 않는다는 것이다.

 

 

스트림의 중간 연산

중간 연산 설명
Stream<T> distinct() 중복 제거
Stream<T> filter() 조건에 안맞는 요소 제외
Stream<T> limit() 스트림의 일부를 잘라낸다
Stream<T> skip() 스트림의 일부를 건너뛴다.
Stream<T> peek() 스트림의 요소에 작업을 수행한다.
Stream<T> sorted() 스트림의 요소를 정렬한다.

 

skip(), limit()

skip()과 limit()은 스트림의 일부를 잘라낼 때 사용한다. 

skip(5)는 처음 5개의 요소를 건너뛰고, limit(3)은 스트림의 요소를 3개로 제한한다.

 

코드 예제

Stream<String> stream1 = Stream.of("1", "2", "3", "4", "5", "6","7","8","9","10");
stream1.skip(3).limit(5).forEach(System.out::print); //45678

자른 후 결과

 

 

 

filter(), distinct()

distinct()는 중복된 요소를 제거하고, filter()는 주어진 조건에 맞지 않는 요소를 제거한다.

 

코드 예제

Stream<String> stream2 = Stream.of("1", "1", "1", "2", "3", "4");
stream2.distinct().filter(i -> i.equals("4")).forEach(System.out::print); //4

결과

 

 

distinct()로 중복인 1이 하나만 남고,

filter를 통해 4가 아닌 요소가 모두 제거되고 4만 남는다.

 

 

 

sorted()

sorted는 지정된 Comparator로 스트림을 정렬한다. Comparator를 지정하지 않으면 스트림 요소의 기본 정렬 기준으로 정렬한다. 

 

기본 정렬 코드 예제

// 기본 정렬: String의 기본 졍렬에 맞게 정렬된다.
Stream<String> stream = Stream.of("dd", "aa", "CC", "cc", "x");
stream.sorted().forEach(System.out::print);

 

 

람다식 코드 예제

// 람다식 사용 가능
Stream<String> stream2 = Stream.of("dd", "aa", "CC", "cc", "x");
stream2.sorted((s1, s2) -> s1.compareTo(s2)).forEach(System.out::print);

 

Comparator의 default 메서드 comparing 코드 예제

// Comparator의 default 메서드인 comparing 사용
Stream<String> stream3 = Stream.of("dd", "aa", "CC", "cc", "x");
stream3.sorted(Comparator.comparing((i) -> i.length())).forEach(System.out::print);

comparing 안에 원하는 기준을 람다식 등으로 넣을 수 있다. 위의 코드는 길이로 비교하겠다는 기준을 넣은 것이다.

 

 

 

 

peek()

forEach와 달리 peek은 스트림의 요소를 소모하지 않는다. 연산과 연산 사이에 올바르게 처리되었는지 확인하기 위해 사용한다.

 

코드 예제

//peek
int[] intArr = {1, 2, 3, 4, 5};

int sum = Arrays.stream(intArr)
        .peek(s -> System.out.print(s))//12345
        .sum();
System.out.println();
System.out.println(sum);//15

 

 

map()

스트림 원하는 필드만 뽑거나 특정 형태로 변환하기 

 

원하는 필드만 뽑는 코드 예제

Member[] members = {
        new Member("김"),
        new Member("최"),
        new Member("박"),
        new Member("이")
};

Stream<Member> memberStream = Stream.of(members);

memberStream.map(member -> member.getName())
            .forEach(System.out::println);

사실 원하는 필드만 뽑는 것도 map의 인자에 맞는 형태로 스트림을 변환하는 것이다.

결과

 

 

 

특정 형태로 변환하는 코드 예제

List<String> list = Arrays.asList("Apple", "Cherry", "Orange");
Stream<String> stream = list.stream();

stream.map(s->s.toUpperCase()).forEach(System.out::println);

대문자로 변경하기

결과값

 

 

 

 

 

스트림의 최종 연산

최종 연산은 스트림의 요소를 소모해 결과를 도출한다. 그래서 최종 연산 후 스트림은 재사용할 수 없다.

 

최종 연산 설명
void forEach()
void forEachOrdered()
각 요소에 지정된 작업 수행
long count() 스트림 요소 개수 반환
Optional max()

Optional min()
스트림 최대 최소값 반환
Optional findAny() // 아무거나
Optional findFirst() // 첫번째 요소
스트림 요소 중 하나 반환
boolean allMatch() // 모두 만족시키는지

boolean anyMatch() // 하나라도 만족시키는지

boolean noneMatch() // 모두 만족하지 않는지
조건을 만족하는지
Object[] toArray() 

A[] toArray() 
배열로 반환
reduce() 스트림 요소를 하나씩 줄여가며 계산
collect() 스트림의 요소를 수집

 

 

forEach()

스트림의 요소를 출력한다.

 

코드 예제

Stream<String> stringStream = Stream.of("abcdfe","sadasd","a");
    stringStream.forEach(System.out::println);

출력값

 

 

조건 검사 메서드

스트림의 요소에 지정된 요소가 조건에 맞는지 체크하는 메서드로 allMatch(), anyMatch(), noneMatch(),findFirst(), findAny()가 있다.

 

코드 예제

// 조건 검사 메서드
//allMatch()
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
boolean isAllMatch = integerStream.allMatch((i) -> i <= 9);
System.out.println("isAllMatch = " + isAllMatch);

//anyMatch()
Stream<Integer> integerStream2 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
boolean isAnyMatch = integerStream2.anyMatch((i) -> i <= 1);
System.out.println("isAnyMatch = " + isAnyMatch);

//noneMatch()
Stream<Integer> integerStream3 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
boolean isNoneMatch = integerStream3.noneMatch((i) -> i > 9);
System.out.println("isNoneMatch = " + isNoneMatch);

//findAny() - 병렬에 사용
Stream<Integer> integerStream4 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
Optional<Integer> any = integerStream4.filter((i) -> i > 4).findAny();
System.out.println("any = " + any);

//findFirst()
Stream<Integer> integerStream5 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
Optional<Integer> first = integerStream5.filter((i) -> i > 4).findFirst();
System.out.println("first = " + first);

출력 결과

 

reduce()

스트림의 요소를 줄여나가면서 연산을 수행하고, 최종 결과를 반환한다.

 

 reduce는 아래의 코드처럼 구현되어 있으니, 이해에 참고하자.

 

reduce 내부 코드

T reduce(T identity, BinaryOperator<T> accumulator){
	T a = identity;
    
    for(T b: stream)
    	a = accumulator.apply(a, b);
    
    return a;
}

 

reduce는 초기값과, 어떤 연산을 통해 스트림을 줄여나갈 지를 인자에 두고 사용할 수 있다.

 

코드 예제

Integer[] intArr = {11, 10, 5, 2};

Stream<Integer> stream = Stream.of(intArr);

Integer sum = stream.reduce(0, (a, b) -> a + b);
System.out.println("sum = " + sum); //sum = 28

Stream<Integer> stream2 = Stream.of(intArr);
Integer max = stream2.reduce((Integer.MIN_VALUE), (a, b) -> a > b ? a : b);
System.out.println("max = " + max);//11

 

초기값이 없는 경우도 가능하긴 하다. 다만, 이런 경우 null이 발생할 수 있으므로 Optional 객체를 반환한다.

 

코드 예제

Stream<Integer> stream3 = Stream.of(intArr);
Optional<Integer> max2 = stream3.reduce(Integer::max);
System.out.println("max2 = " + max2);

 

collect()는 양이 방대해 따로 정리하였다.

아래의 링크

컬렉션 정리